2. Node亲和性:nodeSelector、nodeAffinity(required/preferred)配置与实战

Node亲和性是Kubernetes调度器将Pod绑定到特定节点的一组策略,属于调度约束的核心能力。与污点/容忍的“排斥-容忍”逻辑不同,亲和性采用“吸引-匹配”机制,主动引导Pod选择符合条件的节点。本章从最基础的nodeSelector入手,逐步深入到nodeAffinity的强制与偏好规则,并通过实战案例展示配置方法。

2.1 nodeSelector:最简单的节点选择器

nodeSelector是Kubernetes最早提供的节点选择机制,通过在Pod的spec中定义nodeSelector字段,要求节点必须包含指定的标签且值匹配。它只支持“与”逻辑(所有条件必须同时满足),不支持复杂表达式。

核心特点:
  • 语法简单,仅需在Pod spec中设置nodeSelector: { key: value }。
  • 只支持精确匹配(等于),不支持In、NotIn、Exists等操作符。
  • 多个条件之间是AND关系,所有标签必须同时存在于节点上。
  • 如果没有任何节点满足条件,Pod将处于Pending状态。

配置示例:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-selector
spec:
  nodeSelector:
    disktype: ssd
    region: cn-east-1
  containers:
  - name: nginx
    image: nginx:1.25

上述Pod只会被调度到同时具有disktype=ssd和region=cn-east-1标签的节点上。如果集群中没有这样的节点,Pod将一直Pending。

最佳实践:在生产环境中,建议先通过kubectl label node <node-name> disktype=ssd为节点打标签,再创建使用nodeSelector的Pod。避免因标签缺失导致调度失败。

2.2 nodeAffinity:增强型节点亲和性

nodeAffinity是nodeSelector的升级版,属于调度策略的进阶能力。它支持丰富的匹配操作符(In、NotIn、Exists、DoesNotExist、Gt、Lt),并分为两种类型:requiredDuringSchedulingIgnoredDuringExecution(硬亲和)和preferredDuringSchedulingIgnoredDuringExecution(软亲和)。

类型 语义 调度行为 执行阶段
required 硬亲和(必须满足) 节点必须满足所有规则,否则Pod无法调度 调度时强制执行,运行期间忽略节点变化
preferred 软亲和(尽量满足) 调度器优先选择满足规则的节点,不满足时仍可调度到其他节点 调度时尽力满足,运行期间忽略节点变化
注意:两种类型的名称中都包含“IgnoredDuringExecution”,这意味着节点标签在Pod运行期间发生变化时,不会驱逐已运行的Pod。如果需要运行时响应标签变化,需要使用Kubernetes 1.30+的“MatchLabelKeys”等新特性。

2.3 requiredDuringScheduling 实战配置

硬亲和适用于强依赖场景,例如GPU计算节点、特定硬件节点、合规区域节点。以下示例要求Pod必须调度到GPU节点且区域为cn-east:

apiVersion: v1
kind: Pod
metadata:
  name: gpu-inference
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: gpu-type
            operator: In
            values:
            - nvidia-a100
            - nvidia-h100
          - key: topology.kubernetes.io/zone
            operator: In
            values:
            - cn-east-1a
            - cn-east-1b
  containers:
  - name: inference
    image: inference:latest
    resources:
      limits:
        nvidia.com/gpu: 1

关键解析:

  • nodeSelectorTerms:多个term之间是OR关系,满足任意一个term即可。
  • matchExpressions:同一个term内的多个表达式是AND关系,必须全部满足。
  • 操作符In:节点的标签值必须匹配values列表中的任意一个。
  • 如果集群中没有同时满足gpu-type和zone条件的节点,Pod将无法调度。

2.4 preferredDuringScheduling 实战配置

软亲和适用于优化调度场景,例如优先将Pod调度到低延迟区域或高带宽节点,但不强制。调度器会为每个节点计算权重分数,分数越高越优先选择。

apiVersion: v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 5
  template:
    metadata:
      labels:
        app: web
    spec:
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 80
            preference:
              matchExpressions:
              - key: network-tier
                operator: In
                values:
                - low-latency
          - weight: 20
            preference:
              matchExpressions:
              - key: disk-type
                operator: In
                values:
                - nvme-ssd
      containers:
      - name: web
        image: nginx:1.25

权重计算逻辑:

  • 每个preference独立计算,满足条件则累加对应的weight值。
  • 调度器将所有满足条件的preference权重相加,得到节点的总得分。
  • 得分最高的节点被优先选中;如果多个节点得分相同,调度器按默认策略(如资源均衡)选择。
  • 如果没有任何节点满足preference,Pod仍然可以调度到任意节点(权重为0)。
权重设计建议:将核心需求(如低延迟)设为高权重(如80-100),次要需求(如SSD)设为低权重(如10-30)。避免所有preference权重相同,否则调度器无法体现优先级差异。

2.5 nodeSelector 与 nodeAffinity 对比

特性 nodeSelector nodeAffinity (required) nodeAffinity (preferred)
匹配操作符 仅支持等于(=) In, NotIn, Exists, DoesNotExist, Gt, Lt In, NotIn, Exists, DoesNotExist, Gt, Lt
逻辑关系 AND(所有条件必须满足) term间OR,表达式间AND term间OR,表达式间AND
强制程度 强制(不满足则Pending) 强制(不满足则Pending) 偏好(不满足仍可调度)
权重支持 不支持 不支持 支持(weight 1-100)
适用场景 简单标签匹配,快速部署 强依赖硬件/区域/合规 性能优化、成本控制、负载均衡

2.6 亲和性调度流程(SVG图)

下图展示了Kubernetes调度器在处理Node亲和性时的完整决策流程,从Pod创建到最终节点绑定:

Node亲和性调度决策流程 Pod 创建请求 检查 nodeSelector 是否存在? (Pod.spec.nodeSelector) 是 否 nodeSelector 标签匹配 精确等于匹配 跳过 nodeSelector 直接检查 nodeAffinity 检查 nodeAffinity required / preferred required 条件是否满足? (不满足则Pending) 满足 不满足 Pod Pending 调度到目标节点

2.7 实战:组合使用 nodeSelector 与 nodeAffinity

在实际生产环境中,nodeSelector和nodeAffinity可以同时使用,调度器会先检查nodeSelector(所有标签必须匹配),再检查nodeAffinity的required规则。以下是一个综合示例:

apiVersion: v1
kind: Pod
metadata:
  name: hybrid-affinity-pod
spec:
  # 第一层:nodeSelector 精确匹配
  nodeSelector:
    environment: production

  affinity:
    nodeAffinity:
      # 第二层:硬亲和 - 必须属于指定可用区
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: topology.kubernetes.io/zone
            operator: In
            values:
            - cn-east-1a
            - cn-east-1b

      # 第三层:软亲和 - 优先选择高内存节点
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 70
        preference:
          matchExpressions:
          - key: memory-size
            operator: In
            values:
            - large
            - xlarge
      - weight: 30
        preference:
          matchExpressions:
          - key: cpu-arch
            operator: In
            values:
            - amd64

  containers:
  - name: app
    image: app:latest

调度执行顺序:

  1. 调度器首先过滤出所有environment=production的节点(nodeSelector)。
  2. 从这些节点中,进一步筛选出zone为cn-east-1a或cn-east-1b的节点(required硬亲和)。
  3. 对剩余节点,根据memory-size和cpu-arch的权重计算得分,选择最优节点(preferred软亲和)。
关键结论:
  • nodeSelector是第一道过滤,适合做粗粒度的环境/区域隔离。
  • nodeAffinity required是第二道强制过滤,适合做硬件/合规约束。
  • nodeAffinity preferred是最终优化排序,适合做性能/成本调优。
  • 三层策略可以叠加使用,但要注意过度约束可能导致Pod无法调度。

2.8 常见问题与避坑指南

问题 原因 解决方案
Pod一直Pending required条件过于严格,没有节点满足所有标签 使用kubectl describe pod查看调度事件;检查节点标签是否完整
Pod调度到不符合preferred的节点 preferred是软约束,不保证一定满足 如果必须满足,将preferred改为required
nodeSelector和nodeAffinity冲突 两个条件同时使用时,必须同时满足 检查标签交集是否为空;考虑合并到nodeAffinity中
节点标签变更后Pod未迁移 亲和性只在调度时检查,运行期间不重新评估 使用PodDisruptionBudget配合手动驱逐;或升级到支持运行时亲和性的版本
权重设置不合理导致调度倾斜 多个preference权重差异过大或过小 权重差异建议在50以上;避免所有preference权重相同
调试命令速查:
  • kubectl get nodes --show-labels — 查看所有节点标签
  • kubectl describe pod <pod-name> — 查看调度事件和亲和性配置
  • kubectl get events --field-selector involvedObject.name=<pod-name> — 查看调度相关事件
  • kubectl label node <node> <key>=<value> --overwrite — 动态修改节点标签