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 时的完整决策路径,清晰区分了硬约束的“过滤”阶段与软偏好的“评分”阶段。

节点亲和性调度决策流程 节点亲和性调度决策流程 Pod 调度请求 阶段1:requiredDuringScheduling 过滤 nodeSelectorTerms(OR)→ matchExpressions(AND) 满足? Pod Pending 阶段2:preferredDuringScheduling 计算权重得分(0-100) 综合评分 + 其他优先级 选择最优节点绑定 说明 硬约束不满足 → 直接 Pending 软约束不满足 → 仍可调度 两者可同时配置,互不冲突

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 的设计意图。