一、什么是Pod亲和性与反亲和性?

想象一下你管理着一个幼儿园,有的小朋友喜欢和好朋友坐在一起(亲和性),有的小朋友必须和爱捣蛋的同学分开坐(反亲和性)。Kubernetes调度Pod时也有类似的逻辑。

Pod亲和性(Affinity)让某些Pod倾向于部署在同一节点或区域,比如前端Pod和后端Pod离得近可以降低延迟。反亲和性(Anti-Affinity)则让Pod互相避开,比如把多个Redis实例分散部署避免单点故障。

# 技术栈:Kubernetes v1.24+
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
spec:
  replicas: 3
  template:
    spec:
      affinity:
        podAntiAffinity:  # 反亲和性规则
          requiredDuringSchedulingIgnoredDuringExecution:  # 硬性要求
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-server
            topologyKey: kubernetes.io/hostname  # 按主机名分散

这个例子强制3个web-server的Pod必须分散在不同节点(hostname),就像老师把吵闹的孩子分到不同小组。

二、亲和性调度实战演示

来看个电商系统的典型场景:我们希望商品服务Pod和缓存服务Pod尽量部署在同一个可用区。

# 技术栈:Kubernetes v1.24+
apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-service
spec:
  template:
    spec:
      affinity:
        podAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:  # 软性偏好
          - weight: 80  # 优先级权重
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - redis-cache
              topologyKey: topology.kubernetes.io/zone  # 按可用区调度

这里用preferred表示尽量满足(软策略),如果实在没有符合条件的节点,Pod也能被调度。就像优先安排好朋友坐同桌,但教室座位不够时也可以分开。

三、反亲和性的高级用法

数据库集群需要更严格的反亲和性,我们来看MySQL主从集群的部署案例:

# 技术栈:Kubernetes v1.24+
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql
  replicas: 3  
  template:
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - mysql
            topologyKey: topology.kubernetes.io/region  # 按地域分散
          - labelSelector:
              matchExpressions:
              - key: role
                operator: In
                values:
                - master
            topologyKey: kubernetes.io/hostname  # 主节点独占主机

这个配置实现两个层级的安全防护:

  1. 所有MySQL实例必须跨地域部署(region级隔离)
  2. 主节点必须独占物理主机(hostname级隔离)

四、实际应用中的经验技巧

场景选择:

  • 亲和性适合:微服务调用密集的组件、需要数据本地化的应用
  • 反亲和性适合:有单点故障风险的服务、竞争资源的应用

参数调优:

affinity:
  nodeAffinity:  # 节点亲和性可以组合使用
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: ssd
          operator: Exists
  podAntiAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 100
      podAffinityTerm:
        labelSelector: {...}

常见坑点:

  1. 过度使用required可能导致Pod无法调度,建议先用preferred测试
  2. topologyKey填写错误(比如拼错"hostname")会静默失效
  3. 节点标签缺失时调度器会直接忽略相关规则

五、技术原理深度解析

调度器处理亲和性规则就像玩拼图:

  1. 先通过labelSelector筛选目标Pod
  2. topologyKey检查节点分布情况
  3. required规则一票否决,对preferred规则打分

背后的拓扑域(Topology Domain)概念很重要:

  • hostname:单台物理机级别隔离
  • zone:可用区级别,如AWS的us-east-1a
  • region:地域级别,如华北/华东

六、完整实战案例

部署一个高可用的WordPress应用:

# 技术栈:Kubernetes v1.24+
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
spec:
  replicas: 4
  template:
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - wordpress
            topologyKey: kubernetes.io/hostname
        podAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 90
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - mysql
              topologyKey: topology.kubernetes.io/zone

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql
  replicas: 3
  template:
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - mysql
            topologyKey: topology.kubernetes.io/zone

这个配置实现了:

  1. WordPress实例均匀分布在各个节点
  2. 优先和MySQL部署在同一可用区
  3. MySQL实例在可用区级别实现分散部署

七、技术方案对比选型

与节点亲和性(nodeAffinity)的区别:

  • Pod亲和性关注Pod之间的关系
  • 节点亲和性关注Pod与节点属性的匹配

与污点/容忍(Taint/Toleration)的配合:

tolerations:
- key: "special"
  operator: "Exists"
  effect: "NoSchedule"
affinity:
  nodeAffinity: {...}

这种组合就像:"我可以接受特殊座位(容忍),但最好坐在前三排(亲和性)"

八、最佳实践总结

  1. 生产环境建议

    • 关键服务使用required反亲和性
    • 性能敏感服务使用preferred亲和性
    • 结合nodeAffinity做多维调度
  2. 调试命令

    kubectl describe pod <name> | grep -A 20 Affinity
    kubectl get nodes --show-labels
    
  3. 扩展方向

    • 通过调度器策略调优权重参数
    • 结合自定义调度器实现更复杂的逻辑
    • 使用Pod拓扑分布约束(Topology Spread Constraints)

记住:没有完美的调度策略,只有最适合业务场景的平衡点。就像安排座位表,既要考虑学生个性,也要保证班级整体运转顺畅。