3. 节点亲和性与 Pod 调度:nodeAffinity 的硬约束与软约束

节点亲和性(Node Affinity)是 Kubernetes 调度器将 Pod 绑定到特定节点集合的核心策略。与简单的 nodeSelector 相比,nodeAffinity 提供了更丰富的匹配表达式(In、NotIn、Exists、DoesNotExist、Gt、Lt)以及两种调度语义:硬约束(必须满足)和软约束(尽量满足)。

3.1 硬约束 vs 软约束:核心区别

维度 requiredDuringSchedulingIgnoredDuringExecution(硬约束) preferredDuringSchedulingIgnoredDuringExecution(软约束)
调度语义 Pod 必须被调度到满足条件的节点上;若不存在符合条件的节点,Pod 将一直处于 Pending 状态。 调度器会优先选择满足条件的节点;若没有节点满足,Pod 仍会被调度到其他可用节点上。
权重 无权重概念,条件为布尔逻辑。 每个规则可设置 weight(1-100),调度器按加权和排序节点。
执行阶段 仅在 Pod 调度时检查(Scheduling)。运行期间节点标签变化不影响已运行 Pod。 仅在 Pod 调度时计算偏好。运行期间节点标签变化不影响已运行 Pod。
典型场景 GPU 节点、合规区域、专用硬件池。 可用区均衡、性能优先、成本优化。
关键理解: 两种策略的 IgnoredDuringExecution 后缀意味着:节点在 Pod 运行后变更标签,不会驱逐已运行的 Pod。这是 Kubernetes 设计上对运行工作负载的保护。

3.2 调度流程与决策树

下图展示了调度器在处理 nodeAffinity 时的完整决策路径:

nodeAffinity 调度决策流程 Pod 创建请求 requiredDuringScheduling 存在满足条件的节点? 是 preferredDuringScheduling 计算所有节点加权得分 选择最高加权得分节点 Pod 绑定到节点 Pending 说明: • 硬约束失败 → Pod 进入 Pending 状态,调度器持续重试。 • 软约束即使所有节点都不满足,Pod 仍会被调度到任意可用节点。

3.3 实战配置示例

3.3.1 硬约束:强制调度到 GPU 节点

以下配置确保 Pod 只能运行在带有 gpu-type=nvidia-a100 标签的节点上:

apiVersion: v1
kind: Pod
metadata:
  name: gpu-inference-pod
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: gpu-type
            operator: In
            values:
            - nvidia-a100
  containers:
  - name: inference
    image: nvidia/cuda:12.0-runtime
    resources:
      limits:
        nvidia.com/gpu: 1
避坑提示: 当使用 In 操作符时,values 列表至少包含一个值。若节点标签值可能为空,考虑使用 Exists 操作符仅检查 key 是否存在。

3.3.2 软约束:优先选择低延迟区域

以下配置优先将 Pod 调度到 zone=low-latency 的节点,权重为 80;若不可用,则回退到其他区域:

apiVersion: v1
kind: Pod
metadata:
  name: latency-sensitive-app
spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 80
        preference:
          matchExpressions:
          - key: zone
            operator: In
            values:
            - low-latency
      - weight: 20
        preference:
          matchExpressions:
          - key: zone
            operator: In
            values:
            - standard
  containers:
  - name: app
    image: nginx:alpine

3.4 多条件组合与操作符详解

操作符 语义 示例
In 标签值在给定集合中 key: region, values: [us-east-1, us-west-2]
NotIn 标签值不在给定集合中 key: disk-type, values: [hdd]
Exists 节点存在该标签键(忽略值) key: nvidia.com/gpu, operator: Exists
DoesNotExist 节点不存在该标签键 key: spot-instance, operator: DoesNotExist
Gt / Lt 标签值大于/小于给定数值(值必须为整数) key: memory-gb, operator: Gt, values: [64]

3.5 硬约束与软约束联合使用

生产环境中,硬约束和软约束通常组合使用,形成“必须满足基础条件 + 尽量优化”的策略:

apiVersion: v1
kind: Pod
metadata:
  name: hybrid-affinity-pod
spec:
  affinity:
    nodeAffinity:
      # 硬约束:必须运行在 ARM 架构节点上
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/arch
            operator: In
            values:
            - arm64
      # 软约束:优先选择高内存节点
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 70
        preference:
          matchExpressions:
          - key: memory-size
            operator: In
            values:
            - large
            - xlarge
      - weight: 30
        preference:
          matchExpressions:
          - key: topology.kubernetes.io/zone
            operator: In
            values:
            - zone-a
  containers:
  - name: app
    image: arm64v8/nginx
调度器行为: 调度器首先过滤出所有满足硬约束的节点(ARM64),然后在这些节点上根据软约束权重计算得分(高内存 +70,zone-a +30),最终选择总分最高的节点。若没有节点满足硬约束,Pod 将保持 Pending。

3.6 调试与验证命令

使用 kubectl debug 和调度器事件排查亲和性调度问题:

# 查看 Pod 调度事件
kubectl describe pod <pod-name> | grep -A 10 Events

# 使用临时调试容器检查节点标签
kubectl debug node/<node-name> -it --image=busybox -- sh
# 在调试容器内执行:cat /etc/hostname 或 kubectl get nodes --show-labels

# 模拟调度(dry-run)
kubectl create -f pod.yaml --dry-run=server -o json | jq '.spec.nodeName'

# 查看调度器日志(需有权限)
kubectl logs -n kube-system -l component=kube-scheduler --tail=50 | grep <pod-name>

3.7 常见问题与排错

  • Pod 一直 Pending: 检查 kubectl describe pod 中 Events 是否提示 0/3 nodes are available: 3 node(s) didn't match node selector。确认节点标签是否存在且值匹配。
  • 软约束未生效: 软约束是尽力而为,若所有节点都不满足,Pod 仍会被调度。检查 weight 值是否过低,或与其他调度策略(如 Pod 反亲和性)冲突。
  • 节点标签变更后 Pod 未迁移: 这是预期行为。两种策略的 IgnoredDuringExecution 后缀明确表示运行时不重新调度。如需响应式迁移,需结合 descheduler 或自定义控制器。
  • 多条件逻辑混淆: nodeSelectorTerms 下多个 matchExpressions 是 OR 关系;单个 matchExpressions 内多个表达式是 AND 关系。