【K8S 运维实战】34-多集群管理Karmada

多集群管理:Karmada 与 Cluster API > 多集群不是"多份 yaml 重复 apply",而是统一调度、差异覆盖、故障自愈的分发体系

写在前面

你可能已经用 kubectl 管了 3 个集群------开发、预发、生产,每天 kubectl --context=prod applykubectl --context=staging applykubectl --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,服务部分降级。

原因 : 默认 purgingModeImmediately(立即清理故障集群资源),不等新集群 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

多集群应用分发原则

  1. 每个集群独立可运行: 不要把所有 replica 集中在一个集群,每个集群至少有 30% 的副本
  2. 覆盖差异最小化: OverridePolicy 只覆盖必要差异(镜像仓库/数据库地址/StorageClass),不要覆盖太多
  3. 依赖一起分发: PropagationPolicy 的 resourceSelectors 必须包含所有依赖资源
  4. 集群标签标准化: 给 Cluster 对象打上 region/zone/storage/environment 标签,方便 affinity 选择
  5. 故障演练: 定期模拟集群故障(断网/关 API Server),验证 failover 策略是否生效
  6. 网络互通优先: 跨集群服务发现的前提是网络互通,先解决网络再考虑 mcs-api

五、小结

多集群管理的本质是:把"每集群手动 apply"升级为"一个控制面统一调度+差异化覆盖+故障自愈"。Karmada 用 PropagationPolicy 定义分发策略(集群亲和/权重/分散),OverridePolicy 定义环境差异(镜像仓库/数据库/StorageClass),ClusterHealthCheck+failover 实现集群级容灾。Cluster API 补充了集群生命周期管理(创建/升级/删除),两者配合形成"集群生命周期+应用分发"的完整闭环。跨集群服务用 mcs-api 的 ServiceExport/ServiceImport。关键教训:PropagationPolicy 必须覆盖所有依赖资源(遗漏 Secret = Pod 启动失败);OverridePolicy 不要用 plaintext 整字段替换(用 imageOverrider 更安全);容灾迁移设 Graciously 模式(不等新集群 Ready 就删旧集群 = 服务降级)。

思考题

  1. 你的公司有 5 个区域集群,每个集群流量不同。如果 cluster-cn 的流量占 60%,cluster-us 占 25%,cluster-eu 占 15%,怎么设计 replicaScheduling 的权重让 Pod 数量匹配流量比例?
  2. Karmada 的 Push 模式和 Pull 模式分别适合什么场景?边缘集群(经常断网)应该用哪种?
  3. 如果 cluster-cn 完全宕机(API Server 不可达),Karmada 怎么知道它故障了?如果网络只是短暂抖动(30s 内恢复),怎么避免误判故障触发不必要的迁移?

延伸阅读

相关推荐
Jul1en_1 小时前
【Java 脚手架】封装通用工具类-1
java·spring boot·分布式·spring·spring cloud
略略略咯咯1 小时前
centos更换镜像源
linux·运维·centos
关于不上作者榜就原神启动那件事2 小时前
从 MDC 到 Agent:我手搓的文档路由协议,在 Spring AI Alibaba 里找到了正式实现
java·人工智能·spring·ai·agent
亦暖筑序2 小时前
重新认识 AgentScope-Java 2.0:ReActAgent 负责推理,HarnessAgent 负责运行
java·后端·agent
IKUN家族2 小时前
Spring MVC(三)
运维·服务器·spring boot·spring·servlet·java-ee
木井巳2 小时前
【DFS解决floodfill算法】岛屿的最大面积
java·算法·leetcode·深度优先
Leon-Ning Liu2 小时前
【真实经验分享】ORA-12516 排查实录:共享服务器模式下 SHARED_SERVER_SESSIONS 不足引发的监听故障
运维·服务器·oracle
林森lsjs2 小时前
完结撒花!Java SE 语法阶段总结!
java·开发语言
lupai2 小时前
短信接口快速接入与调用实战指南
java·开发语言·数据库