多集群管理:Karmada 与 Cluster API > 多集群不是"多份 yaml 重复 apply",而是统一调度、差异覆盖、故障自愈的分发体系
写在前面
你可能已经用 kubectl 管了 3 个集群------开发、预发、生产,每天 kubectl --context=prod apply、kubectl --context=staging apply、kubectl --context=dev apply,三遍操作,六个终端窗口。但当集群变成 10 个(国内 3 个区域 + 海外 2 个 + 边缘集群 5 个),手动管理就崩了:某个集群配置忘了同步,另一个集群资源配额不对,还有个集群版本没升级。这篇文章讲的是:用 Karmada 统一调度多集群资源,用 Cluster API 管理集群生命周期,用 mcs-api 实现跨集群服务发现,用容灾策略实现集群级故障自愈。读完这篇,你就不再用"多份 yaml 重复 apply"来管理多集群了。
核心问题
多集群怎么统一管理与应用分发------让一个控制面管 N 个集群,调度有策略,覆盖有差异,故障有自愈?
一、原理剖析
1.1 多集群架构模式
多集群不是只有一种模式,根据业务需求有三种主流架构:
三种多集群架构模式:
1. 联邦式(Federation) --- Karmada 模式
┌──────────────────┐
│ Karmada 控制面 │ ← 统一管理所有集群的调度/分发/容灾
│(karmada-apiserver│
│ /etcd/scheduler)│
└──┬───┬───┬───┬───┘
│ │ │ │
┌──▼┌──▼┌──▼┌──▼┐
│C1 │C2 │C3 │C4 │ ← 成员集群,资源由 Karmada 分发
└───┘───┘───┘───┘
优势:统一入口,全局调度,容灾切换
劣势:控制面是单点,需要 HA
2. 辐条式(Spoke/Hub) --- Rancher 模式
┌──────────────┐
│ Hub 集群 │ ← 管理集群,存所有配置
│(ArgoCD/Rancher│
│ /Fleet) │
└──┬───┬───┬───┘
│ │ │
┌──▼┌──▼┌──▼┐
│S1 │S2 │S3 │ ← 辐条集群,从 Hub 拉取配置
└───┘───┘───┘
优势:Hub 集群可以独立运行
劣势:辐条集群间不能直接通信
3. 星型(Star) --- 边缘场景
┌──────────────────┐
│ 中心集群 │ ← 管理所有边缘集群
│(控制面+全局服务) │
└──┬───┬───┬───┬───┘
│ │ │ │
┌──▼┌──▼┌──▼┌──▼┐
│E1 │E2 │E3 │E4 │ ← 边缘集群,资源有限,自治运行
└───┘───┘───┘───┘
优势:边缘自治,断网后独立运行
劣势:边缘集群能力受限(不能承载全局服务)
这篇文章重点讲联邦式(Karmada),因为它最适合多区域多活+容灾场景。
1.2 Karmada 架构
Karmada 的核心是:一个独立的控制面(karmada-apiserver + etcd),管理多个成员集群。它不侵入成员集群,而是在控制面保存资源的"分发意图",然后由各控制器把资源推送到成员集群。
Karmada 架构:
┌───────────────────────────────────────────────────────────┐
│ Karmada 控制面 │
│ │
│ ┌─────────────────┐ ┌──────────────┐ ┌─────────────┐ │
│ │ karmada-apiserver│ │ etcd │ │ controller- │ │
│ │ (K8s API 兼容) │ │(存储分发意图 │ │ manager │ │
│ │ │ │ +成员集群状态│ │ (分发/同步/ │ │
│ └─────────────────┘ └──────────────┘ │ 容灾/解绑) │ │
│ └─────────────┘ │
│ ┌─────────────────┐ ┌──────────────┐ ┌─────────────┐ │
│ │ karmada-scheduler│ │ descheduler │ │ webhook │ │
│ │ (集群亲和/权重/ │ │ (再平衡调度 │ │ (准入校验) │ │
│ │ 分散策略) │ │ 策略) │ │ │ │
│ └─────────────────┘ └──────────────┘ └─────────────┘ │
└──────────────────────────┬───────────────────────────────┘
│
┌──────────────────────┼───────────────────────┐
│ │ │
┌───▼─────┐ ┌──────▼─────┐ ┌──────▼─────┐
│Cluster-CN│ │Cluster-US │ │Cluster-EU │
│(成员集群)│ │(成员集群) │ │(成员集群) │
│ │ │ │ │ │
│ karmada- │ │ karmada- │ │ karmada- │
│ agent │ │ agent │ │ agent │
│(上报状态 │ │(上报状态 │ │(上报状态 │
│ 执行分发)│ │ 执行分发) │ │ 执行分发) │
└──────────┘ └───────────┘ └───────────┘
核心流程:
1. 用户在 karmada-apiserver 创建 Deployment + PropagationPolicy
2. scheduler 根据策略选择目标集群(ClusterAffinity/SpreadConstraints/Weight)
3. controller-manager 把 Deployment 推送到选定的成员集群
4. karmada-agent 上报成员集群的资源状态(workload 状态/资源使用率)
5. 如果集群故障,descheduler 把资源迁移到其他健康集群
核心 CRD:
- PropagationPolicy: 定义资源"分发到哪些集群、怎么调度"
- OverridePolicy: 定义资源"到达某个集群后,怎么差异化修改"
- Cluster: 成员集群的注册信息(API 地址/状态/标签)
- ResourceBinding: scheduler 调度结果(哪个资源分发到哪些集群)
- Work: 实际推送到成员集群的资源清单(由 controller-manager 管理)
1.3 Cluster API:集群生命周期管理
Karmada 管的是"资源分发",Cluster API 管的是"集群本身怎么创建/升级/删除"。两者互补:Cluster API 创建集群 → 注册到 Karmada → Karmada 分发应用。
Cluster API 核心概念:
┌──────────────────────────────────────────────┐
│ 管理集群(Bootstrap) │
│ │
│ ┌──────────────┐ ┌────────────────────┐ │
│ │ Cluster CRD │ │ Machine CRD │ │
│ │(集群元数据: │ │(节点定义:infra │ │
│ │ 网络/版本/ │ │ provider 模板/ │ │
│ │ 控制面数量) │ │ OS/image) │ │
│ └──────────────┘ └────────────────────┘ │
│ │
│ ┌────────────────────┐ ┌──────────────┐ │
│ │ MachineDeployment │ │ InfraProvider │ │
│ │(节点组:滚动升级 │ │ (AWS/GCP/vSphere│ │
│ │ MinReady/MaxSurge)│ │ /Metal) │ │
│ └────────────────────┘ └──────────────┘ │
│ │
│ ┌──────────────────────────────┐ │
│ │ CAPD (Provider Docker) │ ← 测试用 │
│ │ CAPA (Provider AWS) │ ← AWS 集群│
│ │ CAPG (Provider GCP) │ ← GCP 集群│
│ │ CAPV (Provider vSphere) │ ← vSphere │
│ │ CAPM (Provider Metal3) │ ← 裸金属 │
│ └──────────────────────────────┘ │
└──────────────────────────────────────────────┘
集群生命周期:
Cluster API 创建集群 → Karmada 注册集群 → Karmada 分发应用
Cluster API 升级集群 → Karmada 纳管不变 → 应用继续运行
Cluster API 删除集群 → Karmada 解绑集群 → 容灾策略触发迁移
1.4 跨集群服务治理(mcs-api)
多集群里,一个服务可能部署在多个集群中,客户端需要跨集群发现和访问。mcs-api(Multi-Cluster Service API)就是做这件事的:
mcs-api 工作流程:
Cluster A (服务提供方):
┌──────────────┐
│ ServiceExport │ → 声明"我要把 myapp 服务导出给其他集群"
│ name: myapp │
└──────────────┘
│
│ Karmada 控制面同步
│
Cluster B (服务消费方):
┌──────────────┐
│ ServiceImport │ → 岀入"我要从其他集群导入 myapp 服务"
│ name: myapp │ 创建一个 Endpoints 对象,指向 Cluster A 的 Pod IP
│ clusters: │ (或 ClusterIP,取决于网络模式)
│ - name: A │
└──────────────┘
│
│ 服务消费方 Pod 访问 myapp → 路由到 Cluster A 的 Pod
网络前提:
- 集群间网络必须互通(VPC Peering / VPN / Submariner)
- 或者用 L7 网关(Ingress/Gateway API)做跨集群路由
二、实战操作
2.1 Karmada 部署
bash
# 方式 1:用 Helm 安装 Karmada 控制面(推荐)
helm repo add karmada https://raw.githubusercontent.com/karmada-io/karmada/master/charts
helm repo update
# 在管理集群上安装 Karmada 控制面
helm install karmada karmada/karmada-operator \
-n karmada-system \
--create-namespace \
--set components=karmada-apiserver,karmada-etcd,karmada-controller-manager,karmada-scheduler,karmada-descheduler,karmada-webhook \
--set karmada-apiserver.replicaCount=3 \
--set karmada-etcd.replicaCount=3 \
--set karmada-controller-manager.replicaCount=2 \
--set karmada-scheduler.replicaCount=2 \
--set karmada-descheduler.enabled=true \
--wait
# 方式 2:用 karmadactl 初始化(适合测试)
karmadactl init --karmada-apiserver-port=32443 --kubeconfig=/path/to/host-cluster.kubeconfig
# 获取 Karmada kubeconfig
karmadactl kubeconfig --karmada-apiserver-port=32443 > karmada-kubeconfig
# 注册成员集群
karmadactl join cluster-cn --kubeconfig=karmada-kubeconfig \
--cluster-kubeconfig=/path/to/cluster-cn.kubeconfig \
--cluster-context=cluster-cn
karmadactl join cluster-us --kubeconfig=karmada-kubeconfig \
--cluster-kubeconfig=/path/to/cluster-us.kubeconfig \
--cluster-context=cluster-us
karmadactl join cluster-eu --kubeconfig=karmada-kubeconfig \
--cluster-kubeconfig=/path/to/cluster-eu.kubeconfig \
--cluster-context=cluster-eu
# 验证成员集群状态
kubectl --kubeconfig=karmada-kubeconfig get clusters
# NAME VERSION MODE READY AGE
# cluster-cn v1.30 Push True 5m
# cluster-us v1.30 Push True 3m
# cluster-eu v1.30 Push True 1m
Karmada 的两种成员集群管理模式:
- Push 模式(默认): Karmada 控制面直接推资源到成员集群,需要成员集群的 kubeconfig
- Pull 模式: 成员集群的 karmada-agent 主动从 Karmada 控制面拉取资源,适合边缘/不可靠网络场景
2.2 PropagationPolicy 与 OverridePolicy
PropagationPolicy --- 定义资源分发策略:
yaml
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
name: myapp-propagation
spec:
resourceSelectors:
- apiVersion: apps/v1
kind: Deployment
name: myapp
- apiVersion: v1
kind: Service
name: myapp
- apiVersion: networking.k8s.io/v1
kind: Ingress
name: myapp
placement:
clusterAffinity:
clusterNames: # 指定目标集群
- cluster-cn
- cluster-us
- cluster-eu
clusterTolerations: # 集群容忍度(类似 node tolerations)
- key: cluster.region
operator: Equal
value: cn
effect: NoSchedule
spreadConstraints:
- spreadByLabel: cluster.region # 按区域标签分散
maxGroups: 3 # 最多 3 个区域
minGroups: 2 # 至少 2 个区域(高可用)
replicaScheduling:
replicaSchedulingType: Divided # 分配副本数(不是每个集群全量)
replicaDivisionPreference: Weighted # 按权重分配
weightPreference:
staticWeightList:
- clusterName: cluster-cn
weight: 5 # 中国集群 5 份权重
- clusterName: cluster-us
weight: 3 # 美国集群 3 份权重
- clusterName: cluster-eu
weight: 2 # 欧洲集群 2 份权重
suspension:
dispatching: false # 不暂停分发
OverridePolicy --- 环境差异化覆盖:
yaml
apiVersion: policy.karmada.io/v1alpha1
kind: OverridePolicy
metadata:
name: myapp-cn-overrides
spec:
resourceSelectors:
- apiVersion: apps/v1
kind: Deployment
name: myapp
overrideRules:
- targetCluster:
clusterNames: [cluster-cn] # 只覆盖中国集群
overriders:
plaintext:
- path: /spec/template/spec/containers/0/resources/requests/cpu
value: "500m" # 中国集群 CPU 更大(流量更大)
- path: /spec/template/spec/containers/0/env
operator: add
value:
- name: REGION
value: cn
- name: DB_HOST
value: cn-mysql.internal
imageOverrider:
- component: registry # 替换镜像仓库地址
operator: replace
value: registry.cn.internal # 中国集群用国内镜像仓库
yaml
apiVersion: policy.karmada.io/v1alpha1
kind: OverridePolicy
metadata:
name: myapp-us-overrides
spec:
resourceSelectors:
- apiVersion: apps/v1
kind: Deployment
name: myapp
overrideRules:
- targetCluster:
clusterNames: [cluster-us]
overriders:
plaintext:
- path: /spec/template/spec/containers/0/resources/requests/cpu
value: "200m"
- path: /spec/template/spec/containers/0/env
operator: add
value:
- name: REGION
value: us
- name: DB_HOST
value: us-mysql.internal
imageOverrider:
- component: registry
operator: replace
value: registry.us.internal
ClusterOverridePolicy --- 集群级别的全局覆盖(优先级更高):
yaml
apiVersion: policy.karmada.io/v1alpha1
kind: ClusterOverridePolicy
metadata:
name: global-storageclass
spec:
resourceSelectors:
- apiVersion: apps/v1
kind: StatefulSet
overrideRules:
- targetCluster:
labelSelector:
matchLabels:
cluster.storage: local-ssd # 所有 SSD 存储的集群
overriders:
plaintext:
- path: /spec/volumeClaimTemplates/0/spec/storageClassName
value: local-ssd # 替换 StorageClass
2.3 Cluster API 集群生命周期管理
安装 Cluster API provider:
bash
# 初始化管理集群(选择需要的 provider)
clusterctl init --infrastructure aws # AWS 集群
# 或
clusterctl init --infrastructure docker # 测试用 Docker 集群
# 或
clusterctl init --infrastructure vsphere # vSphere 集群
# 创建新集群(AWS 示例)
clusterctl generate cluster my-cluster \
--kubernetes-version v1.30.0 \
--control-plane-machine-count=3 \
--worker-machine-count=5 \
--infrastructure aws > cluster.yaml
kubectl apply -f cluster.yaml
# 等待集群就绪
clusterctl describe cluster my-cluster
# 获取新集群的 kubeconfig
clusterctl get kubeconfig my-cluster > my-cluster-kubeconfig
# 升级集群(K8s 版本滚动升级)
kubectl patch machinedeployment my-cluster-md-0 \
--type merge \
-p '{"spec":{"template":{"spec":{"version":"v1.30.1"}}}}'
# MachineDeployment 会像 Deployment 滚动升级一样:
# 创建新版本 Machine → 新节点 Ready → 删除旧版本 Machine → 旧节点终止
# 删除集群
kubectl delete cluster my-cluster
# Cluster API 会自动清理所有 Machine 和基础设施资源
MachineDeployment 滚动升级策略:
yaml
apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineDeployment
metadata:
name: my-cluster-md-0
spec:
replicas: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 同时多创建 1 个新节点
maxUnavailable: 0 # 不允许有节点不可用(保守策略)
deletePolicy: Random # 删除旧节点的顺序
minReadySeconds: 300 # 新节点 Ready 后等 5 分钟再删旧节点
template:
spec:
version: v1.30.0
bootstrap:
configRef:
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
kind: KubeadmConfigTemplate
name: my-cluster-md-0
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: AWSMachineTemplate
name: my-cluster-md-0
2.4 跨集群服务发现(mcs-api)
ServiceExport --- 在集群 A 导出服务:
yaml
apiVersion: multicluster.x-k8s.io/v1alpha1
kind: ServiceExport
metadata:
name: myapp
namespace: myapp
ServiceImport --- 在集群 B 导入服务:
yaml
apiVersion: multicluster.x-k8s.io/v1alpha1
kind: ServiceImport
metadata:
name: myapp
namespace: myapp
spec:
type: ClusterSetIP # 集群集 IP(虚拟 IP,路由到导出集群)
ports:
- port: 8080
protocol: TCP
clusters:
- cluster: cluster-cn # 服务来源集群
Karmada 自动同步 ServiceExport/ServiceImport:
bash
# 在 Karmada 控制面创建 ServiceExport
kubectl --kubeconfig=karmada-kubeconfig apply -f service-export.yaml
# Karmada controller-manager 会自动:
# 1. 把 ServiceExport 推送到 cluster-cn(源集群)
# 2. 在其他成员集群创建 ServiceImport + Endpoints
# 验证跨集群服务发现
kubectl --kubeconfig=cluster-us-kubeconfig get endpoints -n myapp myapp
# 应该看到指向 cluster-cn Pod IP 的 Endpoints
2.5 多集群容灾方案
ClusterHealthCheck + 故障转移:
yaml
apiVersion: cluster.karmada.io/v1alpha1
kind: ClusterHealthCheck
metadata:
name: cluster-health
spec:
clusterNames:
- cluster-cn
- cluster-us
- cluster-eu
checkInterval: 30s # 每 30s 检查一次集群健康
healthyThreshold: 3 # 连续 3 次健康才标记 Healthy
unhealthyThreshold: 3 # 连续 3 次不健康才标记 Unhealthy
checkType: APIReady # 检查类型:API Server 可达性
PropagationPolicy 容灾配置:
yaml
apiVersion: policy.karmada.io/v1alpha1
kind: PropagationPolicy
metadata:
name: myapp-ha
spec:
resourceSelectors:
- apiVersion: apps/v1
kind: Deployment
name: myapp
placement:
clusterAffinity:
clusterNames:
- cluster-cn
- cluster-us
- cluster-eu
spreadConstraints:
- spreadByLabel: cluster.region
maxGroups: 3
minGroups: 2
replicaScheduling:
replicaSchedulingType: Divided
replicaDivisionPreference: Weighted
weightPreference:
staticWeightList:
- clusterName: cluster-cn
weight: 5
- clusterName: cluster-us
weight: 3
- clusterName: cluster-eu
weight: 2
failover:
application: Evict # 集群故障时,迁移应用到其他健康集群
purgingMode: Graciously # 优雅迁移:等旧集群 Pod 终止后再删资源
故障转移流程:
正常状态:
cluster-cn: 50% replicas (5/10)
cluster-us: 30% replicas (3/10)
cluster-eu: 20% replicas (2/10)
cluster-cn 故障:
1. ClusterHealthCheck 检测 cluster-cn Unhealthy(连续 3 次)
2. Karmada 标记 cluster-cn 为 Unhealthy
3. failover.application=Evict 触发迁移
4. descheduler 把 cluster-cn 的 5 个 replica 重新分配:
- cluster-us: 30% → 60% (6/10)
- cluster-eu: 20% → 40% (4/10)
5. purgingMode=Graciously: 等 cluster-cn Pod 自然终止后清理资源
故障后状态:
cluster-cn: 0% replicas (故障,被驱逐)
cluster-us: 60% replicas (6/10)
cluster-eu: 40% replicas (4/10)
cluster-cn 恢复:
1. ClusterHealthCheck 检测 cluster-cn 恢复 Healthy
2. descheduler 重新平衡:
- cluster-cn: 50% (5/10)
- cluster-us: 30% (3/10)
- cluster-eu: 20% (2/10)
三、踩坑与排查
踩坑 1:PropagationPolicy 分发的 Deployment 在成员集群上 imagePullSecrets 丢失
现象 : 在 Karmada 控制面创建的 Deployment 带了 imagePullSecrets,但推送到成员集群后 secrets 不存在,Pod 启动失败 ImagePullBackOff。
原因 : PropagationPolicy 只分发你在 resourceSelectors 里指定的资源。你指定了 Deployment,但没指定 Secret,所以 Secret 不被分发。成员集群上 Deployment 引用的 Secret 不存在。
bash
# 在成员集群上查看
kubectl --kubeconfig=cluster-cn-kubeconfig get secrets -n myapp
# 没有 registry-secret!
# 在 Karmada 控制面查看 PropagationPolicy
kubectl --kubeconfig=karmada-kubeconfig get propagationpolicy myapp-propagation -o yaml
# resourceSelectors 只有 Deployment/Service/Ingress,没有 Secret
解决: 把 Secret 也加入 PropagationPolicy 的 resourceSelectors:
yaml
spec:
resourceSelectors:
- apiVersion: apps/v1
kind: Deployment
name: myapp
- apiVersion: v1
kind: Service
name: myapp
- apiVersion: v1
kind: Secret
name: registry-secret # 加上 Secret!
教训: PropagationPolicy 的 resourceSelectors 必须覆盖资源引用的所有依赖(Secret、ConfigMap、PVC、ServiceAccount 等)。否则成员集群上资源创建成功但引用失败。
蹈坑 2:OverridePolicy 的 plaintext patch 路径写错,成员集群上 Deployment 字段丢失
现象 : OverridePolicy 配置了覆盖 image.registry,但推送到成员集群后,Deployment 的整个 image 字段变成空了。
原因 : OverridePolicy 的 plaintext overrider 使用 JSON Path 定位修改字段。如果你写的 path 是 /spec/template/spec/containers/0/image,它会替换整个 image 对象(包括 repository/tag/pullPolicy),而不是只替换 registry 部分。
yaml
# 错误写法:整字段替换
overriders:
plaintext:
- path: /spec/template/spec/containers/0/image
value: "registry.cn.internal/company/myapp:v2.4.1"
# 这会替换整个 image 字段,丢失 pullPolicy 等
# 正确写法:只替换 registry 部分(用 imageOverrider)
overriders:
imageOverrider:
- component: registry
operator: replace
value: registry.cn.internal
# 只替换 registry,保留 tag/pullPolicy
解决 : 优先使用 imageOverrider(专门处理镜像)和 commandOverrider(专门处理命令),而不是 plaintext 整字段替换。只有简单字段(cpu/memory 数值)才用 plaintext。
蹈坑 3:集群故障转移后应用短暂不可用------新集群 Pod 还没 Ready,旧集群资源已删除
现象: cluster-cn 故障,descheduler 把 replica 迁移到 cluster-us,但 cluster-us 的新 Pod 还在启动中,cluster-cn 的资源已被清理,总可用 replica 数从 10 降到 4,服务部分降级。
原因 : 默认 purgingMode 是 Immediately(立即清理故障集群资源),不等新集群 Pod Ready。
解决:
yaml
failover:
application: Evict
purgingMode: Graciously # 优雅模式:等新集群资源就绪后再清理旧集群资源
另外,在 PropagationPolicy 中设置 replicaSchedulingType: Divided + 合理的权重,确保每个集群都有足够的 replica 承载独立运行的能力(不是所有 replica 都集中在主集群)。
踩坑 4:Cluster API 创建集群时 Machine 一直 Pending
现象 : clusterctl describe cluster my-cluster 显示控制面 Machine 状态是 Provisioning,一直不变成 Ready。
bash
# 查看 Machine 详细事件
kubectl describe machine my-cluster-control-plane-0
# 常见错误: "failed to create infrastructure resource: AWSMachine"
原因排查:
bash
# 1. 检查 InfraProvider 日志
kubectl logs -n capi-system deployment/capa-controller-manager
# 常见: "failed to create EC2 instance: InsufficientInstanceCapacity"
# 2. 检查 IAM 权限
# Cluster API provider 需要 IAM 权限创建 EC2/VPC/Subnet
# 确保 management cluster 的 ServiceAccount 有足够的权限
# 3. 检查 SSH key pair
# AWSMachineTemplate 需要引用已存在的 key pair 名
aws ec2 describe-key-pairs --key-names my-cluster-key
解决: 确保 Infrastructure Provider 的所有前置条件(IAM 权限/VPC 配置/SSH key/AMI ID)都已满足。Cluster API 不会帮你创建这些基础设施依赖,它只负责创建 K8s 节点。
四、最佳实践
Karmada 多集群治理清单
| 项目 | 建议 | 说明 |
|---|---|---|
| 控制面 HA | karmada-apiserver 3 replicas | 生产必须 HA |
| 控制面 HA | etcd 3 replicas + 定期备份 | 控制面数据丢失 = 全局不可管理 |
| 成员集群模式 | Push 模式(可靠网络)/Pull 模式(边缘) | 根据网络可靠性选择 |
| 注册流程 | karmadactl join + 验证 Ready | 注册后必须验证集群状态 |
| PropagationPolicy | resourceSelectors 必须覆盖所有依赖 | Secret/ConfigMap/PVC 不要遗漏 |
| OverridePolicy | 优先用 imageOverrider/commandOverrider | 避免 plaintext 整字段替换 |
| 副本调度 | Divided + Weighted | 不要每个集群全量副本 |
| 分散策略 | minGroups >= 2 | 至少 2 个区域/集群保证高可用 |
| 容灾 | purgingMode=Graciously | 优雅迁移,不立即清理 |
| 容灾 | unhealthyThreshold >= 3 | 不要一次检查失败就判定故障 |
| 监控 | ClusterHealthCheck 30s 间隔 | 及时发现集群异常 |
| 网络 | 集群间 VPC Peering 或 Submariner | 跨集群服务发现的前提 |
| mcs-api | ServiceExport/ServiceImport 配合 Karmada | 跨集群服务发现 |
| 升级 | 先升级成员集群 K8s,再升级 Karmada | Karmada 版本要兼容成员集群 |
Cluster API 生命周期管理清单
| 项目 | 建议 |
|---|---|
| Provider 选择 | 测试用 Docker,生产用 AWS/vSphere/Metal3 |
| MachineDeployment | maxSurge=1, maxUnavailable=0(保守滚动) |
| minReadySeconds | 300s(5 分钟),新节点稳定后再删旧节点 |
| 版本升级 | MachineDeployment patch version 字段,自动滚动 |
| 节点 drain | KubeadmConfig 中 preKubeadmCommands 添加 drain |
| 清理 | 删除 Cluster 时自动清理 Machine 和 Infra 资源 |
| 集群注册 | Cluster API 创建集群 → karmadactl join 注册到 Karmada |
多集群应用分发原则
- 每个集群独立可运行: 不要把所有 replica 集中在一个集群,每个集群至少有 30% 的副本
- 覆盖差异最小化: OverridePolicy 只覆盖必要差异(镜像仓库/数据库地址/StorageClass),不要覆盖太多
- 依赖一起分发: PropagationPolicy 的 resourceSelectors 必须包含所有依赖资源
- 集群标签标准化: 给 Cluster 对象打上 region/zone/storage/environment 标签,方便 affinity 选择
- 故障演练: 定期模拟集群故障(断网/关 API Server),验证 failover 策略是否生效
- 网络互通优先: 跨集群服务发现的前提是网络互通,先解决网络再考虑 mcs-api
五、小结
多集群管理的本质是:把"每集群手动 apply"升级为"一个控制面统一调度+差异化覆盖+故障自愈"。Karmada 用 PropagationPolicy 定义分发策略(集群亲和/权重/分散),OverridePolicy 定义环境差异(镜像仓库/数据库/StorageClass),ClusterHealthCheck+failover 实现集群级容灾。Cluster API 补充了集群生命周期管理(创建/升级/删除),两者配合形成"集群生命周期+应用分发"的完整闭环。跨集群服务用 mcs-api 的 ServiceExport/ServiceImport。关键教训:PropagationPolicy 必须覆盖所有依赖资源(遗漏 Secret = Pod 启动失败);OverridePolicy 不要用 plaintext 整字段替换(用 imageOverrider 更安全);容灾迁移设 Graciously 模式(不等新集群 Ready 就删旧集群 = 服务降级)。
思考题
- 你的公司有 5 个区域集群,每个集群流量不同。如果 cluster-cn 的流量占 60%,cluster-us 占 25%,cluster-eu 占 15%,怎么设计 replicaScheduling 的权重让 Pod 数量匹配流量比例?
- Karmada 的 Push 模式和 Pull 模式分别适合什么场景?边缘集群(经常断网)应该用哪种?
- 如果 cluster-cn 完全宕机(API Server 不可达),Karmada 怎么知道它故障了?如果网络只是短暂抖动(30s 内恢复),怎么避免误判故障触发不必要的迁移?