4. 节点亲和性(Node Affinity):requiredDuringSchedulingIgnoredDuringExecution 与 preferredDuringSchedulingIgnoredDuringExecution 规则详解
节点亲和性(Node Affinity)是 Kubernetes 调度器将 Pod 绑定到特定节点集合的核心策略。与简单的 nodeSelector 相比,Node Affinity 提供了更丰富的匹配表达式(In、NotIn、Exists、DoesNotExist、Gt、Lt)以及两种截然不同的执行语义:硬性要求(required)与软性偏好(preferred)。
4.1 两种执行语义:硬约束与软偏好
Kubernetes 通过 requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution 两个字段区分调度阶段的强制性与倾向性规则。它们的核心差异如下表所示:
| 特性 | requiredDuringSchedulingIgnoredDuringExecution | preferredDuringSchedulingIgnoredDuringExecution |
|---|---|---|
| 调度阶段行为 | Pod 必须满足所有匹配规则,否则无法调度 | 调度器尽力满足规则,但不保证;不满足时仍可调度到其他节点 |
| 执行阶段行为 | 节点标签变更后,已运行的 Pod 不受影响(IgnoredDuringExecution) | 节点标签变更后,已运行的 Pod 不受影响(IgnoredDuringExecution) |
| 权重支持 | 不支持权重,所有规则必须同时满足 | 支持权重(weight,1–100),调度器按加权评分选择最优节点 |
| 典型场景 | GPU 节点、合规区域、专用硬件 | 性能优先、成本优化、可用区均衡 |
| 失败后果 | Pod 处于 Pending 状态,无法调度 | Pod 仍可调度到不匹配的节点,评分较低 |
4.2 requiredDuringSchedulingIgnoredDuringExecution:硬性亲和规则
该规则使用 nodeSelectorTerms 定义一组匹配条件。多个 nodeSelectorTerms 之间是 OR 关系(满足任一 Term 即可);每个 Term 内的多个 matchExpressions 之间是 AND 关系(必须全部满足)。
apiVersion: v1
kind: Pod
metadata:
name: gpu-inference-pod
spec:
containers:
- name: inference
image: nvidia/cuda:12.0-runtime
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: accelerator
operator: In
values:
- nvidia-tesla-v100
- nvidia-a100
- key: topology.kubernetes.io/zone
operator: In
values:
- us-east-1a
上述配置的含义:Pod 必须调度到同时满足 accelerator 为 V100 或 A100 且 可用区为 us-east-1a 的节点上。如果集群中没有这样的节点,Pod 将一直处于 Pending 状态。
4.2.1 操作符详解
| 操作符 | 行为 | 示例 |
|---|---|---|
In |
标签值在给定的 values 列表中 | key: gpu, operator: In, values: [true] |
NotIn |
标签值不在给定的 values 列表中 | key: disktype, operator: NotIn, values: [hdd] |
Exists |
节点上存在该标签键(不关心值) | key: nvidia.com/gpu, operator: Exists |
DoesNotExist |
节点上不存在该标签键 | key: spot-instance, operator: DoesNotExist |
Gt |
标签值(数值字符串)大于给定值 | key: memory-gb, operator: Gt, values: [128] |
Lt |
标签值(数值字符串)小于给定值 | key: cpu-cores, operator: Lt, values: [8] |
Gt 和 Lt 操作符要求标签值必须是可以解析为数字的字符串。如果标签值是非数字字符串(如 large),调度器会忽略该表达式,可能导致意外的调度行为。
4.3 preferredDuringSchedulingIgnoredDuringExecution:软性偏好规则
软性规则通过 preferences 字段定义,每个偏好项包含一个 weight(1–100)和一组 matchExpressions。调度器会计算所有可调度节点的加权总分,选择得分最高的节点。
apiVersion: v1
kind: Pod
metadata:
name: web-frontend
spec:
containers:
- name: nginx
image: nginx:1.25
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- us-east-1a
- weight: 20
preference:
matchExpressions:
- key: node-type
operator: In
values:
- ssd-optimized
在这个例子中,调度器优先选择 us-east-1a 可用区的节点(权重 80),如果同时满足 ssd-optimized 标签则额外加分(权重 20)。即使没有任何节点满足这些条件,Pod 仍然可以调度到集群中的任意节点。
4.3.1 评分机制
调度器对每个可调度节点执行以下评分流程:
- 遍历所有偏好项:对每个
preference,检查节点是否满足其matchExpressions。 - 累加权重:如果节点满足该偏好,将对应的
weight加入总分。 - 归一化:最终得分 = 总分 / 所有偏好项权重之和 × 100(百分比形式)。
- 选择最高分节点:得分最高的节点被选中;如果多个节点得分相同,调度器按其他策略(如 LeastRequestedPriority)进一步筛选。
4.4 节点亲和性调度流程
下图展示了调度器在处理 Node Affinity 时的完整决策路径:
4.5 组合使用:硬约束 + 软偏好的典型模式
生产环境中,通常将 requiredDuringScheduling 和 preferredDuringScheduling 组合使用,形成"底线保障 + 优化选择"的调度策略:
apiVersion: apps/v1
kind: Deployment
metadata:
name: ml-training
spec:
replicas: 3
selector:
matchLabels:
app: ml-training
template:
metadata:
labels:
app: ml-training
spec:
containers:
- name: trainer
image: pytorch/pytorch:2.0-cuda12
resources:
requests:
nvidia.com/gpu: 1
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: nvidia.com/gpu.present
operator: Exists
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 90
preference:
matchExpressions:
- key: topology.kubernetes.io/zone
operator: In
values:
- us-east-1a
- us-east-1b
- weight: 10
preference:
matchExpressions:
- key: node-type
operator: NotIn
values:
- spot-instance
- 硬约束:Pod 必须调度到有 GPU 的节点上(
nvidia.com/gpu.present: Exists),这是运行训练任务的前提条件。 - 软偏好:优先选择
us-east-1a或us-east-1b可用区(权重 90),同时尽量避免 spot 实例(权重 10)。如果所有 GPU 节点都在us-east-1c,Pod 仍然可以调度过去,只是评分较低。
4.6 常见陷阱与避坑指南
- 标签不存在导致调度失败:使用
In操作符时,如果节点上根本没有该标签键,表达式视为不匹配。建议先用Exists检查标签存在性,再用In筛选具体值。 - 权重值超出范围:
weight必须在 1–100 之间。设置为 0 会被调度器忽略,设置为大于 100 会导致 Pod 创建失败。 - 多个 Term 的 OR 语义误用:
nodeSelectorTerms列表中的每个 Term 是 OR 关系,容易误写成 AND。如果需要 AND 语义,将所有表达式放在同一个 Term 内。 - 忽略执行阶段的影响:即使节点标签在 Pod 运行后发生变化,已运行的 Pod 也不会被重新调度。如果需要动态响应标签变化,需结合 Pod 拓扑分布约束或自定义控制器。
- 软偏好权重过于平均:多个偏好项的权重差距过小(如 50、55、45),调度器难以形成有效优先级,建议至少保持 2 倍以上的权重差距。
kubectl describe pod <pod-name> 查看 Events 字段,可以获取调度器对 Node Affinity 规则的匹配结果。如果 Pod 处于 Pending 状态,Events 中会明确显示 "0/3 nodes are available: 1 node(s) didn't match node selector, 2 node(s) didn't match Pod's node affinity" 等详细信息。