4. 节点选择器与亲和性:nodeSelector、nodeAffinity、节点反亲和性配置

在 Kubernetes 集群中,Pod 默认会被调度到任意满足资源需求的节点上。但在生产环境中,我们常常需要将 Pod 精确绑定到特定节点——例如 GPU 节点、SSD 节点或特定可用区。Kubernetes 提供了从简单到复杂的三种调度约束机制:nodeSelector(节点选择器)、nodeAffinity(节点亲和性)和 Pod 反亲和性(Pod Anti-Affinity)。本章将逐一剖析它们的原理、配置语法与最佳实践。

4.1 nodeSelector:最简单的节点选择

nodeSelector 是 Kubernetes 最原始的节点选择方式,它通过 Pod 规约中的 nodeSelector 字段,要求 Pod 只能调度到包含指定标签的节点上。其工作流程如下:

  1. 运维人员为节点打上标签(如 disktype=ssd、gpu=true)。
  2. 开发人员在 Pod 的 spec.nodeSelector 中声明需要的标签键值对。
  3. 调度器仅将 Pod 分配给标签完全匹配的节点;若无匹配节点,Pod 将处于 Pending 状态。
适用场景: 简单、固定的硬件分区,如“所有 GPU 任务必须跑在 GPU 节点上”。
局限性: 仅支持精确的键值对匹配,不支持“或”“非”“存在”等逻辑表达式。

4.1.1 节点标签操作

为节点添加标签:

kubectl label nodes node-01 disktype=ssd
kubectl label nodes node-02 gpu=true

查看节点标签:

kubectl get nodes --show-labels

4.1.2 Pod 配置示例

apiVersion: v1
kind: Pod
metadata:
  name: ssd-pod
spec:
  nodeSelector:
    disktype: ssd
  containers:
  - name: app
    image: nginx
特性 说明
匹配逻辑 所有键值对必须完全匹配(AND 关系)
支持运算符 仅支持 =(等于)
多标签组合 支持,但必须全部满足
节点无匹配标签 Pod 无法调度,保持 Pending

4.2 nodeAffinity:更灵活的节点亲和性

nodeAffinity 是 nodeSelector 的升级版,属于 调度策略 的一部分,定义在 spec.affinity.nodeAffinity 下。它支持丰富的表达式运算符,并区分两种语义:

  • requiredDuringSchedulingIgnoredDuringExecution:硬约束,调度时必须满足,否则 Pod 无法调度。
  • preferredDuringSchedulingIgnoredDuringExecution:软约束,调度器会尽量满足,但不保证;权重越高,优先级越大。
命名解读: “IgnoredDuringExecution” 表示节点在 Pod 运行期间标签发生变化时,Kubernetes 不会驱逐已运行的 Pod。这是设计上的安全考虑。

4.2.1 常用运算符

运算符 含义 示例
In 标签值属于给定集合 zone in (us-east-1a, us-east-1b)
NotIn 标签值不属于给定集合 zone notin (us-west-2a)
Exists 节点存在该标签键(值不限) gpu exists
DoesNotExist 节点不存在该标签键 spot-instance doesnotexist
Gt / Lt 标签值大于/小于指定数值(需为数值字符串) cpu-core gt 16

4.2.2 硬约束 + 软约束组合示例

apiVersion: v1
kind: Pod
metadata:
  name: affinity-pod
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: topology.kubernetes.io/zone
            operator: In
            values:
            - us-east-1a
            - us-east-1b
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 80
        preference:
          matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd
      - weight: 20
        preference:
          matchExpressions:
          - key: cpu-model
            operator: In
            values:
            - amd-epyc
  containers:
  - name: app
    image: nginx

上述配置的含义:Pod 必须调度到 us-east-1a 或 us-east-1b 可用区;在此前提下,优先选择 disktype=ssd 的节点(权重 80),其次考虑 cpu-model=amd-epyc 的节点(权重 20)。

4.3 节点反亲和性:避免 Pod 聚集

节点反亲和性(Pod Anti-Affinity)与节点亲和性不同,它关注的是 Pod 与 Pod 之间的分布关系,而非 Pod 与节点的关系。它定义在 spec.affinity.podAntiAffinity 下,同样支持 required 和 preferred 两种模式。

典型场景:

  • 将同一应用的多个副本分散到不同节点,提高可用性。
  • 将计算密集型的 Pod 与内存密集型的 Pod 隔离,避免资源争抢。

4.3.1 拓扑键(topologyKey)

反亲和性通过 topologyKey 定义“分散的范围”。最常用的拓扑键:

拓扑键 分散粒度 说明
kubernetes.io/hostname 节点级别 每个节点最多运行一个匹配的 Pod
topology.kubernetes.io/zone 可用区级别 每个可用区最多运行一个匹配的 Pod
topology.kubernetes.io/region 地域级别 每个地域最多运行一个匹配的 Pod

4.3.2 硬反亲和性示例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-anti-affinity
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web
            topologyKey: kubernetes.io/hostname
      containers:
      - name: nginx
        image: nginx

该配置强制要求:每个节点上最多只能运行一个带有 app=web 标签的 Pod。如果集群只有 2 个节点,则 3 个副本中只有 2 个能成功调度,第 3 个将处于 Pending 状态。

注意: 硬反亲和性(required)会显著降低 Pod 的可调度性。在生产环境中,如果副本数大于节点数,建议使用 preferred 软约束,避免 Pod 无法运行。

4.4 三种机制的对比与选择

机制 作用对象 表达能力 调度语义 典型场景
nodeSelector Pod → 节点 仅精确键值对 硬约束 GPU、SSD 等固定硬件分区
nodeAffinity Pod → 节点 丰富运算符 + 权重 硬约束 / 软约束 可用区亲和、机型偏好、成本优化
podAntiAffinity Pod → Pod 标签选择器 + 拓扑键 硬约束 / 软约束 高可用分散、故障域隔离

4.5 调度决策流程(SVG 图)

下图展示了 Kubernetes 调度器在处理 Pod 调度请求时,依次评估 nodeSelector、nodeAffinity 和 podAntiAffinity 的逻辑顺序:

Pod 调度约束评估流程 Pod 调度请求 步骤 1:nodeSelector 匹配 精确键值对检查 步骤 2:nodeAffinity 评估 required(硬约束)→ preferred(软约束 + 权重) 步骤 3:podAntiAffinity 评估 required(硬分散)→ preferred(软分散) 所有约束均满足? 调度成功 → 绑定节点 调度失败 → Pending 评估顺序不可颠倒 nodeSelector → nodeAffinity → podAntiAffinity

4.6 最佳实践与避坑指南

  • 优先使用 nodeAffinity 替代 nodeSelector:nodeAffinity 完全覆盖 nodeSelector 的功能,且表达能力更强。新项目建议直接使用 nodeAffinity。
  • 软约束权重设计:权重范围 1–100,建议按业务重要性分配。例如,SSD 节点权重 80,高内存节点权重 20。
  • 避免硬反亲和性过度约束:当副本数 > 节点数时,硬反亲和性会导致部分 Pod 永远无法调度。此时应改用 preferredDuringScheduling。
  • 结合 topologyKey 实现故障域隔离:使用 topology.kubernetes.io/zone 可将 Pod 分散到不同可用区,提升跨可用区容灾能力。
  • 标签命名规范:建议使用 topology.kubernetes.io/* 或 node.kubernetes.io/* 等标准前缀,避免与自定义标签冲突。
生产建议: 在 Deployment 中同时使用 nodeAffinity(选择高性能节点)和 podAntiAffinity(分散副本),可以实现“好节点 + 高可用”的双重目标。但务必在测试环境验证调度行为,避免因约束冲突导致 Pod 大面积 Pending。