4. 故障域模型:节点故障域(region/zone)与 Pod 拓扑分布约束(topologySpreadConstraints)

在 Kubernetes 集群中,故障域(Failure Domain)是基础设施层面的物理或逻辑隔离边界。当集群跨越多个数据中心或可用区时,合理利用故障域模型可以确保应用在节点、机架、可用区甚至地域级别实现高可用。Helm 部署的故障域隔离演练,核心就是验证 Pod 是否按照预设的拓扑分布约束分散到不同的故障域中。

4.1 节点故障域:region 与 zone

Kubernetes 通过节点标签(Labels)来标识故障域。最常用的两个标签是:

  • topology.kubernetes.io/region:表示节点所在的地理区域(如 cn-north-1)。
  • topology.kubernetes.io/zone:表示节点所在的可用区(如 cn-north-1a)。

一个 region 通常包含多个 zone,每个 zone 拥有独立的电力、网络和冷却系统。当集群节点分布在多个 zone 时,即使某个 zone 整体故障,其他 zone 的 Pod 仍可继续提供服务。

故障域层级 标签键 示例值 隔离粒度
地域(Region) topology.kubernetes.io/region cn-north-1 跨数据中心区域
可用区(Zone) topology.kubernetes.io/zone cn-north-1a 独立故障隔离单元
主机(Host) kubernetes.io/hostname ip-10-0-1-23 单台物理/虚拟机
演练提示:在开始故障域隔离演练前,请使用 kubectl get nodes --show-labels 确认集群节点已正确打上 region 和 zone 标签。如果节点缺少这些标签,需要联系基础设施团队或手动添加。

4.2 Pod 拓扑分布约束(topologySpreadConstraints)

Pod 拓扑分布约束是 Kubernetes 原生提供的调度策略,用于控制 Pod 在不同故障域之间的分布均匀性。通过在 Deployment 或 StatefulSet 的 Pod 模板中定义 topologySpreadConstraints,可以强制调度器将 Pod 分散到不同的 region、zone 或 hostname 上。

核心字段说明:

字段 类型 说明
topologyKey string 指定故障域标签键,如 topology.kubernetes.io/zone
maxSkew int 允许的最大分布倾斜度,即任意两个故障域之间 Pod 数量的最大差值
whenUnsatisfiable string 约束不满足时的行为:DoNotSchedule(不调度)或 ScheduleAnyway(尽量调度)
labelSelector object 用于匹配 Pod 的标签选择器,通常与 Deployment 的 matchLabels 一致

4.3 故障域分布模型 SVG

下图展示了一个跨 2 个 region、每个 region 包含 2 个 zone 的集群故障域模型,以及 Pod 在 zone 级别的均匀分布情况:

故障域模型与 Pod 拓扑分布约束 故障域模型与 Pod 拓扑分布约束 Region A (cn-north-1) Region B (cn-east-1) Zone A1 (cn-north-1a) Zone A2 (cn-north-1b) Zone B1 (cn-east-1a) Zone B2 (cn-east-1b) Pod A1-1 Pod A1-2 Node Pod A2-1 Pod A2-2 Node Pod B1-1 Pod B1-2 Node Pod B2-1 Pod B2-2 Node 每个 Zone 部署 2 个 Pod,maxSkew=1 确保任意两个 Zone 之间 Pod 数量差不超过 1

4.4 Helm Chart 中的拓扑分布配置

在 Helm Chart 的 values.yaml 中,通常将拓扑分布约束设计为可配置项,以便在不同环境(开发、预发、生产)中灵活调整。以下是一个典型的配置模板:

# values.yaml
topologySpreadConstraints:
  - topologyKey: topology.kubernetes.io/zone
    maxSkew: 1
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        app: {{ .Chart.Name }}
  - topologyKey: kubernetes.io/hostname
    maxSkew: 2
    whenUnsatisfiable: ScheduleAnyway
    labelSelector:
      matchLabels:
        app: {{ .Chart.Name }}

在 Deployment 模板中引用该配置:

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Chart.Name }}
spec:
  replicas: {{ .Values.replicas }}
  selector:
    matchLabels:
      app: {{ .Chart.Name }}
  template:
    metadata:
      labels:
        app: {{ .Chart.Name }}
    spec:
      topologySpreadConstraints:
        {{- toYaml .Values.topologySpreadConstraints | nindent 8 }}
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
关键设计原则:
  • zone 级别约束使用 DoNotSchedule,确保 Pod 严格分散到不同可用区,避免单 zone 故障影响所有副本。
  • hostname 级别约束使用 ScheduleAnyway,作为软约束尽量分散到不同节点,但不强制阻塞调度。
  • maxSkew 值越小,分布越均匀,但调度失败风险越高。生产环境建议 zone 级别设为 1,hostname 级别设为 2。

4.5 演练验证清单

在故障域隔离演练中,需要逐项验证以下内容:

  1. 节点标签完整性:所有节点是否已正确标注 region 和 zone 标签。
  2. Helm 模板渲染:使用 helm template 检查生成的 YAML 中 topologySpreadConstraints 是否正确。
  3. Pod 分布状态:部署后使用 kubectl get pods -o wide 查看 Pod 所在节点和 zone。
  4. 分布均匀性:统计每个 zone 中的 Pod 数量,确认差值不超过 maxSkew。
  5. 故障模拟:手动封锁(cordon)一个 zone 中的所有节点,观察 Pod 是否被调度到其他 zone。
常见陷阱:
  • 如果集群节点数量少于 replicas 且 whenUnsatisfiable: DoNotSchedule,部分 Pod 将处于 Pending 状态。此时应降低副本数或改用 ScheduleAnyway。
  • labelSelector 必须与 Deployment 的 matchLabels 一致,否则拓扑约束不会生效。
  • 跨 region 调度需要集群网络支持,且可能引入额外的延迟开销,演练前应评估业务容忍度。