2. Node亲和性:nodeSelector、nodeAffinity的required与preferred规则、多标签匹配策略

Node亲和性(Node Affinity)是Kubernetes调度器将Pod绑定到特定节点的一组规则。与简单的nodeSelector相比,Node亲和性提供了更丰富的表达能力,支持必须满足(required)和倾向满足(preferred)两种语义,以及多标签逻辑组合。本章从基础到进阶,系统讲解这三种调度约束机制。

2.1 nodeSelector:最简标签匹配

nodeSelector是Kubernetes最早提供的节点选择机制,通过在Pod的spec中定义nodeSelector字段,指定一组键值对标签。调度器仅将Pod调度到包含所有指定标签的节点上。

关键特性:nodeSelector只支持“与(AND)”逻辑,所有标签必须同时匹配;不支持“或(OR)”或“非(NOT)”操作。
特性 nodeSelector 适用场景
匹配逻辑 仅AND(全部匹配) 简单固定分组
表达式能力 无(仅键值对) GPU节点、SSD节点
软约束 不支持 —
维护成本 低 小规模集群
apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  nodeSelector:
    accelerator: nvidia-tesla-v100
    disktype: ssd
  containers:
  - name: cuda-container
    image: nvidia/cuda:12.0-base

上述Pod只会被调度到同时拥有accelerator=nvidia-tesla-v100和disktype=ssd标签的节点上。如果集群中没有这样的节点,Pod将一直处于Pending状态。

2.2 nodeAffinity:进阶亲和性规则

nodeAffinity是nodeSelector的进化版,属于Pod的affinity字段下的子结构。它支持两种规则类型:requiredDuringSchedulingIgnoredDuringExecution(硬约束)和preferredDuringSchedulingIgnoredDuringExecution(软约束)。

命名解读:“requiredDuringSchedulingIgnoredDuringExecution”表示调度时必须满足,但节点运行时标签变化不影响已运行Pod;“preferred”表示调度时尽量满足,但不强制。

2.2.1 required(硬约束)规则

硬约束规则与nodeSelector效果类似,但支持更丰富的表达式操作符:In、NotIn、Exists、DoesNotExist、Gt、Lt。多个matchExpressions之间默认是AND关系。

apiVersion: v1
kind: Pod
metadata:
  name: affinity-required-pod
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: topology.kubernetes.io/zone
            operator: In
            values:
            - us-east-1a
            - us-east-1b
          - key: node-type
            operator: NotIn
            values:
            - spot
  containers:
  - name: app
    image: nginx:1.25

上述Pod要求:节点必须位于us-east-1a或us-east-1b可用区,且不能是spot类型。如果两个条件无法同时满足,Pod不会被调度。

2.2.2 preferred(软约束)规则

软约束规则为调度器提供倾向性建议,但不强制。每个preference可以设置weight权重(1-100),调度器会计算所有满足条件的节点得分,得分高的节点优先被选中。

apiVersion: v1
kind: Pod
metadata:
  name: affinity-preferred-pod
spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 80
        preference:
          matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd
      - weight: 20
        preference:
          matchExpressions:
          - key: region
            operator: In
            values:
            - us-east
  containers:
  - name: app
    image: nginx:1.25

调度器会优先选择disktype=ssd的节点(权重80),同时如果节点也在us-east区域(权重20),得分更高。但如果没有SSD节点,Pod仍然可以被调度到普通节点上。

2.3 多标签匹配策略与组合

实际生产环境中,节点标签往往不止两三个。理解多标签的组合逻辑是正确使用Node亲和性的关键。

2.3.1 nodeSelectorTerms 与 matchExpressions 的层级关系

nodeAffinity的nodeSelectorTerms是一个列表,列表内多个term之间是OR关系;每个term内的matchExpressions列表是AND关系。这种设计提供了“或”与“与”的混合表达能力。

nodeAffinity 多标签匹配逻辑结构 Pod nodeSelectorTerms [ OR ] Term 1 (AND) Term 2 (AND) Term 3 (AND) Expr 1: zone In [a,b] Expr 2: node-type NotIn [spot] Expr 1: disktype In [ssd] Expr 1: gpu Exists 匹配任意一个 Term 即可调度;每个 Term 内的所有 Expression 必须同时满足

2.3.2 多标签组合示例

以下示例展示了一个复杂的多标签匹配策略:Pod优先选择SSD节点且位于us-east区域,但如果没有,也可以接受HDD节点但必须位于us-east区域。

spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:          # Term 1: SSD + us-east
          - key: disktype
            operator: In
            values: [ssd]
          - key: region
            operator: In
            values: [us-east]
        - matchExpressions:          # Term 2: HDD + us-east(备选)
          - key: disktype
            operator: In
            values: [hdd]
          - key: region
            operator: In
            values: [us-east]
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 70
        preference:
          matchExpressions:
          - key: disktype
            operator: In
            values: [ssd]
常见陷阱:多个matchExpressions在同一term内是AND关系,如果误将“或”逻辑的标签放在同一term内,会导致调度失败。例如“节点标签为A或B”应该拆成两个term,而不是放在一个term内。

2.4 nodeSelector 与 nodeAffinity 的对比与选型

对比维度 nodeSelector nodeAffinity required nodeAffinity preferred
约束强度 硬约束 硬约束 软约束
操作符支持 仅= In/NotIn/Exists/DoesNotExist/Gt/Lt In/NotIn/Exists/DoesNotExist/Gt/Lt
OR逻辑 不支持 支持(多term) 支持(多preference)
权重打分 不支持 不支持 支持(weight 1-100)
运行时标签变化 不影响 不影响(IgnoredDuringExecution) 不影响
推荐场景 简单固定分组 合规/安全/硬件要求 性能优化/负载均衡

2.5 最佳实践与避坑指南

  • 优先使用nodeAffinity替代nodeSelector:nodeAffinity功能更强大,且未来版本可能弃用nodeSelector。
  • required规则不要过于严格:如果硬约束条件过多,可能导致大量Pod处于Pending状态,建议配合preferred规则兜底。
  • 权重设计要合理:preferred规则的weight值差异应足够大(建议至少20分差距),否则调度器可能无法有效区分优先级。
  • 标签命名规范:使用topology.kubernetes.io/*、node.kubernetes.io/*等标准前缀,避免自定义标签冲突。
  • 测试验证:使用kubectl get nodes --show-labels确认节点标签,再用kubectl describe pod <pod-name>查看调度事件。
生产建议:在大型集群中,将nodeAffinity与PodAntiAffinity、topologySpreadConstraints配合使用,可以实现更精细的拓扑分布控制,避免单点故障和资源倾斜。