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 同时配置时,调度器按以下优先级处理:

  1. 硬约束(required):所有 requiredDuringScheduling 规则必须同时满足,否则 Pod 处于 Pending 状态。
  2. 软约束(preferred/weight):在满足硬约束的节点中,按权重计算得分。
  3. TopologySpreadConstraints:在满足上述约束的节点中,进一步按 skew 打分,优先选择使分布更均衡的节点。
最佳实践:在多租户发布窗口隔离场景中,建议将 PodAntiAffinity(硬约束)用于租户级隔离,TopologySpreadConstraints(硬约束)用于全局均衡,PodAffinity(软约束)用于性能敏感型工作负载的局部亲和。

3.5 高级调度策略对比总结

维度 PodAffinity PodAntiAffinity TopologySpreadConstraints
匹配方式 标签选择器 标签选择器 标签选择器 + 计数
拓扑域维度 任意 topologyKey 任意 topologyKey 任意 topologyKey
硬/软约束 两者均支持 两者均支持 两者均支持
适用规模 中小规模(标签匹配开销) 中小规模 大规模(O(n) 计数)
典型用途 缓存亲和、服务就近 故障域隔离、租户隔离 跨可用区均衡、发布窗口流量平滑

3.6 高级调度策略协同工作流

高级调度策略协同工作流 高级调度策略协同工作流 新 Pod 创建请求 阶段1:硬约束检查 required PodAffinity / PodAntiAffinity 阶段2:软约束评分 preferred PodAffinity / PodAntiAffinity 阶段3:拓扑分布均衡 TopologySpreadConstraints 计算 skew 并打分 选定最优节点 Pod 调度成功 说明 • 硬约束不满足 → Pending • 软约束 + 均衡综合打分 • 最终选择得分最高节点

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。