深色模式
拓扑分布约束 TopologySpread
摘要:本文面向生产 SRE / 平台工程师,系统讲解
topologySpreadConstraints的字段语义(maxSkew、whenUnsatisfiable、labelSelector、minDomains、matchLabelKeys、nodeAffinityPolicy/nodeTaintsPolicy),说明它为何比 Pod 反亲和更适合跨域均匀打散,并给出集群级默认约束配置。适用 Kubernetes v1.28+(特性自 v1.26 稳定,minDomains自 v1.30 GA)。
适用版本与前提
- Kubernetes:v1.28+(
PodTopologySpread插件自 v1.26 稳定) - 关键字段版本:
minDomains:Beta 自 v1.25/默认开 v1.28,GA 自 v1.30matchLabelKeys:Beta 自 v1.27nodeAffinityPolicy/nodeTaintsPolicy:Beta 自 v1.26([版本相关] 在 v1.26 之前默认关闭或字段不存在)
- 工具:
kubectl explain pod.spec.topologySpreadConstraints - 前提:节点具备稳定的拓扑 label(如
topology.kubernetes.io/zone、kubernetes.io/hostname)
背景与问题
假设 20 节点的集群要跑一个 2–15 副本自动扩缩的工作负载。若只有 2 个副本,我们希望它们不要落在同一节点(单节点故障即全挂);若扩到 15 个,又希望它们均匀分布在多个可用区,以降低延迟与跨区流量成本,并在某区故障时自愈。
Pod 反亲和(podAntiAffinity)只能"阻止同域共存",做不到"均匀打散":6 副本落在 3 个区可能变成 4-1-1,而非期望的 2-2-2。拓扑分布约束正是为"声明式均匀分布"而生,且复杂度远低于反亲和。
核心概念
topologySpreadConstraints 是 Pod spec 下的数组,每个元素定义一条"相对现有 Pod 的均匀分布"规则。字段:
| 字段 | 含义 | 必填 |
|---|---|---|
maxSkew | 域间 Pod 数允许的最大不均匀度(>0) | 是 |
topologyKey | 节点 label key,相同 value 的节点视为同一"域" | 是 |
whenUnsatisfiable | DoNotSchedule(硬)/ ScheduleAnyway(软) | 是 |
labelSelector | 计入分布的 Pod 集合 | 是(通常) |
minDomains | 最小合格域数量(仅 DoNotSchedule) | 否 |
matchLabelKeys | 把 Pod 自身这些 label 的值并入 selector(Beta v1.27) | 否 |
nodeAffinityPolicy | Honor/Ignore:是否仅算满足 node affinity 的节点(Beta v1.26,默认 Honor) | 否 |
nodeTaintsPolicy | Honor/Ignore:是否排除 Pod 不容忍的 taint 节点(Beta v1.26,默认 Ignore) | 否 |
约束唯一性
对同一 topologyKey + whenUnsatisfiable 组合,只能存在一条 constraint。例如已有 hostname + DoNotSchedule,想再加一条 hostname 约束必须用 ScheduleAnyway,否则校验失败。版本相关:该行为与 v1.26+ 一致,旧版请以目标集群 kubectl explain 为准。
架构与原理
maxSkew 语义随 whenUnsatisfiable 不同:
DoNotSchedule:maxSkew是"目标域 Pod 数"与"全局最小值"之间的允许最大差值。若 3 区现有 2/2/1,maxSkew: 1,全局最小值为 1,新增 Pod 不能放进已有 2 个的区(否则该区=3,差值=2>1),只能放进 1 个的区。ScheduleAnyway:不拒绝,但给"有助于降低 skew"的节点更高分。
生产实践一:跨可用区均匀(硬约束)
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 6
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: web
containers:
- name: web
image: nginx:1.271
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
maxSkew: 1 + 6 副本 + 3 区 → 强制 2-2-2 分布。若某区节点全满导致无法保持 skew≤1,新副本会 Pending(硬约束代价),此时应评估是否放宽到 ScheduleAnyway 或扩容该区。
生产实践二:节点级打散 + 跨区软约束
yaml
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: web
- maxSkew: 2
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: web1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
先保证"每节点至多一个"(硬),再尽量跨区均匀(软,不阻塞调度)。
生产实践三:minDomains 保底可用区数
yaml
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
minDomains: 3
labelSelector:
matchLabels:
app: web1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
minDomains: 3 要求至少 3 个合格域存在;若可用区不足 3 个,调度器把"全局最小值"视为 0,Pod 暂不调度(避免全部堆进 1 个区)。注意:minDomains 仅可与 DoNotSchedule 同用,自 v1.30 GA。
生产实践四:集群级默认约束
集群管理员可在 kube-scheduler 配置里设置默认拓扑分布,对所有未显式声明的工作负载生效:
yaml
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
pluginConfig:
- name: PodTopologySpread
args:
defaultConstraints:
- maxSkew: 3
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
defaultNodeInclusionPolicy:
nodeAffinityPolicy: Honor
nodeTaintsPolicy: Ignore1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
故障排查
bash
# 检查节点拓扑 label
kubectl get nodes -L topology.kubernetes.io/zone,kubernetes.io/hostname
# 查看各域 Pod 分布
kubectl get pods -l app=web -o custom-columns=NODE:.spec.nodeName,ZONE:.metadata.labels.'topology\.kubernetes\.io/zone'
# 因约束不满足导致 Pending
kubectl describe pod <pod> | grep -i topology1
2
3
4
5
6
2
3
4
5
6
| 现象 | 根因 | 处理 |
|---|---|---|
| 副本 Pending | DoNotSchedule + maxSkew 无法满足 | 放宽 maxSkew / 改 ScheduleAnyway / 扩容域 |
| 分布不均但能调度 | 用了 ScheduleAnyway | 预期行为,非硬约束 |
minDomains 下不调度 | 合格域 < minDomains | 扩域或减小 minDomains |
回滚与清理
bash
kubectl rollout undo deployment/web
kubectl delete -f web.yaml1
2
2
安全与合规
topologyKey应使用稳定、全员一致的 label。使用临时/易变 label 会导致分布抖动与意外 Pending。nodeAffinityPolicy: Honor默认只把满足 Pod node affinity 的节点计入域,避免把"本就不该去的节点"当作均衡对象;需跨全部节点均匀时设为Ignore。
性能、容量与成本
PodTopologySpread复杂度远低于InterPodAffinity,官方推荐用于大规模集群的跨域均匀。它专精打散,是替代"贵且易 Pending"的 Pod 反亲和的优选。- 跨区均匀能降低跨可用区流量成本与延迟,但
DoNotSchedule过严会牺牲可调度性——在可用区资源紧张的云环境中需权衡。
替代方案与权衡
| 目标 | 推荐 | 不推荐 |
|---|---|---|
| 跨域均匀 | topologySpreadConstraints | podAntiAffinity(不保证均匀、昂贵) |
| 每节点互斥 | 二者皆可(maxSkew:1 + hostname) | 手写脚本 |
| 严格同节点共置 | podAffinity | topologySpread(语义不符) |
FAQ
Q:maxSkew: 1 是否意味着每域 Pod 数完全相同? A:不绝对。maxSkew 限制的是"最大差值"。副本数不能整除域数时,分布如 3 区的 7 副本会是 3-2-2(差值 1),符合 maxSkew: 1。
Q:ScheduleAnyway 会被完全无视吗? A:不会。它在 Score 阶段给更均衡的节点加分,调度器倾向于满足,但资源极度紧张时仍可能落入不均衡节点。
参考资料
- Kubernetes 官方文档 - Pod Topology Spread Constraints,访问日期:2026-10-08。
- Kubernetes 官方文档 - Assigning Pods to Nodes,访问日期:2026-10-08。
- Kubernetes 官方文档 - Scheduler Framework,访问日期:2026-10-08。