中间件托管平台模块

2025-10-15T10:30:00+08:00 | 20分钟阅读 | 更新于 2025-10-15T10:30:00+08:00

@

学习目标

学完本模块,你应该能够:

  1. 在平台申请并创建一个生产级 MySQL / Redis / Kafka / Elasticsearch 托管实例,理解其运行在租户 Namespace 下的隔离边界。
  2. 解释中间件实例的高可用形态(主从 / 集群 / 多副本)及故障切换原理。
  3. 配置自动备份策略,并演示一次基于 PITR(时间点恢复)的故障恢复。
  4. 区分透明加密、审计、网络隔离三种安全能力,说明它们如何满足合规。
  5. 结合联邦监控识别慢查询、热 Key、消费堆积等中间件典型异常,并触发分级告警。

前置知识

  • 了解至少一种中间件(MySQL 主从复制、Redis 主从 / 集群、Kafka 分区与副本、ES 分片)的基本工作原理。
  • 已阅读本白皮书「模块 02 多 K8s 集群纳管」「模块 03 应用全生命周期管理」,理解 API 网关、Namespace 隔离与联邦监控。
  • 了解 KMS 信封加密与备份存储(对象存储)基本概念。

本章你会动手做的事

  1. 在测试租户 Namespace 下申请一个 1 主 2 从的 MySQL 实例,观察 Pod 与 Service 落地。
  2. 配置每日全量 + 增量备份,并手动触发一次 PITR 恢复到指定时间点。
  3. 用 redis-benchmark 制造热 Key,验证联邦大盘出现热 Key 指标并触发 Alertmanager 告警。

项目结构与文件清单

本模块提供「平台侧 Go 控制面(调 operator/helm SDK)+ 目标集群 K8s IaC + 联邦观测配置」三件套:

paas-middleware/                           # 中间件托管平台源码(Go + gin + client-go)
├── go.mod
├── main.go                                # gin 入口 + /metrics(暴露 paas_middleware_* 指标)
├── pkg/
│   ├── gateway/
│   │   └── client.go                      # 统一 K8s API 网关多集群客户端
│   ├── middleware/
│   │   ├── instance.go                    # 经 dynamic client 创建 MiddlewareInstance(调 operator)
│   │   ├── backup.go                      # 触发备份 Job(PITR 基点)
│   │   └── status.go                      # 读取实例状态
│   ├── metrics/
│   │   └── metrics.go                     # prometheus 业务指标
│   └── handler/
│       └── middleware.go                  # gin 创建/备份/状态 Handler
├── manifests/                             # K8s IaC(kubectl apply 即用)
│   ├── mysql-instance.yaml               # 平台下发的中介件 CRD(MiddlewareInstance)
│   ├── mysql-statefulset.yaml            # MySQL StatefulSet + PVC + Service(group replication)
│   ├── redis-statefulset.yaml            # Redis Cluster StatefulSet + PVC + Service
│   ├── mysql-backup-cronjob.yaml         # 每日全量 + 增量备份 CronJob
│   ├── middleware-networkpolicy.yaml     # NetworkPolicy 严格隔离
│   └── rbac-prod.yaml                     # 生产实例 RBAC 约束
└── observability/                         # Prometheus / 联邦观测配置
    ├── mysql-servicemonitor.yaml         # MySQL/Redis ServiceMonitor
    ├── prometheus-mw-relabel.yaml        # 联邦抓取 + tenant/app/env/cluster 注入
    ├── mysql-recording-rules.yaml        # 慢查询/命中率/堆积 recording rules
    └── mysql-alert-rules.yaml            # 主库宕机/慢查询/热Key/堆积/备份失败告警

下面各章逐文件完整展示,且所有文件相互自洽:Go 创建的 MiddlewareInstance 字段 = manifests/mysql-instance.yaml,Go 暴露的 paas_middleware_* / middleware_backup_* 指标 = 观测配置中的 recording/alert 输入。


一、模块概述与企业商用价值

中间件托管平台模块(Managed Middleware Platform)把企业最常用的有状态组件——MySQL、Redis、Kafka、Elasticsearch、对象存储等——从"各团队自己裸装"升级为"平台统一托管、按需申请、自动护航"。它解决的核心矛盾是:有状态组件最怕数据丢、最怕被邻居挤爆、最怕运维口径不一;而业务团队往往既不懂调优,也不愿承担 7×24 值守。

类比:这套模块像写字楼的"中央机房 + 专业电工班"。每个租户(公司)在自家楼层(Namespace)里租用一个带锁的机柜(实例),物业(平台)负责通电(高可用)、定期体检(备份)、配独立电表(指标标签)、上锁防窥(加密与隔离)。租户只管用,不用自己当运维。

适用角色与业务痛点

角色痛点本模块价值
业务研发自建中间件运维成本高、易出错按需申请,平台托管运维
DBA / 中间件运维实例散落、备份靠人肉统一生命周期 + 自动备份
平台管理员资源抢占、合规难证明资源隔离 + 加密 + 审计留痕

合规与不可替代性

金融 / 政企要求:数据有备份且可恢复(容灾合规)、敏感数据静态加密(数据合规)、实例间网络隔离(等保合规)、操作可审计(监管留存)。本模块把以上固化为能力,而非依赖个人自觉。

下面这张图给出本模块在整体 PaaS 中的定位。

graph TB
  subgraph 使用方
    Biz[业务应用/研发]
  end
  subgraph PaaS平台
    MW[中间件托管平台]
    Ops[生命周期控制器]
    BK[(备份与PITR)]
    ENC[透明加密 KMS]
    SEC[网络隔离/审计]
  end
  subgraph 基础设施
    GW[统一 K8s API 网关]
    NS[(租户 Namespace
MySQL/Redis/Kafka/ES Pod)] end Biz --> MW MW --> Ops MW --> BK MW --> ENC MW --> SEC Ops --> GW GW --> NS

这张图说明:业务只面向托管平台申请实例,平台经控制器与 API 网关把有状态 Pod 落到租户 Namespace,并叠加备份、加密、隔离三种护航能力。


二、细分功能详解(商用生产级)

下面区分【基础能力】与【高级企业增值能力】,带 ★ 为禁止删减底线能力。

功能分级总览

级别功能项商用说明
【基础能力】① 托管类型MySQL / Redis / Kafka / ES / 对象存储
【基础能力】② 实例生命周期申请 / 创建 / 扩容 / 缩容 / 销毁
【基础能力】③ 高可用主从 / 集群 / 多副本 + 故障切换
【基础能力】⑤ 参数模板与版本管理统一参数基线 + 大版本升级
【基础能力】⑥ 容量规划与配额规格档位 + 租户配额
【高级企业增值能力】④ 自动备份与 PITR全量 + 增量 + 时间点恢复
【高级企业增值能力】⑦ 监控告警慢查询 / 热 Key / 堆积 等专属指标
【高级企业增值能力】⑧ 安全(加密/审计/隔离)透明加密 + 操作审计 + 网络隔离
★禁止删减★ 备份自动备份 + PITR,数据可恢复底线
★禁止删减★ 加密静态数据 KMS 信封加密
★禁止删减★ 资源隔离租户 Namespace + 配额 + 网络策略

① 托管类型(基础能力)

平台内置 Operator 支持:MySQL(主从 / Group Replication)、Redis(主从 / Cluster)、Kafka(多 broker + 副本)、Elasticsearch(多节点分片)、对象存储兼容(MinIO / 自建)。统一抽象为 MiddlewareInstance CRD。平台侧 Go(pkg/middleware/instance.go)经 dynamic client 把该 CRD 下发到集群,由集群内 Operator 调谐出 StatefulSet + Service + PVC。

② 实例生命周期(基础能力)

从申请到销毁五态:申请 → 创建(调谐出 StatefulSet + Service + PVC)→ 扩容 / 缩容(调整规格或副本)→ 维护(参数变更)→ 销毁(安全擦除 + PVC 回收 + 配额释放)。

③ 高可用(基础能力)

  • MySQL:1 主 2 从,主库宕机由控制器选新主,VIP / Service 不变。
  • Redis:Cluster 模式跨节点分片,副本自动顶替。
  • Kafka:多分区多副本(replication.factor≥3),Leader 重选举。
  • ES:多主多数据节点,分片自动再平衡。

④ 自动备份与 PITR(★禁止删减,高级能力)

每日全量 + 持续增量(binlog / WAL / segment)备份至对象存储;恢复时先恢复全量再重放增量到指定时间戳(Point-In-Time Recovery)。这是数据零丢失的底线能力,不可关闭

⚠️ 备份存储与目标实例必须跨故障域(不同集群或对象存储桶),否则"集群没了备份也没了"等于没备份。

平台下发的 MiddlewareInstancemanifests/mysql-instance.yaml)即把备份策略固化为期望状态:

# file: manifests/mysql-instance.yaml
apiVersion: paas.example.com/v1
kind: MiddlewareInstance
metadata:
  name: mysql-order
  namespace: tenant-a-prod          # 租户生产 Namespace,强隔离
  labels:
    app: mysql-order
    tenant: tenant-a
    env: prod
    cluster: cluster-prod
spec:
  type: mysql
  version: "8.0"
  topology:
    mode: group-replication
    replicas: 3                      # 1主2从
  storage:
    class: ssd-encrypted             # ★ 加密存储类
    size: 200Gi
  backup:
    full: "0 2 * * *"                # 每日全量
    incremental: binlog              # 增量
    pitr: true                       # ★ 时间点恢复
    retention: 30d

⑤ 参数模板与版本管理(基础能力)

内置合规参数基线(如 MySQL innodb_flush_log_at_trx_commit=1、Redis 禁用危险命令),大版本升级走灰度校验。

⑥ 容量规划与配额(基础能力)

实例规格分档(小 / 中 / 大),每租户设存储与连接数配额,防止单实例吃满节点。生产配额约束见第五章 manifests/rbac-prod.yaml 配套 ResourceQuota。

⑦ 监控告警(高级能力)

专属指标:MySQL 慢查询数、连接数;Redis 热 Key / 大 Key / 命中率;Kafka 消费堆积(Lag);ES 分片未分配。经联邦进 Grafana 租户大盘,异常触发 Alertmanager。

⑧ 安全(★禁止删减:加密 / 隔离,高级能力)

  • 透明加密:PVC 或上层存储经 KMS 信封加密,静态数据不可读(storageClassName: ssd-encrypted)。
  • 审计:实例的创建、扩缩、参数变更、备份恢复均留痕 ≥ 90 天。
  • 网络隔离:NetworkPolicy 限制实例仅能被授权应用访问(见 manifests/middleware-networkpolicy.yaml)。

下面这张图给出托管平台功能组件关系。

graph LR
  A[托管类型] --> B[实例生命周期]
  B --> C[高可用主从/集群]
  C --> D[自动备份与PITR]
  C --> E[参数模板与版本]
  B --> F[容量规划与配额]
  C --> G[监控告警]
  G --> H[透明加密]
  G --> I[审计留痕]
  G --> J[网络隔离]
  D -.★禁止删减.-> D
  H -.★禁止删减.-> H
  J -.★禁止删减.-> J

这张图按"类型 → 生命周期 → 高可用 → 备份/参数/容量/监控/安全"呈现模块功能树,并标出三项禁止删减能力。


三、底层架构联动设计

本模块的有状态实例运行在租户 Namespace,通过统一 API 网关纳管的多集群承载,并把运行指标汇入联邦监控。

1. 与多 K8s 集群的交互链路

中间件控制器(平台侧)监听 MiddlewareInstance 对象,把期望状态(副本数、规格、存储类)经统一 K8s API 网关下发给目标集群,由集群内 Operator 调谐出 StatefulSet / PVC / Service。租户永远只能落在被授权的 Namespace,跨集群数据同步(如容灾副本)也由网关统一编排。

平台侧多集群客户端 pkg/gateway/client.go 与模块 03 同构(统一经 API 网关访问各集群):

// file: pkg/gateway/client.go
package gateway

import (
	"context"
	"fmt"

	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
)

// ClusterGateway 统一 K8s API 网关客户端管理器。
type ClusterGateway struct {
	clients     map[string]kubernetes.Interface
	gatewayREST *rest.Config
}

// NewClusterGateway 从网关 kubeconfig 初始化。
func NewClusterGateway(kubeconfigPath string) (*ClusterGateway, error) {
	cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath)
	if err != nil {
		return nil, fmt.Errorf("build gateway config: %w", err)
	}
	cfg.Impersonate = rest.ImpersonationConfig{
		UserName: "paas-platform",
		Groups:   []string{"paas:platform"},
	}
	clientset, err := kubernetes.NewForConfig(cfg)
	if err != nil {
		return nil, fmt.Errorf("new gateway clientset: %w", err)
	}
	return &ClusterGateway{
		clients:     map[string]kubernetes.Interface{"gateway": clientset},
		gatewayREST: cfg,
	}, nil
}

// RESTConfig 暴露底层 *rest.Config,供 dynamic client 复用。
func (g *ClusterGateway) RESTConfig() *rest.Config { return g.gatewayREST }

// ClientFor 返回指定集群(经网关纳管)的客户端。
func (g *ClusterGateway) ClientFor(ctx context.Context, clusterID string) (kubernetes.Interface, error) {
	if c, ok := g.clients[clusterID]; ok {
		return c, nil
	}
	cs, err := kubernetes.NewForConfig(g.gatewayREST)
	if err != nil {
		return nil, fmt.Errorf("client for cluster %s: %w", clusterID, err)
	}
	g.clients[clusterID] = cs
	return cs, nil
}

平台侧经 dynamic client 创建 MiddlewareInstance(调 operator 托管中间件) 的核心实现 pkg/middleware/instance.go

// file: pkg/middleware/instance.go
package middleware

import (
	"context"
	"fmt"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/dynamic"
)

var middlewareGVR = schema.GroupVersionResource{
	Group: "paas.example.com", Version: "v1", Resource: "middlewareinstances",
}

// MiddlewareClient 通过动态客户端操作中间件 CRD(由集群内 Operator 调谐)。
type MiddlewareClient struct {
	dyn dynamic.Interface
}

func NewMiddlewareClient(dyn dynamic.Interface) *MiddlewareClient {
	return &MiddlewareClient{dyn: dyn}
}

// CreateInstance 创建 MiddlewareInstance(client-go 调 operator 托管中间件的核心示例)。
func (m *MiddlewareClient) CreateInstance(ctx context.Context, ns string, inst *unstructured.Unstructured) error {
	_, err := m.dyn.Resource(middlewareGVR).Namespace(ns).Create(ctx, inst, metav1.CreateOptions{})
	if err != nil {
		return fmt.Errorf("create middlewareinstance: %w", err)
	}
	return nil
}

// BuildMySQLInstance 构造与 manifests/mysql-instance.yaml 完全一致的 MiddlewareInstance。
func BuildMySQLInstance(name, ns string, replicas int, size string) *unstructured.Unstructured {
	return &unstructured.Unstructured{Object: map[string]interface{}{
		"apiVersion": "paas.example.com/v1",
		"kind":       "MiddlewareInstance",
		"metadata": map[string]interface{}{
			"name":      name,
			"namespace": ns,
			"labels": map[string]interface{}{
				"app":     name,
				"tenant":  "tenant-a",
				"env":     "prod",
				"cluster": "cluster-prod",
			},
		},
		"spec": map[string]interface{}{
			"type":    "mysql",
			"version": "8.0",
			"topology": map[string]interface{}{
				"mode":     "group-replication",
				"replicas": int64(replicas),
			},
			"storage": map[string]interface{}{
				"class": "ssd-encrypted",
				"size":  size,
			},
			"backup": map[string]interface{}{
				"full":        "0 2 * * *",
				"incremental": "binlog",
				"pitr":        true,
				"retention":   "30d",
			},
		},
	}}
}

2. 与联邦 Prometheus 监控的联动(必绑定)

实例产生的指标——QPS、慢查询、连接数、热 Key、消费 Lag、分片状态——由集群内 Prometheus Agent 采集,并强制注入 tenant/app/env/cluster 标签(其中 app 取中间件类型与实例名),经联邦汇总进中心 Prometheus + Thanos。Grafana 按租户过滤出实例专属大盘;异常(如 Lag 暴涨)触发 Alertmanager 按严重度分级路由。

实例侧 ServiceMonitorobservability/mysql-servicemonitor.yaml)与中心抓取 + relabel(observability/prometheus-mw-relabel.yaml)如下:

# file: observability/mysql-servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: mysql-order
  namespace: monitoring
  labels:
    tenant: tenant-a
    env: prod
    release: paas
spec:
  selector:
    matchLabels:
      app: mysql-order
  namespaceSelector:
    matchNames:
      - tenant-a-prod
  endpoints:
    - port: mysql
      path: /metrics
      interval: 15s
      metricRelabelings:
        - sourceLabels: [__name__]
          regex: "mysql_up|mysql_global_status_slow_queries|mysql_global_status_threads_connected|mysql_global_status_max_connections"
          action: keep
# file: observability/prometheus-mw-relabel.yaml
# 中心 Prometheus 抓取配置片段:把 Namespace/Pod 标签注入为 tenant/app/env/cluster 维度
scrape_configs:
  - job_name: paas-middleware
    kubernetes_sd_configs:
      - role: endpoints
        namespaces:
          names:
            - tenant-a-prod
            - tenant-a-test
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_label_tenant]
        target_label: tenant
      - source_labels: [__meta_kubernetes_pod_label_app]
        target_label: app
      - source_labels: [__meta_kubernetes_pod_label_env]
        target_label: env
      - source_labels: [__meta_kubernetes_pod_label_cluster]
        target_label: cluster
      - source_labels: [__meta_kubernetes_namespace]
        target_label: namespace
      - source_labels: [__address__]
        target_label: instance
  # 平台控制面暴露的 paas_middleware_* 业务指标(已自带 tenant/app/env/cluster 标签)
  - job_name: paas-middleware-platform
    static_configs:
      - targets: ["paas-middleware.platform.svc:8081"]
    relabel_configs:
      - source_labels: [__address__]
        target_label: instance

下面这张图是中间件与联邦监控的联动拓扑。

graph TB
  subgraph 生产集群[生产集群 租户NS]
    OP[中间件 Operator]
    MY[MySQL Pod]
    RD[Redis Pod]
    KF[Kafka Pod]
    PA[Prometheus Agent]
  end
  MY -->|指标 tenant/app/env/cluster| PA
  RD -->|指标 tenant/app/env/cluster| PA
  KF -->|指标 tenant/app/env/cluster| PA
  OP --> MY
  OP --> RD
  OP --> KF
  PA -->|联邦拉取| CENTER[中心 Prometheus]
  CENTER --> THANOS[Thanos 长期存储]
  CENTER --> GRAF[Grafana 租户大盘]
  CENTER --> AM[Alertmanager 分级告警]
  AM -->|堆积/慢查询| MW[中间件模块自动处置]

这张图说明:有状态实例指标带四维标签进联邦,Grafana/Alertmanager 按租户过滤与分级,异常可反哺中间件模块做自动处置(如扩副本、告警收敛)。

3. 与密钥管理、审批流、审计的联动

  • 密钥管理:实例账号密码、TLS 证书经 KMS 信封加密托管,注入时不落明文。
  • 审批流:生产实例的创建、规格变更、备份恢复、销毁均须走审批引擎多级审批。
  • 审计:上述操作全留痕 ≥ 90 天,与模块 01 RBAC、模块 09 全局审计打通。
  • 成本计量:实例规格与存储用量驱动模块 08 成本分摊。

四、端到端标准操作流程

以"DBA 申请一个生产 MySQL 主从实例并配置备份"为主线,分角色给出可培训步骤。

角色与职责

  • 业务/DBA:提交实例申请、定义规格与备份策略。
  • 集群运维:复核目标集群资源、确认高可用拓扑。
  • 平台管理员:审批生产实例创建与备份恢复。

操作流程时序

sequenceDiagram
  participant DBA as DBA/业务
  participant MW as 中间件模块
  participant Appr as 审批引擎
  participant Admin as 平台管理员
  participant GW as K8s API网关
  participant Op as 集群Operator
  participant BK as 备份服务
  participant Mon as 联邦监控
  DBA->>MW: 提交MySQL主从实例申请
  MW->>Appr: 发起生产创建审批
  Appr->>Admin: 通知待审批
  Admin->>Appr: 多级审批通过
  Appr->>MW: 放行
  MW->>GW: 下发 MiddlewareInstance
  GW->>Op: 调谐 StatefulSet/PVC
  Op->>GW: 实例就绪
  GW->>MW: 状态回传
  MW->>BK: 注册自动备份策略
  Op->>Mon: 指标带标签进联邦
  Mon->>DBA: 大盘可见实例指标

这段时序展示从申请到实例就绪、备份注册、指标上联邦的完整链路,以及审批闸门的串联。

平台侧 Go 实现(gin + client-go 调用 operator)

pkg/middleware/backup.go 用 client-go 触发备份 Job(PITR 基点)

// file: pkg/middleware/backup.go
package middleware

import (
	"context"
	"fmt"

	batchv1 "k8s.io/api/batch/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

// BackupClient 触发一次性备份 Job。
type BackupClient struct {
	cs kubernetes.Interface
}

func NewBackupClient(cs kubernetes.Interface) *BackupClient {
	return &BackupClient{cs: cs}
}

// TriggerBackup 立即触发一次备份 Job(等效 cron 之外的手动 PITR 基点)。
func (b *BackupClient) TriggerBackup(ctx context.Context, ns, instance, img string) (string, error) {
	jobName := fmt.Sprintf("%s-backup-%d", instance, metav1.Now().Unix())
	job := &batchv1.Job{
		ObjectMeta: metav1.ObjectMeta{
			Name:      jobName,
			Namespace: ns,
			Labels: map[string]string{
				"app": instance, "paas.example.com/type": "backup",
			},
		},
		Spec: batchv1.JobSpec{
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"app": instance, "paas.example.com/type": "backup",
					},
				},
				Spec: corev1.PodSpec{
					RestartPolicy: corev1.RestartPolicyOnFailure,
					Containers: []corev1.Container{{
						Name:  "backup",
						Image: img,
						Args:  []string{"backup", "--instance", instance, "--pitr"},
					}},
				},
			},
		},
	}
	if _, err := b.cs.BatchV1().Jobs(ns).Create(ctx, job, metav1.CreateOptions{}); err != nil {
		return "", fmt.Errorf("trigger backup: %w", err)
	}
	return jobName, nil
}

pkg/middleware/status.go 用 dynamic client 读取实例状态(phase / 就绪副本):

// file: pkg/middleware/status.go
package middleware

import (
	"context"
	"fmt"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/client-go/dynamic"
)

// Status 读取 MiddlewareInstance 的 phase 与就绪副本数(Go 调用 K8s 读取状态)。
func (m *MiddlewareClient) Status(ctx context.Context, ns, name string) (phase string, replicas int64, err error) {
	obj, err := m.dyn.Resource(middlewareGVR).Namespace(ns).Get(ctx, name, metav1.GetOptions{})
	if err != nil {
		return "", 0, fmt.Errorf("get instance: %w", err)
	}
	status, found, _ := unstructured.NestedMap(obj.Object, "status")
	if !found {
		return "Unknown", 0, nil
	}
	phase, _, _ = unstructured.NestedString(status, "phase")
	replicas, _, _ = unstructured.NestedInt64(status, "readyReplicas")
	return phase, replicas, nil
}

pkg/metrics/metrics.go 定义平台暴露的 paas_middleware_* / middleware_backup_* 指标:

// file: pkg/metrics/metrics.go
package metrics

import "github.com/prometheus/client_golang/prometheus"

var (
	InstanceProvisionTotal = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "paas_middleware_instance_total",
			Help: "Total middleware instances provisioned",
		},
		[]string{"tenant", "app", "env", "cluster", "type", "result"},
	)
	InstanceReady = prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "paas_middleware_instance_ready",
			Help: "1 if instance ready",
		},
		[]string{"tenant", "app", "env", "cluster", "type"},
	)
	BackupLastSuccess = prometheus.NewGaugeVec(
		prometheus.GaugeOpts{
			Name: "middleware_backup_last_success_timestamp",
			Help: "Timestamp of last successful backup",
		},
		[]string{"tenant", "app", "env", "cluster"},
	)
	BackupDuration = prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Name:    "middleware_backup_duration_seconds",
			Help:    "Backup duration seconds",
			Buckets: prometheus.DefBuckets,
		},
		[]string{"tenant", "app", "env", "cluster"},
	)
)

func init() {
	prometheus.MustRegister(InstanceProvisionTotal, InstanceReady, BackupLastSuccess, BackupDuration)
}

pkg/handler/middleware.go 把以上收敛为 gin Handler:

// file: pkg/handler/middleware.go
package handler

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"k8s.io/client-go/dynamic"

	"github.com/example/paas-middleware/pkg/gateway"
	"github.com/example/paas-middleware/pkg/metrics"
	"github.com/example/paas-middleware/pkg/middleware"
)

// CreateInstanceRequest 创建实例请求体。
type CreateInstanceRequest struct {
	Tenant   string `json:"tenant" binding:"required"`
	App      string `json:"app" binding:"required"`
	Env      string `json:"env" binding:"required"`
	Cluster  string `json:"cluster" binding:"required"`
	Type     string `json:"type" binding:"required"` // mysql | redis | kafka
	Replicas int    `json:"replicas"`
	Size     string `json:"size"`
}

// CreateInstanceHandler 经 API 网关创建中间件实例(调 operator)。
func CreateInstanceHandler(gw *gateway.ClusterGateway, dyn dynamic.Interface) gin.HandlerFunc {
	return func(c *gin.Context) {
		var req CreateInstanceRequest
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
		ctx := c.Request.Context()
		if _, err := gw.ClientFor(ctx, req.Cluster); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		ns := req.Tenant + "-" + req.Env
		mw := middleware.NewMiddlewareClient(dyn)
		inst := middleware.BuildMySQLInstance(req.App, ns, req.Replicas, req.Size)
		if err := mw.CreateInstance(ctx, ns, inst); err != nil {
			metrics.InstanceProvisionTotal.WithLabelValues(req.Tenant, req.App, req.Env, req.Cluster, req.Type, "failed").Inc()
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		metrics.InstanceProvisionTotal.WithLabelValues(req.Tenant, req.App, req.Env, req.Cluster, req.Type, "success").Inc()
		c.JSON(http.StatusOK, gin.H{"status": "provisioning", "namespace": ns})
	}
}

// BackupHandler 触发备份(Go 调用 K8s 创建 Job)。
func BackupHandler(gw *gateway.ClusterGateway) gin.HandlerFunc {
	return func(c *gin.Context) {
		var req struct {
			Tenant  string `json:"tenant" binding:"required"`
			App     string `json:"app" binding:"required"`
			Env     string `json:"env" binding:"required"`
			Cluster string `json:"cluster" binding:"required"`
			Image   string `json:"image"`
		}
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
		ctx := c.Request.Context()
		cs, err := gw.ClientFor(ctx, req.Cluster)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		ns := req.Tenant + "-" + req.Env
		img := req.Image
		if img == "" {
			img = "harbor.example.com/paas-mysql-backup:latest"
		}
		bc := middleware.NewBackupClient(cs)
		job, err := bc.TriggerBackup(ctx, ns, req.App, img)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		metrics.BackupLastSuccess.WithLabelValues(req.Tenant, req.App, req.Env, req.Cluster).SetToCurrentTime()
		c.JSON(http.StatusOK, gin.H{"job": job, "status": "triggered"})
	}
}

// StatusHandler 读取实例状态(Go 调用 K8s 读取状态)。
func StatusHandler(gw *gateway.ClusterGateway, dyn dynamic.Interface) gin.HandlerFunc {
	return func(c *gin.Context) {
		tenant := c.Param("tenant")
		env := c.Param("env")
		app := c.Param("app")
		cluster := c.Query("cluster")
		if cluster == "" {
			cluster = "cluster-prod"
		}
		ctx := c.Request.Context()
		if _, err := gw.ClientFor(ctx, cluster); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		mw := middleware.NewMiddlewareClient(dyn)
		phase, ready, err := mw.Status(ctx, tenant+"-"+env, app)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		readyVal := 0.0
		if phase == "Ready" {
			readyVal = 1
		}
		metrics.InstanceReady.WithLabelValues(tenant, app, env, cluster, "mysql").Set(readyVal)
		c.JSON(http.StatusOK, gin.H{"phase": phase, "readyReplicas": ready})
	}
}

var _ = time.Now

go.modmain.go

// file: go.mod
module github.com/example/paas-middleware

go 1.22

require (
	github.com/gin-gonic/gin v1.10.0
	github.com/prometheus/client_golang v1.19.0
	k8s.io/apimachinery v0.30.0
	k8s.io/client-go v0.30.0
)
// file: main.go
package main

import (
	"log"

	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"k8s.io/client-go/dynamic"

	"github.com/example/paas-middleware/pkg/gateway"
	"github.com/example/paas-middleware/pkg/handler"
)

func main() {
	gw, err := gateway.NewClusterGateway("/etc/paas/gateway.kubeconfig")
	if err != nil {
		log.Fatalf("init gateway: %v", err)
	}
	dyn, err := dynamic.NewForConfig(gw.RESTConfig())
	if err != nil {
		log.Fatalf("init dynamic: %v", err)
	}

	r := gin.Default()
	r.POST("/api/v1/middleware", handler.CreateInstanceHandler(gw, dyn))
	r.POST("/api/v1/middleware/backup", handler.BackupHandler(gw))
	r.GET("/api/v1/middleware/:tenant/:env/:app", handler.StatusHandler(gw, dyn))
	r.GET("/metrics", gin.WrapH(promhttp.Handler()))
	if err := r.Run(":8081"); err != nil {
		log.Fatalf("server: %v", err)
	}
}

分步操作(K8s IaC 完整可 apply)

MySQL StatefulSet + PVC + Service(group replication,1 主 2 从,manifests/mysql-statefulset.yaml

# file: manifests/mysql-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql-order
  namespace: tenant-a-prod
  labels:
    app: mysql-order
    tenant: tenant-a
    env: prod
    cluster: cluster-prod
spec:
  serviceName: mysql-order-headless
  replicas: 3
  selector:
    matchLabels:
      app: mysql-order
  template:
    metadata:
      labels:
        app: mysql-order
        tenant: tenant-a
        env: prod
        cluster: cluster-prod
    spec:
      containers:
        - name: mysql
          image: harbor.example.com/mysql:8.0
          ports:
            - containerPort: 3306
              name: mysql
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-order-secret
                  key: ROOT_PASSWORD
          volumeMounts:
            - name: data
              mountPath: /var/lib/mysql
          resources:
            requests:
              cpu: "1"
              memory: 2Gi
            limits:
              cpu: "2"
              memory: 4Gi
  volumeClaimTemplates:
    - metadata:
        name: data
        labels:
          app: mysql-order
          tenant: tenant-a
          env: prod
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: ssd-encrypted   # ★ KMS 信封加密存储类
        resources:
          requests:
            storage: 200Gi
---
apiVersion: v1
kind: Service
metadata:
  name: mysql-order
  namespace: tenant-a-prod
  labels:
    app: mysql-order
    tenant: tenant-a
    env: prod
spec:
  selector:
    app: mysql-order
  ports:
    - name: mysql
      port: 3306
      targetPort: 3306
---
apiVersion: v1
kind: Service
metadata:
  name: mysql-order-headless
  namespace: tenant-a-prod
  labels:
    app: mysql-order
    tenant: tenant-a
    env: prod
spec:
  clusterIP: None
  selector:
    app: mysql-order
  ports:
    - name: mysql
      port: 3306
      targetPort: 3306

Redis Cluster StatefulSet + PVC + Service(manifests/redis-statefulset.yaml

# file: manifests/redis-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-cache
  namespace: tenant-a-prod
  labels:
    app: redis-cache
    tenant: tenant-a
    env: prod
    cluster: cluster-prod
spec:
  serviceName: redis-cache-headless
  replicas: 6
  selector:
    matchLabels:
      app: redis-cache
  template:
    metadata:
      labels:
        app: redis-cache
        tenant: tenant-a
        env: prod
        cluster: cluster-prod
    spec:
      containers:
        - name: redis
          image: harbor.example.com/redis:7.2
          ports:
            - containerPort: 6379
              name: redis
          volumeMounts:
            - name: data
              mountPath: /data
          resources:
            requests:
              cpu: "500m"
              memory: 1Gi
            limits:
              cpu: "1"
              memory: 2Gi
  volumeClaimTemplates:
    - metadata:
        name: data
        labels:
          app: redis-cache
          tenant: tenant-a
          env: prod
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: ssd-encrypted
        resources:
          requests:
            storage: 20Gi
---
apiVersion: v1
kind: Service
metadata:
  name: redis-cache
  namespace: tenant-a-prod
  labels:
    app: redis-cache
    tenant: tenant-a
    env: prod
spec:
  selector:
    app: redis-cache
  ports:
    - name: redis
      port: 6379
      targetPort: 6379
---
apiVersion: v1
kind: Service
metadata:
  name: redis-cache-headless
  namespace: tenant-a-prod
  labels:
    app: redis-cache
    tenant: tenant-a
    env: prod
spec:
  clusterIP: None
  selector:
    app: redis-cache
  ports:
    - name: redis
      port: 6379
      targetPort: 6379

每日全量 + 增量备份 CronJob(manifests/mysql-backup-cronjob.yaml

# file: manifests/mysql-backup-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: mysql-order-backup
  namespace: tenant-a-prod
  labels:
    app: mysql-order
    tenant: tenant-a
    env: prod
    cluster: cluster-prod
spec:
  schedule: "0 2 * * *"          # 每日全量
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      template:
        metadata:
          labels:
            app: mysql-order
            paas.example.com/type: backup
        spec:
          restartPolicy: OnFailure
          containers:
            - name: backup
              image: harbor.example.com/paas-mysql-backup:latest
              args: ["backup", "--instance", "mysql-order", "--pitr", "--upload", "s3://paas-backup/tenant-a/"]
              env:
                - name: S3_ENDPOINT
                  value: "https://obs.example.com"
                - name: BACKUP_PASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: mysql-order-secret
                      key: BACKUP_PASSWORD

NetworkPolicy 严格隔离(manifests/middleware-networkpolicy.yaml

# file: manifests/middleware-networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: mysql-order-netpol
  namespace: tenant-a-prod
  labels:
    app: mysql-order
    tenant: tenant-a
    env: prod
spec:
  podSelector:
    matchLabels:
      app: mysql-order
  policyTypes:
    - Ingress
    - Egress
  ingress:
    # 仅允许同租户 order-service 访问 3306
    - from:
        - podSelector:
            matchLabels:
              app: order-service
          namespaceSelector:
            matchLabels:
              tenant: tenant-a
      ports:
        - protocol: TCP
          port: 3306
  egress:
    # 允许访问备份对象存储与 DNS
    - to:
        - ipBlock:
            cidr: 10.20.0.0/16   # 备份存储网段
      ports:
        - protocol: TCP
          port: 443
    - to:
        - namespaceSelector: {}
      ports:
        - protocol: UDP
          port: 53

运维 runbook(端到端真实命令)

# 1) 经平台 API 申请生产 MySQL 主从实例(Go CreateInstanceHandler -> 下发 MiddlewareInstance CRD)
curl -XPOST http://paas-gateway/api/v1/middleware -H 'Content-Type: application/json' -d '{
  "tenant":"tenant-a","app":"mysql-order","env":"prod","cluster":"cluster-prod",
  "type":"mysql","replicas":3,"size":"200Gi"}'

# 2) 直接使用 IaC 落地(与平台下发等价)
kubectl apply -f manifests/mysql-instance.yaml
kubectl apply -f manifests/mysql-statefulset.yaml
kubectl apply -f manifests/mysql-backup-cronjob.yaml
kubectl apply -f manifests/middleware-networkpolicy.yaml

# 3) 查看实例状态(Pod/STS/PVC)
kubectl -n tenant-a-prod get middlewareinstance mysql-order
kubectl -n tenant-a-prod get sts,pods,pvc -l app=mysql-order

# 4) 手动触发一次 PITR 备份基点
kubectl -n tenant-a-prod create job mysql-order-backup-manual --from=cronjob/mysql-order-backup

# 5) 扩缩容(增加只读副本)
kubectl -n tenant-a-prod scale sts/mysql-order --replicas=5

# 6) 镜像安全(中间件基础镜像同样扫描)
trivy image --severity CRITICAL,HIGH --exit-code 1 harbor.example.com/mysql:8.0

【测试环境】与【生产环境】差异化步骤

环节测试环境生产环境
创建审批免审 / 单级★ 多级审批
加密存储可选★ 强制 KMS 信封加密
备份策略可关闭或低频★ 自动备份 + PITR 不可关
高可用拓扑单副本即可多副本 + 跨故障域
网络隔离宽松NetworkPolicy 严格限制
监控告警仅大盘大盘 + Alertmanager 分级
销毁直接删审批 + 安全擦除 + 审计

⚠️ 即便测试环境,也建议开启加密与备份,保证压测数据、恢复演练与生产一致,避免"测试不备份、生产不敢恢复"。


五、生产环境管控与安全约束

生产环境对中间件实例的管控显著严于测试。下面是「测试 vs 生产 差异化管控」对照表。

管控维度测试环境生产环境
实例创建免审 / 单级★ 多级审批
静态加密可选★ KMS 信封加密,强制
资源隔离逻辑 Namespace★ Namespace + 配额 + NetworkPolicy
备份可关闭★ 自动备份 + PITR,不可关
高可用单副本多副本跨故障域
参数基线默认合规基线 + 变更审批
销毁直接删审批 + 安全擦除 + 审计归档
审计留存90 天90 天以上,不可篡改
监控仅大盘大盘 + 分级告警 + 自动处置

关键约束说明

  • 资源隔离:生产实例独占节点池或强绑配额,防止与 noisy neighbor 争抢 IO;NetworkPolicy 仅放行授权应用网段(见 manifests/middleware-networkpolicy.yaml)。以下 RBAC 把"生产实例变更"收敛给运维角色:
# file: manifests/rbac-prod.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: tenant-a-prod-mw-ops
  namespace: tenant-a-prod
rules:
  - apiGroups: ["paas.example.com"]
    resources: ["middlewareinstances"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]   # 仅运维可写
  - apiGroups: ["apps"]
    resources: ["statefulsets"]
    verbs: ["get", "list", "watch", "scale"]
  - apiGroups: ["batch"]
    resources: ["cronjobs", "jobs"]
    verbs: ["get", "list", "watch", "create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: tenant-a-prod-mw-ops
  namespace: tenant-a-prod
subjects:
  - kind: User
    name: mw-ops@tenant-a
roleRef:
  kind: Role
  name: tenant-a-prod-mw-ops
  apiGroup: rbac.authorization.k8s.io
  • 敏感操作审批:创建、扩缩、参数变更、恢复、销毁均须审批,记录不可绕过。
  • 审计规则:操作含 操作人 / 租户 / 实例 / 时间 / 前后值,留存 ≥ 90 天。
  • 故障熔断:实例不可用触发自动选主 / 副本顶替;备份连续失败升级 P1 告警(见第六章 BackupFailed)。

六、常见生产故障与解决方案

结合多集群与联邦监控场景列举高频问题。下方 observability/mysql-recording-rules.yamlobservability/mysql-alert-rules.yaml 把"可观测"固化为规则。

recording rules(慢查询 / 命中率 / 堆积)

# file: observability/mysql-recording-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: mysql-recording-rules
  namespace: monitoring
  labels:
    role: recording-rules
spec:
  groups:
    - name: middleware_slo
      interval: 30s
      rules:
        - record: redis_hit_rate
          expr: |
            sum(rate(redis_keyspace_hits_total[5m])) by (tenant,app,env,cluster)
            /
            (sum(rate(redis_keyspace_hits_total[5m])) by (tenant,app,env,cluster)
             + sum(rate(redis_keyspace_misses_total[5m])) by (tenant,app,env,cluster))            
        - record: mysql_slow_query_rate
          expr: rate(mysql_global_status_slow_queries[5m])
        - record: mysql_connection_ratio
          expr: mysql_global_status_threads_connected / mysql_global_status_max_connections
        - record: kafka_consumer_lag_sum
          expr: sum(kafka_consumer_group_lag) by (tenant,app,env,cluster)
        # 平台控制面指标(与 pkg/metrics/metrics.go 上报一致)
        - record: middleware_instance_available
          expr: paas_middleware_instance_ready

告警规则(主库宕机 / 慢查询 / 热Key / 堆积 / 备份失败)

# file: observability/mysql-alert-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: mysql-alert-rules
  namespace: monitoring
  labels:
    role: alert-rules
spec:
  groups:
    - name: middleware_alerts
      rules:
        - alert: MySQLDown
          expr: mysql_up == 0
          for: 2m
          labels:
            severity: critical
            tenant: "{{ $labels.tenant }}"
          annotations:
            summary: "MySQL 实例 {{ $labels.instance }} 不可达"
        - alert: MySQLSlowQuerySpike
          expr: mysql_slow_query_rate > 1
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "慢查询率升高"
        - alert: RedisHitRateLow
          expr: redis_hit_rate < 0.9
          for: 10m
          labels:
            severity: warning
          annotations:
            summary: "Redis 命中率低于 90%"
        - alert: KafkaConsumerLagHigh
          expr: kafka_consumer_lag_sum > 100000
          for: 10m
          labels:
            severity: warning
          annotations:
            summary: "Kafka 消费堆积超过 10 万"
        - alert: BackupFailed
          expr: (time() - middleware_backup_last_success_timestamp) > 86400
          for: 5m
          labels:
            severity: critical
          annotations:
            summary: "实例 {{ $labels.app }} 超过 24h 无成功备份"
        - alert: BackupJobFailed
          expr: kube_job_status_failed{job_name=~".*backup.*"} == 1
          for: 5m
          labels:
            severity: critical
          annotations:
            summary: "备份 Job 执行失败"

故障 1:MySQL 主库宕机,写入中断

现象:Grafana 显示主库 Pod 非就绪,应用报连接拒绝,Alertmanager P1(MySQLDown)。 排查:① 联邦大盘确认主从状态;② 查节点是否 NotReady(资源 / 网络);③ 查 PVC 是否绑定。 优化:控制器自动选新主,Service 端点不变;事后复盘是否跨故障域,必要时迁移副本。

故障 2:Kafka 消费堆积(Lag 暴涨)

现象:联邦指标 kafka_consumer_lag_sum 持续上升,触发 KafkaConsumerLagHigh排查:① 消费端是否宕机;② 分区数是否过少;③ 下游依赖是否变慢。 优化:临时扩消费副本 + 增加分区;长期做消费端限流与背压。

故障 3:误删数据需恢复

现象:运维误执行 DROP,业务要求找回。 排查:① 确认备份存在且未过期;② 取最近全量 + 重放 binlog 到删除前时间点。 优化:走 PITR 恢复到临时实例校验,再切流量;全程审批 + 审计。

故障处置 runbook

# 主库故障自动选主验证:删主库 Pod,观察副本顶替
kubectl -n tenant-a-prod delete pod mysql-order-0
kubectl -n tenant-a-prod get pods -l app=mysql-order -w

# 手动触发一次备份(PITR 基点)
kubectl -n tenant-a-prod create job mysql-order-backup-manual --from=cronjob/mysql-order-backup

# PITR 恢复到指定时间点(先恢复到临时实例校验,再切流)
kubectl -n tenant-a-prod run mysql-restore --image=harbor.example.com/paas-mysql-backup \
  --restart=Never -- backup --pitr --target-time="2025-10-15T02:00:00+08:00" \
  --src=s3://paas-backup/tenant-a/mysql-order/ --dst=/restore
kubectl -n tenant-a-prod exec mysql-restore -- mysql -h mysql-order < /restore/export.sql

# 确认备份成功时间戳指标(配合 BackupFailed 告警)
kubectl -n tenant-a-prod get pods -l paas.example.com/type=backup

# redis-benchmark 制造热 Key,验证联邦大盘热 Key 指标 + Alertmanager 告警
redis-benchmark -h redis-cache.tenant-a-prod.svc -t set -n 100000 -r 10

下面这张图给出中间件故障排查通用流程。

flowchart TD
  S[联邦告警触发] --> A{实例是否不可用?}
  A -->|是| B[确认主从/副本状态]
  B --> C[控制器自动选主/顶替]
  A -->|否| D{指标异常类型?}
  D -->|慢查询| E[查索引/参数基线]
  D -->|热Key/堆积| F[扩副本/加分区]
  D -->|数据误删| G[走PITR恢复到临时实例]
  G --> H[校验后切流]
  C --> I[恢复后审计归档]
  E --> I
  F --> I
  H --> I

这张流程图把"告警 → 不可用/指标异常二分 → 选主/索引/扩副本/PITR → 审计归档"串成可复用的处置路径。


自测题与动手练习

自测题

  1. 中间件实例运行在何处?其隔离边界由什么保证?
  2. PITR 恢复的原理是什么?为什么备份必须与实例跨故障域?
  3. 中间件指标进入联邦必须携带哪四个标签?app 维度在中间件场景取什么?
  4. 生产环境为何强制 KMS 信封加密与 NetworkPolicy 隔离?它们分别防什么?
  5. Kafka 消费堆积时应从哪几个方向排查与优化?

动手练习

  1. tenant-a-test 下申请 1 主 2 从 MySQL 实例,确认 StatefulSet / PVC / Service 落地到目标集群 Namespace。
  2. 配置每日全量 + binlog 增量备份,并手动触发一次 PITR 恢复到指定时间戳,校验数据完整。
  3. 用 redis-benchmark 制造热 Key,在联邦 Grafana 租户大盘观察热 Key 指标并确认 Alertmanager 触发分级告警。

本章小结

  • 中间件托管平台把 MySQL / Redis / Kafka / ES 等从"各团队裸装"升级为"按需申请、平台护航"的标准托管。
  • ★ 备份、加密、资源隔离三项不可删减底线,分别守住"数据可恢复"“静态不可读"“邻居不互扰”。
  • 平台侧 Go 控制面(pkg/gateway + pkg/middleware + pkg/handler)用 client-go(dynamic client)完成 MiddlewareInstance 创建(调 operator)、备份 Job 触发、实例状态读取,并通过 paas_middleware_* / middleware_backup_* 指标与联邦观测配置一一对齐。
  • 实例运行在租户 Namespace,指标带 tenant/app/env/cluster 进联邦,Grafana 隔离 + Alertmanager 分级联动。
  • 生产管控显著严于测试:强制审批、加密、自动备份与严格网络隔离,差异贯穿创建到销毁全生命周期。

下一模块我们将进入「统一观测平台(Prometheus 联邦对接)」,深入联邦架构本身如何支撑以上所有模块的多租户监控与长期存储。

About Me

没什么想介绍的,一个很大众的码农…

喜欢代码,车,马,真的是 🐎

讨厌别人让我给自己的代码写注释 最厌烦别人的程序没有写注释

目标

学AI,加油!加油!