跳转到内容

Kubernetes Pod 亲和性与反亲和性

结论

Kubernetes 调度规则可以先按两个问题理解:

  1. 希望靠近,还是希望避开?
    • 靠近:亲和性(Affinity)
    • 避开:反亲和性(Anti-Affinity)
  2. 必须满足,还是尽量满足?
    • 必须满足:硬规则(required
    • 尽量满足:软规则(preferred

常用组合如下:

目标规则
必须去某类节点节点亲和性 + required
优先去某类节点节点亲和性 + preferred
必须和某类 Pod 靠近Pod 亲和性 + required
尽量和某类 Pod 靠近Pod 亲和性 + preferred
必须和同类副本分开Pod 反亲和性 + required
尽量和同类副本分开Pod 反亲和性 + preferred

一、节点亲和性:Pod 选择节点

节点亲和性关注的是:

text
Pod 和 Node 的关系

例如集群节点带有以下标签:

text
node-1: disk=ssd
node-2: disk=hdd

硬节点亲和性

yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-hard
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
              - key: disk
                operator: In
                values:
                  - ssd
  containers:
    - name: app
      image: nginx

requiredDuringSchedulingIgnoredDuringExecution 可以拆成:

text
required                       必须满足
DuringScheduling               调度时检查
IgnoredDuringExecution         运行后条件变化时不主动驱逐

因此,没有 disk=ssd 节点时,Pod 会保持 Pending

软节点亲和性

yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-soft
spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 100
          preference:
            matchExpressions:
              - key: disk
                operator: In
                values:
                  - ssd
  containers:
    - name: app
      image: nginx

这表示优先调度到 SSD 节点;如果 SSD 节点不存在或资源不足,仍允许调度到其他节点。weight 范围为 1-100,数值越大,偏好权重越高。

节点反亲和性

如果 Pod 不应该运行在带有 gpu=amd 标签的节点上,可以写成:

yaml
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
            - key: gpu
              operator: NotIn
              values:
                - amd

不过,如果目标是“必须使用 NVIDIA 节点”,更推荐使用正向匹配:

yaml
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
            - key: gpu
              operator: In
              values:
                - nvidia

正向匹配比只排除某一种节点更安全,避免未来新增其他类型节点后被意外选中。


二、Pod 亲和性:让 Pod 靠近其他 Pod

Pod 亲和性关注的是:

text
Pod 和 Pod 的关系

例如,frontend 希望和 backend 处于同一个可用区:

yaml
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchExpressions:
              - key: app
                operator: In
                values:
                  - backend
          topologyKey: topology.kubernetes.io/zone
  containers:
    - name: frontend
      image: nginx

含义是:

frontend 必须调度到存在 app=backend Pod 的同一个可用区。

topologyKey 决定“靠近”的范围:

yaml
topologyKey: topology.kubernetes.io/zone

表示同一个可用区;

yaml
topologyKey: kubernetes.io/hostname

表示同一个节点。

Pod 亲和性适合缓存、前后端低延迟通信等场景,但不要默认使用硬亲和性。匹配的目标 Pod 不存在时,硬规则会让当前 Pod 无法调度。


三、Pod 反亲和性:让副本分散

这是生产环境中最常见的用途:Deployment 有多个副本时,尽量不要把它们放在同一个节点。

软 Pod 反亲和性

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                    - key: app
                      operator: In
                      values:
                        - web
                topologyKey: kubernetes.io/hostname
      containers:
        - name: web
          image: nginx

含义是:

尽量不要把多个 app=web Pod 放在同一个节点上。

如果集群有两个节点、但副本数为 3:

  • 软反亲和:第三个 Pod 仍可调度,但可能和其他副本共用节点;
  • 硬反亲和:第三个 Pod 可能保持 Pending

硬反亲和只应在“绝不能共置”时使用,例如确实要求副本位于不同故障域。


四、硬规则和软规则的区别

集群情况硬规则 required软规则 preferred
存在符合条件的节点调度到符合条件的节点优先调度到符合条件的节点
没有符合条件的节点Pod Pending尝试其他节点
符合条件的节点资源不足可能 Pending可能退而求其次

记忆方法:

text
required   = 没有满足条件的节点,就不调度
preferred  = 先尽量满足,做不到也可以调度

IgnoredDuringExecution 表示调度完成后,即使节点标签或匹配条件发生变化,Kubernetes 通常不会因为这个变化主动驱逐已经运行的 Pod。亲和性不是永久绑定。


五、nodeSelector 和节点亲和性

简单的等值匹配可以使用:

yaml
nodeSelector:
  disk: ssd

它适合表达“必须选择 disk=ssd 的节点”。复杂场景使用 nodeAffinity,因为它支持:

  • In
  • NotIn
  • Exists
  • DoesNotExist
  • Gt
  • Lt
  • 多组匹配条件
  • 软规则权重

可以按下面的原则选择:

text
简单等值匹配       nodeSelector
复杂条件或软亲和   nodeAffinity

六、GPU 服务的典型组合

GPU Pod 通常需要“必须去正确的节点”,同时“尽量把副本分散”:

text
硬节点亲和性:保证 Pod 进入 GPU 节点
软 Pod 反亲和性:尽量分散副本
GPU 资源请求:保证实际申请对应的扩展资源

例如只允许调度到 NVIDIA 节点:

yaml
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
            - key: accelerator
              operator: In
              values:
                - nvidia

注意:节点亲和性只负责筛选节点,不能代替 GPU 资源请求、设备插件或运行时配置。实际 GPU 工作负载还需要检查节点标签、capacity/allocatable 扩展资源、设备插件以及容器运行时是否一致。


七、调度排障

Pod 调度失败时,先看状态和事件:

bash
kubectl get pod <pod-name> -o wide
kubectl describe pod <pod-name>

重点看 Events 中的 FailedScheduling,常见原因包括:

  • 节点没有匹配的标签;
  • 节点存在 taint,但 Pod 没有对应 toleration;
  • 硬亲和性条件无法满足;
  • Pod 反亲和性阻止了共置;
  • CPU、内存或 GPU 等资源不足;
  • topologyKey 使用了节点上不存在的标签;
  • 节点被设置为不可调度。

检查节点标签:

bash
kubectl get nodes --show-labels

检查某个节点的详细信息:

bash
kubectl describe node <node-name>

调度问题不要只看 YAML。最终是否能运行,取决于:

text
节点标签 + 污点/容忍 + 资源可分配量 + Pod 亲和规则 + 调度器事件

八、实际选择建议

需求推荐做法
Pod 必须运行在 GPU 节点硬节点亲和性或 nodeSelector
Pod 优先运行在 SSD 节点软节点亲和性
前后端尽量靠近软 Pod 亲和性
多副本尽量跨节点软 Pod 反亲和性
多副本绝不能同节点硬 Pod 反亲和性,但要确认节点数足够
专用节点不允许普通业务进入污点 + 容忍,必要时再配合亲和性

推荐默认策略

大多数业务优先采用:

text
硬节点亲和性:保证业务去正确的节点
软 Pod 反亲和性:尽量提高副本分布和容灾能力

不要一开始大量使用硬反亲和性。副本数超过可用节点数时,硬反亲和性很容易造成部分 Pod 长期 Pending

参考

基于 MIT 许可发布