4. 节点亲和性(nodeAffinity):requiredDuringScheduling 与 preferredDuringScheduling 的配置与示例
节点亲和性(nodeAffinity)是 Kubernetes 调度器将 Pod 绑定到特定节点集合的核心策略之一。与简单的 nodeSelector 相比,nodeAffinity 提供了更丰富的匹配表达式(In、NotIn、Exists、DoesNotExist、Gt、Lt)以及两种截然不同的调度语义:硬约束(required) 和 软偏好(preferred)。理解这两种模式的差异与协同,是精细化控制 Pod 分布的前提。
4.1 两种调度语义对比
| 特性 | requiredDuringSchedulingIgnoredDuringExecution | preferredDuringSchedulingIgnoredDuringExecution |
|---|---|---|
| 调度阶段行为 | 必须满足所有匹配规则,否则 Pod 无法被调度(Pending) | 调度器尝试满足规则,但不强制;若不满足,仍可调度到其他节点 |
| 执行阶段行为 | 节点标签变更后,已运行的 Pod 不受影响(Ignored) | 节点标签变更后,已运行的 Pod 不受影响(Ignored) |
| 权重支持 | 不支持权重,所有规则必须同时满足 | 支持权重(weight 1-100),调度器按加权和排序节点 |
| 典型场景 | GPU 节点、合规节点、专用硬件节点 | 可用区均衡、性能优先、成本优化 |
| 与 nodeSelector 关系 | 功能超集,推荐替代 nodeSelector | nodeSelector 无法实现软偏好 |
命名解析: 两种策略的后缀均为
IgnoredDuringExecution,意味着节点标签在 Pod 运行后发生变化时,调度器不会驱逐已运行的 Pod。Kubernetes 社区正在开发 requiredDuringSchedulingRequiredDuringExecution 作为未来增强,但目前仅上述两种模式可用。
4.2 requiredDuringScheduling 硬约束配置示例
以下示例展示一个需要运行在具有 gpu-type: nvidia-a100 标签且所在可用区为 us-east-1a 或 us-east-1b 的节点上的推理 Pod。任何不满足条件的节点都会被调度器排除。
apiVersion: v1
kind: Pod
metadata:
name: inference-pod
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: gpu-type
operator: In
values:
- nvidia-a100
- key: topology.kubernetes.io/zone
operator: In
values:
- us-east-1a
- us-east-1b
containers:
- name: inference
image: nvidia/cuda:12.0-base
关键点:
nodeSelectorTerms列表项之间是 OR 关系——满足任意一个 term 即可。- 单个 term 内的
matchExpressions列表项之间是 AND 关系——必须全部满足。 - 如果集群中没有节点同时满足
gpu-type=nvidia-a100和zone条件,Pod 将一直处于 Pending 状态。
4.3 preferredDuringScheduling 软偏好配置示例
软偏好适用于希望 Pod 优先调度到高性能节点,但不强制要求的场景。以下示例为 Web 应用设置两个偏好:优先选择 ssd: true 节点(权重 80),其次选择 cpu-fast: yes 节点(权重 20)。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 5
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: ssd
operator: In
values:
- "true"
- weight: 20
preference:
matchExpressions:
- key: cpu-fast
operator: In
values:
- "yes"
containers:
- name: nginx
image: nginx:1.25
权重计算逻辑: 调度器为每个节点计算所有匹配偏好的权重之和,然后加上其他优先级函数(如 Pod 间亲和性、资源可用性)的得分,最终选择总分最高的节点。权重值范围为 1-100,值越大优先级越高。如果所有节点都不满足任何偏好,Pod 仍会被调度到任意可用节点。
4.4 节点亲和性调度决策流程
下图展示了调度器在处理 nodeAffinity 时的完整决策路径,清晰区分了硬约束的“过滤”阶段与软偏好的“评分”阶段。
4.5 操作符详解与组合策略
nodeAffinity 支持六种操作符,灵活覆盖各种标签匹配需求。下表总结了每种操作符的语义与适用场景:
| 操作符 | 语义 | values 字段 | 典型用例 |
|---|---|---|---|
In |
标签值在给定集合中 | 必须提供 | 选择特定可用区、机型 |
NotIn |
标签值不在给定集合中 | 必须提供 | 排除故障节点、特定区域 |
Exists |
节点存在该标签键(值任意) | 不提供 | 检查节点是否打了 GPU 标签 |
DoesNotExist |
节点不存在该标签键 | 不提供 | 排除所有打了特定标签的节点 |
Gt |
标签值(数值)大于给定值 | 必须提供(数值字符串) | 选择内存大于 256GB 的节点 |
Lt |
标签值(数值)小于给定值 | 必须提供(数值字符串) | 选择 CPU 核数小于 32 的节点 |
组合策略最佳实践:
- 硬约束 + 软偏好协同: 先通过 required 排除不兼容节点(如非 GPU 节点),再通过 preferred 在剩余节点中优先选择低负载或近数据中心的节点。
- 多 term 的 OR 用法: 当 Pod 可以接受两种完全不同的节点类型时(如
gpu-type: a100或gpu-type: v100),使用多个nodeSelectorTerms实现逻辑 OR。 - 避免过度约束: 硬约束过多可能导致集群资源利用率下降,建议仅在关键需求(安全、合规、硬件依赖)上使用 required,其余用 preferred 表达倾向。
4.6 实战:混合使用 required 与 preferred
以下完整示例展示了一个批处理作业同时使用两种亲和性策略:硬性要求节点位于 us-east 区域,软性偏好选择具有 fast-network: true 标签且剩余 CPU 较多的节点。
apiVersion: batch/v1
kind: Job
metadata:
name: data-processing
spec:
template:
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: topology.kubernetes.io/region
operator: In
values:
- us-east
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 70
preference:
matchExpressions:
- key: fast-network
operator: In
values:
- "true"
- weight: 30
preference:
matchExpressions:
- key: node-type
operator: In
values:
- compute-optimized
containers:
- name: processor
image: python:3.11
command: ["python", "process.py"]
restartPolicy: Never
验证命令: 使用
kubectl describe pod <pod-name> 查看调度事件,确认调度器是否应用了亲和性规则。在 Events 字段中可以看到类似 Successfully assigned ... to node-xyz 的记录,以及调度器打分详情(需启用 --v=4 日志级别)。
4.7 常见陷阱与避坑指南
- 标签键值大小写敏感: Kubernetes 标签键值对大小写敏感,
GPU-Type与gpu-type被视为不同标签。 - Gt/Lt 操作符仅支持数值: 如果标签值不是纯数字字符串(如
256Gi),Gt/Lt 会解析失败,导致规则不生效。 - 权重值范围严格: weight 必须在 1-100 之间,超出范围会导致 API 校验失败。
- preferred 不保证分布均匀: 如果所有节点都满足某个高权重偏好,Pod 可能集中调度到同一节点,需结合 Pod 反亲和性使用。
- 节点标签变更后的行为: 已运行的 Pod 不会因节点标签变化而被重新调度,这是
IgnoredDuringExecution的设计意图。