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