NodeSelector 与 NodeAffinity:节点选择器与节点亲和性配置

在 Kubernetes 集群中,Pod 调度到哪个节点并非完全随机。当集群包含异构节点(如不同 CPU 架构、GPU 机型、磁盘类型或网络区域)时,必须通过节点选择机制将工作负载精确分配到合适的节点。Kubernetes 提供了两种核心机制:NodeSelector(简单标签匹配)和 NodeAffinity(丰富表达式匹配),后者又分为硬亲和(required)与软亲和(preferred)。

1. NodeSelector:最简单的节点选择

NodeSelector 是 Pod 规范中的一个字段,通过 spec.nodeSelector 定义。它要求节点必须包含所有指定的标签键值对,才能调度该 Pod。所有条件之间是逻辑 AND 关系。

适用场景:快速将 Pod 绑定到具有特定标签的节点,例如 disktype=ssd、gpu=true、region=us-east-1。适合简单、静态的调度需求。
apiVersion: v1
kind: Pod
metadata:
  name: ssd-pod
spec:
  nodeSelector:
    disktype: ssd
  containers:
  - name: app
    image: nginx

如果集群中没有节点包含 disktype=ssd 标签,该 Pod 将一直处于 Pending 状态。NodeSelector 不支持取反、集合运算或权重,表达能力有限。

2. NodeAffinity:更强大的节点亲和性

NodeAffinity 是 nodeSelector 的进化版,属于调度器的高级特性。它在 Pod 的 spec.affinity.nodeAffinity 字段中定义,支持丰富的匹配表达式(In、NotIn、Exists、DoesNotExist、Gt、Lt),并且区分两种模式:requiredDuringSchedulingIgnoredDuringExecution(硬亲和)和 preferredDuringSchedulingIgnoredDuringExecution(软亲和)。

特性 NodeSelector NodeAffinity(硬亲和) NodeAffinity(软亲和)
匹配方式 精确键值对(AND) In、NotIn、Exists、DoesNotExist、Gt、Lt 同左,且支持权重
调度约束 必须满足所有条件 必须满足所有条件 尽量满足,不满足也可调度
运行时变更 不生效 不生效(IgnoredDuringExecution) 不生效(IgnoredDuringExecution)
权重/优先级 不支持 不支持 支持 weight(1-100)
适用场景 简单标签绑定 强制分区、合规、硬件要求 性能优化、负载均衡、成本控制

3. 硬亲和(required):强制调度约束

硬亲和要求节点必须满足所有 matchExpressions 条件,否则 Pod 无法被调度。其行为与 NodeSelector 类似,但表达能力更强。

apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: gpu-type
            operator: In
            values:
            - nvidia-tesla
            - nvidia-a100
          - key: region
            operator: In
            values:
            - us-west-2
  containers:
  - name: cuda-app
    image: nvidia/cuda:12.0-base

上述配置要求节点必须同时满足 gpu-type 为 nvidia-tesla 或 nvidia-a100,且 region 为 us-west-2。多个 matchExpressions 之间是 AND 关系;多个 nodeSelectorTerms 之间是 OR 关系。

注意:硬亲和条件过于严格可能导致 Pod 无法调度。建议在生产环境中为关键工作负载预留至少一个满足条件的节点,或结合污点/容忍度使用。

4. 软亲和(preferred):优先级调度

软亲和允许调度器将 Pod 调度到不满足条件的节点上,但会优先选择满足条件的节点。每个软亲和规则可以设置 weight(1-100),调度器会计算所有满足条件的节点的总分,选择得分最高的节点。

apiVersion: v1
kind: Pod
metadata:
  name: performance-pod
spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 80
        preference:
          matchExpressions:
          - key: disktype
            operator: In
            values:
            - nvme
      - weight: 20
        preference:
          matchExpressions:
          - key: zone
            operator: In
            values:
            - zone-a
  containers:
  - name: app
    image: nginx

调度器会优先选择 disktype=nvme 的节点(权重 80),其次选择 zone=zone-a 的节点(权重 20)。如果两个条件都满足,得分累加为 100。如果都不满足,Pod 仍可调度到其他节点。

5. 硬亲和与软亲和对比

对比维度 硬亲和(required) 软亲和(preferred)
调度失败行为 Pod 保持 Pending Pod 调度到其他节点
权重支持 不支持 支持(1-100)
典型用途 合规、安全、硬件强制要求 性能优化、成本控制、负载均衡
多条件组合 AND/OR 严格匹配 加权评分,灵活选择
运维风险 节点故障可能导致 Pod 无法调度 低风险,调度器自动降级

6. 节点选择与亲和性工作流

下图展示了从 Pod 创建到节点选择的完整调度流程,以及 NodeSelector 和 NodeAffinity 在其中的作用位置。

Pod 节点选择与亲和性调度流程 Pod 创建 检查 nodeSelector(简单标签匹配) 检查硬亲和(required) 检查软亲和(preferred)加权评分 调度到最优节点 调度顺序 1. nodeSelector 2. 硬亲和(必须满足) 3. 软亲和(加权评分)

7. 组合使用的最佳实践

在实际生产环境中,NodeSelector 和 NodeAffinity 经常组合使用,以实现精细化的调度策略。

  • 基础隔离 + 性能优化:使用 NodeSelector 将敏感工作负载限制在特定节点池(如 security-tier=high),同时使用软亲和优先选择 NVMe 磁盘节点。
  • 多区域部署:硬亲和要求 Pod 必须调度到 region=us-east 的节点,软亲和进一步优先选择 zone=a 或 zone=b。
  • GPU 工作负载:硬亲和要求 gpu=true,软亲和优先选择 gpu-model=a100 的节点。
  • 避免单点故障:软亲和可以配合 PodAntiAffinity 使用,将同一应用的多个副本分散到不同节点。
建议:在 GitOps 工作流中,将节点亲和性配置与 Helm values 或 Kustomize overlay 结合,实现环境级别的调度策略管理。例如,生产环境使用硬亲和,开发环境使用软亲和。

8. 常见问题与避坑

问题 原因 解决方案
Pod 一直 Pending 硬亲和条件没有节点满足 检查节点标签;使用 kubectl describe node 查看标签;考虑添加软亲和作为降级
软亲和未生效 权重设置过低,或所有节点都不满足 提高权重;检查标签是否一致;使用 kubectl get nodes --show-labels 验证
标签拼写错误 键或值大小写、空格不一致 使用 kubectl label 统一管理标签;通过 CI/CD 自动化打标
忽略运行时标签变更 亲和性策略后缀为 IgnoredDuringExecution 如需运行时响应,需自定义调度器或使用 descheduler

NodeSelector 和 NodeAffinity 是 Kubernetes 调度体系的基础组件。掌握硬亲和与软亲和的区别,能够帮助你在不同场景下做出正确的调度决策,从而提升集群资源利用率和应用性能。