2. Kubernetes 故障域基础:节点、可用区、区域的概念,以及Pod调度与拓扑分布约束
2.1 故障域层次模型
Kubernetes 集群的物理或逻辑基础设施天然存在故障边界。理解这些边界是设计高可用应用交付的前提。故障域从下至上分为三个层级:节点、可用区、区域。
| 层级 | Kubernetes 标签 | 故障范围 | 典型故障场景 |
|---|---|---|---|
| 节点 | kubernetes.io/hostname |
单台物理机或虚拟机 | 硬件故障、内核崩溃、磁盘损坏、网络链路中断 |
| 可用区 | topology.kubernetes.io/zone |
数据中心内独立供电/网络单元 | 机架断电、接入交换机故障、制冷失效 |
| 区域 | topology.kubernetes.io/region |
地理上隔离的完整数据中心群 | 自然灾害、骨干网中断、大规模电力故障 |
关键认知: 故障域隔离的核心目标是将应用副本分散到不同故障域中,确保单一故障域失效时,剩余副本仍能维持服务容量。在容量规划基准测试中,必须明确每个故障域可承载的 Pod 数量上限。
2.2 节点级故障域与 Pod 调度基础
节点是 Kubernetes 中最小的调度单元。Pod 调度器默认基于节点资源(CPU、内存)进行打分与绑定,但并未感知故障域分布。以下机制用于控制 Pod 在节点间的分布:
- nodeSelector:基于节点标签的简单硬约束,用于将 Pod 定向到特定节点池。
- nodeAffinity:支持
requiredDuringSchedulingIgnoredDuringExecution(硬约束)和preferredDuringSchedulingIgnoredDuringExecution(软约束)两种语义。 - Pod 反亲和性(podAntiAffinity):强制或偏好将同一应用的多个副本调度到不同节点上。
# 示例:使用 podAntiAffinity 确保每个节点最多运行一个副本
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 5
template:
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-app
topologyKey: kubernetes.io/hostname
容量规划陷阱: 仅使用节点级反亲和性,当集群跨多个可用区时,Pod 仍可能全部集中到同一个可用区。一旦该可用区故障,所有副本同时丢失。必须结合可用区拓扑约束。
2.3 可用区与区域:拓扑分布约束
Kubernetes 1.19+ 引入 Pod Topology Spread Constraints(Pod 拓扑分布约束),这是实现故障域隔离的推荐方式。它允许用户定义 Pod 在多个拓扑域(如节点、可用区、区域)之间的分布策略。
# 示例:将副本均匀分布到 3 个可用区
apiVersion: apps/v1
kind: Deployment
metadata:
name: distributed-app
spec:
replicas: 9
template:
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: distributed-app
| 参数 | 说明 | 基准测试影响 |
|---|---|---|
maxSkew |
任意两个拓扑域之间 Pod 数量的最大差值 | 决定故障域内 Pod 密度上限,直接影响单域故障时的容量损失 |
topologyKey |
用于划分拓扑域的节点标签键 | 选择 zone 或 region 决定了隔离粒度 |
whenUnsatisfiable |
DoNotSchedule(硬约束)或 ScheduleAnyway(软约束) |
硬约束保证隔离但可能阻塞调度;软约束允许调度但可能打破均匀分布 |
容量规划基准测试要点: 在演练前,必须确定每个可用区的节点数量、每个节点的可调度 Pod 上限(受 CNI IP 池、节点资源、DaemonSet 占用影响),然后根据
maxSkew 计算每个可用区可安全调度的副本数。例如:3 个可用区,每个可用区 5 个节点,每个节点最多 30 个 Pod,则每个可用区最多 150 个 Pod,maxSkew=1 时全局副本数上限为 450。
2.4 故障域隔离的架构视图
下图展示了跨区域、跨可用区、跨节点的三层故障域隔离架构,以及 Pod 在拓扑约束下的分布效果。
2.5 故障域隔离的容量规划公式
在进行故障域隔离演练的容量规划基准测试时,需要预先计算以下关键指标:
- 单节点最大 Pod 数:
min(节点可分配IP数, 节点可分配CPU/Pod, 节点可分配内存/Pod) - 单可用区最大 Pod 数:
单节点最大Pod数 × 该可用区节点数 - 全局最大副本数(maxSkew=1):
单可用区最大Pod数 × 可用区数量 - 单故障域失效后剩余容量:
全局最大副本数 - 单可用区最大Pod数
基准测试验证点: 在演练中,通过模拟一个可用区完全故障,验证剩余可用区能否接管全部流量,且 Pod 不会因资源不足被驱逐。记录故障注入后的 Pod 重调度时间、服务降级比例、以及是否触发 PDB(PodDisruptionBudget)保护。
2.6 常见配置错误与避坑指南
| 错误模式 | 现象 | 根因 | 修复方案 |
|---|---|---|---|
仅使用 podAntiAffinity 且 topologyKey=hostname |
Pod 均匀分布在节点上,但全部集中在同一个可用区 | 未感知可用区拓扑 | 增加 topologySpreadConstraints 并设置 topologyKey=zone |
maxSkew 设置过大 |
故障域内 Pod 数量严重不均,单域故障时容量损失过大 | 容忍度过高 | 将 maxSkew 设为 1 或 2,根据可用区数量调整 |
未设置 whenUnsatisfiable: DoNotSchedule |
调度器在无法满足分布约束时仍将 Pod 调度到已饱和的故障域 | 软约束导致隔离失效 | 关键应用使用硬约束 DoNotSchedule |
| 节点标签缺失或错误 | 拓扑分布约束不生效,Pod 调度行为异常 | 节点未正确标记 topology.kubernetes.io/zone |
使用 kubectl label node 补全标签,或通过云厂商自动注入 |
演练前必检项: 使用
kubectl get nodes -o jsonpath='{.items[*].metadata.labels.topology\.kubernetes\.io/zone}' 确认所有节点已正确标记可用区。缺失标签的节点会被调度器忽略,导致拓扑约束失效。