3、高级调度策略:PodAffinity、PodAntiAffinity、TopologySpreadConstraints
在多租户与 GitOps 场景下,Pod 的放置位置直接影响故障域隔离、发布窗口内的流量冲击以及资源利用率。Kubernetes 提供了三种高级调度约束——PodAffinity、PodAntiAffinity 和 TopologySpreadConstraints,它们允许调度器根据已有 Pod 的标签和拓扑分布做出智能决策。
3.1 核心概念与适用场景
| 策略类型 | 调度语义 | 典型场景 |
|---|---|---|
| PodAffinity | 将新 Pod 调度到与满足条件的已有 Pod 相同的拓扑域中 | 缓存亲和、服务网格 Sidecar 同节点部署、有状态应用主从同可用区 |
| PodAntiAffinity | 将新 Pod 调度到与满足条件的已有 Pod 不同的拓扑域中 | 多副本分散故障域、租户工作负载隔离、避免单点过载 |
| TopologySpreadConstraints | 在多个拓扑域之间均匀分布 Pod 副本 | 全局负载均衡、跨可用区高可用、发布窗口内渐进式扩容 |
关键区别:PodAffinity/AntiAffinity 是基于匹配标签的硬性或软性约束;TopologySpreadConstraints 是基于计数的分布约束,不依赖 Pod 标签匹配,更适合大规模集群的自动均衡。
3.2 PodAffinity 与 PodAntiAffinity 深度解析
两种策略均通过 spec.affinity 字段配置,支持 requiredDuringSchedulingIgnoredDuringExecution(硬约束)和 preferredDuringSchedulingIgnoredDuringExecution(软约束)两种模式。
3.2.1 配置结构
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- cache
topologyKey: topology.kubernetes.io/zone
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
tenant: alpha
topologyKey: kubernetes.io/hostname
- labelSelector:定义目标 Pod 的标签匹配规则。
- topologyKey:指定拓扑域的维度(如
kubernetes.io/hostname、topology.kubernetes.io/zone)。 - operator:支持
In、NotIn、Exists、DoesNotExist。 - weight:仅用于软约束,取值范围 1–100,权重越高调度器越优先满足。
3.2.2 多租户隔离实战:PodAntiAffinity 硬约束
在 GitOps 多租户环境中,不同租户的工作负载必须运行在不同节点上,防止资源争抢和故障扩散。
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-tenant-blue
namespace: tenant-blue
spec:
replicas: 3
template:
metadata:
labels:
tenant: blue
app: frontend
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: tenant
operator: In
values:
- blue
topologyKey: kubernetes.io/hostname
containers:
- name: app
image: nginx:1.25
效果:同一租户(blue)的 3 个副本被强制调度到 3 个不同节点上;不同租户(如 red、green)的 Pod 不受此约束影响,但可通过额外 AntiAffinity 规则实现跨租户节点隔离。
3.3 TopologySpreadConstraints:全局均衡利器
TopologySpreadConstraints 允许你定义 Pod 在多个拓扑域中的最大分布偏差(maxSkew),适用于跨可用区、跨节点组的均匀分布。
3.3.1 配置示例
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: web
- maxSkew:允许的最大 Pod 数量差异。值为 1 表示任意两个可用区之间的 Pod 数差不超过 1。
- whenUnsatisfiable:
DoNotSchedule(硬约束)或ScheduleAnyway(软约束)。 - topologyKey:与 Affinity 相同的拓扑维度。
3.3.2 发布窗口内的渐进式扩容
在 GitOps 同步窗口期间,新版本 Deployment 的副本数从 1 逐步增加到 10。使用 TopologySpreadConstraints 确保每个可用区内的 Pod 数量始终均衡,避免单个可用区流量过载。
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: web
version: v2
注意:TopologySpreadConstraints 的
labelSelector 必须与 Pod 模板中的标签匹配;如果使用 matchLabels 且 Deployment 的 replicas 在发布过程中变化,调度器会实时重新计算 skew,确保新 Pod 被调度到最缺副本的拓扑域。
3.4 三种策略的协同与冲突处理
当 PodAffinity、PodAntiAffinity 和 TopologySpreadConstraints 同时配置时,调度器按以下优先级处理:
- 硬约束(required):所有
requiredDuringScheduling规则必须同时满足,否则 Pod 处于 Pending 状态。 - 软约束(preferred/weight):在满足硬约束的节点中,按权重计算得分。
- TopologySpreadConstraints:在满足上述约束的节点中,进一步按 skew 打分,优先选择使分布更均衡的节点。
最佳实践:在多租户发布窗口隔离场景中,建议将 PodAntiAffinity(硬约束)用于租户级隔离,TopologySpreadConstraints(硬约束)用于全局均衡,PodAffinity(软约束)用于性能敏感型工作负载的局部亲和。
3.5 高级调度策略对比总结
| 维度 | PodAffinity | PodAntiAffinity | TopologySpreadConstraints |
|---|---|---|---|
| 匹配方式 | 标签选择器 | 标签选择器 | 标签选择器 + 计数 |
| 拓扑域维度 | 任意 topologyKey | 任意 topologyKey | 任意 topologyKey |
| 硬/软约束 | 两者均支持 | 两者均支持 | 两者均支持 |
| 适用规模 | 中小规模(标签匹配开销) | 中小规模 | 大规模(O(n) 计数) |
| 典型用途 | 缓存亲和、服务就近 | 故障域隔离、租户隔离 | 跨可用区均衡、发布窗口流量平滑 |
3.6 高级调度策略协同工作流
3.7 常见陷阱与避坑指南
- topologyKey 选择不当:使用
kubernetes.io/hostname作为 AntiAffinity 的拓扑键时,如果集群节点数少于副本数,部分 Pod 将无法调度。建议结合topology.kubernetes.io/zone或自定义节点标签。 - 软约束权重过高:多个软约束的权重之和可能超过硬约束的优先级,导致调度器做出反直觉的决策。建议软约束权重总和不超过 100。
- TopologySpreadConstraints 与 ReplicaSet 冲突:当
maxSkew设置为 1 且whenUnsatisfiable: DoNotSchedule时,如果集群拓扑域数量少于副本数,调度器会拒绝调度。此时应使用ScheduleAnyway或增大maxSkew。 - 标签选择器遗漏:PodAffinity/AntiAffinity 的
labelSelector必须匹配目标 Pod 的标签,否则规则不生效。建议在 Deployment 的spec.template.metadata.labels中显式声明所有匹配标签。
生产建议:在 GitOps 多租户集群中,将 PodAntiAffinity 硬约束与 TopologySpreadConstraints 硬约束组合使用,前者保证租户级故障域隔离,后者保证全局负载均衡。软约束仅用于性能优化场景,且权重不宜超过 50。