4. PodAntiAffinity 与故障域:硬性反亲和与软性反亲和的区别、在 Values 中配置 topologyKey 实现跨节点/跨可用区调度
在 Kubernetes 集群中,Pod 的调度策略直接影响应用的可用性与容灾能力。PodAntiAffinity(Pod 反亲和性)是控制同一工作负载的多个副本“不要运行在一起”的核心机制,它与故障域(Failure Domain)结合,可以实现跨节点、跨可用区甚至跨区域的精细化调度隔离。
4.1 硬性反亲和(requiredDuringScheduling)与软性反亲和(preferredDuringScheduling)
PodAntiAffinity 有两种调度模式,它们的约束强度和执行时机不同,适用于不同的故障域隔离场景。
| 特性 | 硬性反亲和(required) | 软性反亲和(preferred) |
|---|---|---|
| 调度约束 | 必须满足,否则 Pod 无法调度 | 尽量满足,不满足时仍可调度 |
| 适用场景 | 关键业务,必须跨故障域隔离 | 资源优先,尽量分散但允许集中 |
| 集群资源压力 | 高,可能因资源不足导致 Pod 挂起 | 低,调度器会尝试但不会阻塞 |
| 故障域粒度 | 严格保证每个副本在不同域 | 倾向于分散,不保证隔离 |
| 权重支持 | 不支持权重 | 支持 weight(1-100) |
4.2 topologyKey:定义故障域的边界
topologyKey 是 PodAntiAffinity 的核心参数,它告诉调度器“以什么维度来判断 Pod 是否在同一个故障域”。Kubernetes 内置了标准的 topologyKey,也可以使用自定义标签。
- kubernetes.io/hostname:以节点为故障域,实现跨节点调度。
- topology.kubernetes.io/zone:以可用区为故障域,实现跨可用区调度。
- topology.kubernetes.io/region:以区域为故障域,实现跨区域调度(通常用于多集群联邦)。
- 自定义标签:如
rack、datacenter,用于物理机架或数据中心级别的隔离。
4.3 在 Helm Values 中配置 PodAntiAffinity
在 Helm Chart 中,通常将 PodAntiAffinity 的配置暴露在 values.yaml 中,让用户根据环境灵活调整。以下是一个标准的设计模式:
# values.yaml
podAntiAffinity:
# 可选值: required, preferred, disabled
type: preferred
# 软性反亲和的权重,仅 type=preferred 时生效
weight: 80
# 故障域标签键
topologyKey: "topology.kubernetes.io/zone"
# 标签选择器,默认匹配 app.kubernetes.io/name
labelSelector:
matchLabels:
app.kubernetes.io/name: "{{ include \"myapp.name\" . }}"
在 deployment.yaml 模板中,通过条件判断生成对应的 affinity 配置:
{{- if ne .Values.podAntiAffinity.type "disabled" }}
affinity:
podAntiAffinity:
{{- if eq .Values.podAntiAffinity.type "required" }}
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: {{ .Values.podAntiAffinity.topologyKey }}
labelSelector:
{{- toYaml .Values.podAntiAffinity.labelSelector | nindent 10 }}
{{- else if eq .Values.podAntiAffinity.type "preferred" }}
preferredDuringSchedulingIgnoredDuringExecution:
- weight: {{ .Values.podAntiAffinity.weight }}
podAffinityTerm:
topologyKey: {{ .Values.podAntiAffinity.topologyKey }}
labelSelector:
{{- toYaml .Values.podAntiAffinity.labelSelector | nindent 12 }}
{{- end }}
{{- end }}
4.4 跨节点与跨可用区调度的 SVG 对比
下图展示了硬性反亲和与软性反亲和分别在跨节点(hostname)和跨可用区(zone)场景下的调度效果:
4.5 实战配置示例:跨可用区硬性反亲和
以下是一个完整的 Helm Values 配置,实现 3 副本的跨可用区硬性反亲和:
# values.yaml
replicaCount: 3
podAntiAffinity:
type: required
topologyKey: "topology.kubernetes.io/zone"
labelSelector:
matchLabels:
app.kubernetes.io/name: "myapp"
app.kubernetes.io/instance: "myapp-prod"
对应的调度结果:
- Pod-1 调度到 可用区 A
- Pod-2 调度到 可用区 B
- Pod-3 调度到 可用区 C
- 如果集群只有 2 个可用区,Pod-3 将无法调度(Pending)
4.6 软性反亲和 + 权重调优
当集群资源有限时,可以使用软性反亲和配合权重,在“尽量分散”和“调度成功”之间取得平衡:
# values.yaml
podAntiAffinity:
type: preferred
weight: 90
topologyKey: "topology.kubernetes.io/zone"
labelSelector:
matchLabels:
app.kubernetes.io/name: "myapp"
调度器会优先将 Pod 分散到不同可用区,但如果某个可用区资源不足,允许将 Pod 调度到已有副本的可用区。权重 90 表示这是一个非常强的倾向,但不会阻塞调度。
| 场景 | 硬性反亲和 | 软性反亲和(weight=90) |
|---|---|---|
| 3 副本,3 可用区 | 每个可用区 1 个 Pod | 每个可用区 1 个 Pod |
| 3 副本,2 可用区 | 1 个 Pod Pending | 2 个 Pod 在 A,1 个在 B |
| 5 副本,3 可用区 | 2 个 Pod Pending | 尽量均匀分布,可能 2-2-1 |
4.7 多 topologyKey 组合策略
在大型集群中,可能需要同时考虑节点和可用区两个故障域。Kubernetes 支持在同一个 affinity 中配置多个 topologyKey,但需要注意:
- 硬性反亲和:每个 topologyKey 独立生效,Pod 必须同时满足所有约束。
- 软性反亲和:每个 topologyKey 独立计算权重,调度器综合评分。
示例:先跨可用区,再跨节点
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: "topology.kubernetes.io/zone"
labelSelector:
matchLabels:
app: myapp
- topologyKey: "kubernetes.io/hostname"
labelSelector:
matchLabels:
app: myapp
这种配置确保每个副本不仅在不同可用区,而且在同一可用区内也分布在不同的节点上,实现双重故障域隔离。