主题
Kubernetes Pod 亲和性与反亲和性
结论
Kubernetes 调度规则可以先按两个问题理解:
- 希望靠近,还是希望避开?
- 靠近:亲和性(Affinity)
- 避开:反亲和性(Anti-Affinity)
- 必须满足,还是尽量满足?
- 必须满足:硬规则(
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: nginxrequiredDuringSchedulingIgnoredDuringExecution 可以拆成:
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=backendPod 的同一个可用区。
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=webPod 放在同一个节点上。
如果集群有两个节点、但副本数为 3:
- 软反亲和:第三个 Pod 仍可调度,但可能和其他副本共用节点;
- 硬反亲和:第三个 Pod 可能保持
Pending。
硬反亲和只应在“绝不能共置”时使用,例如确实要求副本位于不同故障域。
四、硬规则和软规则的区别
| 集群情况 | 硬规则 required | 软规则 preferred |
|---|---|---|
| 存在符合条件的节点 | 调度到符合条件的节点 | 优先调度到符合条件的节点 |
| 没有符合条件的节点 | Pod Pending | 尝试其他节点 |
| 符合条件的节点资源不足 | 可能 Pending | 可能退而求其次 |
记忆方法:
text
required = 没有满足条件的节点,就不调度
preferred = 先尽量满足,做不到也可以调度IgnoredDuringExecution 表示调度完成后,即使节点标签或匹配条件发生变化,Kubernetes 通常不会因为这个变化主动驱逐已经运行的 Pod。亲和性不是永久绑定。
五、nodeSelector 和节点亲和性
简单的等值匹配可以使用:
yaml
nodeSelector:
disk: ssd它适合表达“必须选择 disk=ssd 的节点”。复杂场景使用 nodeAffinity,因为它支持:
InNotInExistsDoesNotExistGtLt- 多组匹配条件
- 软规则权重
可以按下面的原则选择:
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。