4. 节点亲和性语法详解:matchExpressions、matchFields 与 operator

节点亲和性(Node Affinity)是 Kubernetes 调度器将 Pod 绑定到特定节点池的核心机制。在滚动升级场景中,通过精确的亲和性规则,可以确保新 Pod 只调度到已升级的节点,从而控制流量迁移粒度。本节深入解析 matchExpressions、matchFields 以及六种 operator 的完整语法与实战用法。

4.1 节点亲和性规则结构

节点亲和性定义在 Pod 的 spec.affinity.nodeAffinity 字段下,包含两种规则类型:

规则类型 字段路径 语义 典型场景
requiredDuringSchedulingIgnoredDuringExecution nodeSelectorTerms[] 硬性约束,调度时必须满足,否则 Pod 无法调度 强制 Pod 运行在特定节点池(如已升级节点)
preferredDuringSchedulingIgnoredDuringExecution preference.matchExpressions 软性偏好,调度器尽量满足,但不强制 优先调度到某节点池,但允许降级到其他节点

两种规则内部都使用 matchExpressions 或 matchFields 定义选择条件。每个条件由 key、operator 和 values(部分 operator 不需要)组成。

4.2 matchExpressions:基于节点标签的选择

matchExpressions 是最常用的选择器,它根据节点的 标签(Label) 进行匹配。节点标签是键值对,例如 node-pool: upgraded、zone: us-east-1。

关键概念: 在滚动升级中,通常会在升级完成后为节点打上标签(如 node-pool-version: v2),然后通过 matchExpressions 让新 Pod 只选择这些节点。

4.2.1 operator 详解

Kubernetes 为 matchExpressions 提供了六种 operator,覆盖了从精确匹配到存在性检查的完整需求:

operator 是否需要 values 匹配逻辑 典型用途
In 是(至少1个) 节点的 label value 在 values 列表中 精确匹配节点池版本、区域
NotIn 是(至少1个) 节点的 label value 不在 values 列表中 排除特定节点(如旧版本节点)
Exists 否 节点存在该 label key(无论 value 是什么) 检查节点是否属于某类(如 GPU 节点)
DoesNotExist 否 节点不存在该 label key 排除具有特定标签的节点
Gt 是(1个数值字符串) 节点的 label value(数值)大于 values 中的值 选择资源充足的节点(如剩余 CPU > 4)
Lt 是(1个数值字符串) 节点的 label value(数值)小于 values 中的值 选择负载较低的节点
注意: Gt 和 Lt 要求 label value 必须是可以解析为数字的字符串(如 "8"),否则调度器会忽略该表达式。对于非数值标签,请使用 In 或 NotIn。

4.2.2 实战代码示例

以下 YAML 展示了一个完整的节点亲和性配置,使用 matchExpressions 实现滚动升级中的流量控制:

apiVersion: v1
kind: Pod
metadata:
  name: app-v2
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: node-pool-version
            operator: In
            values:
            - "v2"
          - key: node-type
            operator: NotIn
            values:
            - "spot"
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 80
        preference:
          matchExpressions:
          - key: zone
            operator: In
            values:
            - "us-east-1a"
          - key: cpu-capacity
            operator: Gt
            values:
            - "8"
  containers:
  - name: app
    image: myapp:2.0

规则解读:

  • 硬性规则: Pod 必须调度到 node-pool-version=v2 且 node-type 不是 spot 的节点。这确保了新版本 Pod 只进入已升级的节点池,并排除竞价实例。
  • 软性偏好: 在满足硬性规则的基础上,优先选择 zone=us-east-1a 且 cpu-capacity 大于 8 的节点,权重为 80。

4.3 matchFields:基于节点字段的选择

matchFields 与 matchExpressions 语法完全相同,但匹配的是节点的 字段(Field) 而非标签。字段是节点对象的固定属性,例如 metadata.name、status.nodeInfo.architecture。

对比维度 matchExpressions matchFields
匹配对象 节点标签(Label) 节点字段(Field)
灵活性 高,可自定义标签 低,字段由 Kubernetes 定义
常见用途 节点池版本、区域、环境 节点名称、架构、操作系统
operator 支持 全部六种 仅 In、NotIn、Exists、DoesNotExist
最佳实践: 在滚动升级场景中,优先使用 matchExpressions 配合自定义标签(如 node-pool-version),因为标签更灵活且易于管理。matchFields 通常用于系统级约束,例如将 Pod 调度到特定名称的节点。

4.3.1 matchFields 示例

spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchFields:
          - key: metadata.name
            operator: In
            values:
            - "node-01"
            - "node-02"

上述规则将 Pod 强制调度到名为 node-01 或 node-02 的节点。注意:matchFields 不支持 Gt 和 Lt,因为字段值通常不是数值。

4.4 节点亲和性选择流程 SVG

下图展示了调度器在处理节点亲和性时的完整决策流程,重点突出 matchExpressions 与 matchFields 的协同工作方式:

节点亲和性调度决策流程 Pod 定义(含 nodeAffinity) 解析 nodeSelectorTerms requiredDuringScheduling 硬性约束,必须满足 preferredDuringScheduling 软性偏好,尽量满足 matchExpressions / matchFields 基于 Label 或 Field 进行匹配 operator 选择 In | NotIn | Exists | DoesNotExist | Gt | Lt 调度决策:允许 / 拒绝

4.5 多表达式组合与逻辑规则

一个 nodeSelectorTerm 中可以包含多个 matchExpressions,它们之间是 AND 关系——所有表达式必须同时满足。如果需要 OR 关系,则定义多个 nodeSelectorTerms。

组合方式 YAML 结构 逻辑关系 示例
同一 term 内多个表达式 一个 nodeSelectorTerms[0].matchExpressions[] 包含多个条目 AND(全部满足) 节点必须同时有 env=prod 和 zone=us-east
多个 term nodeSelectorTerms 列表中有多个 matchExpressions 块 OR(满足任一即可) 节点可以是 env=prod 或 env=staging
滚动升级实战技巧: 使用多个 term 实现“金丝雀发布”策略。例如:
Term 1:node-pool-version In [v2](新版本节点)
Term 2:canary In [true](金丝雀节点)
这样新 Pod 可以调度到已升级节点或专门的金丝雀节点,实现灰度验证。

4.6 常见错误与避坑指南

  • operator 与 values 不匹配: Exists 和 DoesNotExist 不能带 values,否则 API 校验失败;In 和 NotIn 必须至少提供一个 values。
  • Gt/Lt 值类型错误: 确保 label value 是纯数字字符串(如 "10"),不能包含单位(如 "10Gi" 会解析失败)。
  • matchFields 的 key 必须正确: 字段名必须使用 Kubernetes 内部表示,例如 metadata.name 而非 name。可以通过 kubectl get node <node-name> -o json 查看可用字段。
  • 硬性规则过于严格导致 Pod 无法调度: 在滚动升级初期,如果只有少量节点满足条件,大量 Pod 会处于 Pending 状态。建议配合 preferred 规则或使用 PodDisruptionBudget 控制升级速率。
重要: 节点亲和性只在 Pod 调度时生效。如果节点标签在 Pod 运行后发生变化,已经运行的 Pod 不会受到影响(“IgnoredDuringExecution” 的含义)。对于需要实时迁移的场景,请结合 Pod 拓扑分布约束或节点污点(Taint)使用。

4.7 节点亲和性与节点选择器对比

Kubernetes 还提供了更简单的 nodeSelector 字段,但功能有限。下表帮助你在两者之间做出选择:

特性 nodeSelector nodeAffinity(matchExpressions)
表达式能力 仅支持精确匹配(key=value) 支持 In、NotIn、Exists 等六种 operator
多条件组合 多个条件之间是 AND 关系 支持 AND 和 OR(通过多个 term)
软性约束 不支持 支持 preferredDuringScheduling
字段匹配 仅标签 支持标签和字段(matchFields)
推荐场景 简单、静态的节点选择 复杂策略、滚动升级、金丝雀发布
迁移建议: 如果现有配置使用 nodeSelector,可以无缝迁移到 nodeAffinity。例如 nodeSelector: { env: prod } 等价于 matchExpressions: [{ key: env, operator: In, values: [prod] }]。迁移后可以获得更丰富的调度能力。