4. 默认调度算法:NodeResourcesFit、NodeName、NodeUnschedulable、NodeSelector、NodeAffinity、TaintToleration

Kubernetes 调度器在预选(Predicate)阶段会依次执行一组默认的调度算法,对每个待调度的 Pod 和所有候选 Node 进行过滤。这些算法共同决定了 Pod 能否被调度到某个节点上。本章将深入解析五个核心默认算法:NodeResourcesFit、NodeName、NodeUnschedulable、NodeSelector、NodeAffinity 和 TaintToleration。

4.1 算法执行流程与过滤逻辑

调度器在预选阶段按顺序执行这些算法,任何一个算法返回“不满足”,该节点即被排除。下图展示了默认调度算法的执行顺序与过滤关系:

默认调度算法预选阶段执行流程 待调度 Pod 候选 Node 列表 默认调度算法链(顺序执行) 1. NodeName 2. NodeUnschedulable 3. NodeSelector 4. NodeAffinity 5. TaintToleration 通过过滤的 Node 列表 任一算法不通过 → 节点被排除
执行顺序说明:调度器按图中顺序依次执行算法。NodeName 和 NodeUnschedulable 属于快速过滤,能在早期排除大量不匹配节点,减少后续算法的计算开销。

4.2 算法详解与配置实践

4.2.1 NodeName

作用:检查 Pod 是否通过 spec.nodeName 显式指定了目标节点。如果指定,则只匹配该节点;否则通过所有节点。

使用场景:调试、DaemonSet 固定调度、特殊硬件节点绑定。

apiVersion: v1
kind: Pod
metadata:
  name: fixed-node-pod
spec:
  nodeName: worker-01  # 直接指定节点名称
  containers:
  - name: nginx
    image: nginx:latest
注意:使用 nodeName 会绕过调度器,即使节点不可用,Pod 也会被直接分配到该节点。生产环境应优先使用节点选择器或亲和性。

4.2.2 NodeUnschedulable

作用:检查节点是否被标记为不可调度(unschedulable)。如果节点设置了 spec.unschedulable: true,则过滤掉该节点。

管理命令:

# 将节点标记为不可调度(驱逐已有 Pod 需加 --delete-emptydir-data)
kubectl cordon node-01

# 恢复节点可调度
kubectl uncordon node-01

典型用途:节点维护、升级、故障隔离时,先 cordon 节点,确保新 Pod 不会被调度上来。

4.2.3 NodeSelector

作用:根据 Pod 中 spec.nodeSelector 定义的标签键值对,过滤出具有匹配标签的节点。

配置示例:

apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
spec:
  nodeSelector:
    gpu-type: nvidia-tesla  # 节点必须包含此标签
  containers:
  - name: cuda-container
    image: nvidia/cuda:11.0-base
特性 NodeSelector NodeAffinity
表达能力 简单键值对匹配 支持 In/NotIn/Exists/DoesNotExist/Gt/Lt 等操作符
逻辑关系 所有条件必须同时满足(AND) 支持 required(硬性)和 preferred(软性)
灵活性 低 高
推荐场景 简单标签过滤 复杂调度策略、多条件组合

4.2.4 NodeAffinity

作用:通过丰富的标签匹配规则,实现更灵活的节点选择。支持两种类型:

  • requiredDuringSchedulingIgnoredDuringExecution:硬性要求,调度时必须满足,运行中忽略变化。
  • preferredDuringSchedulingIgnoredDuringExecution:软性偏好,调度时尽量满足,不满足也能调度。
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: disk-type
            operator: In
            values:
            - ssd
  containers:
  - name: app
    image: myapp:latest
最佳实践:将硬性约束(如合规区域)放在 required 中,将性能偏好(如 SSD 磁盘)放在 preferred 中,并合理设置 weight(1-100)来区分优先级。

4.2.5 TaintToleration

作用:节点通过 Taint(污点)拒绝 Pod 调度,Pod 通过 Toleration(容忍)声明可以容忍哪些污点。调度器检查 Pod 的容忍度是否能覆盖节点的所有 Taint。

Taint 组成:

  • key:污点键
  • value:污点值(可选)
  • effect:污点效果,三种取值:
    • NoSchedule:不调度新 Pod
    • PreferNoSchedule:尽量不调度
    • NoExecute:不调度 + 驱逐已有 Pod
# 给节点添加污点
kubectl taint nodes node-01 dedicated=gpu:NoSchedule

# 查看节点污点
kubectl describe node node-01 | grep Taints

Pod 容忍配置:

apiVersion: v1
kind: Pod
metadata:
  name: gpu-workload
spec:
  tolerations:
  - key: "dedicated"
    operator: "Equal"
    value: "gpu"
    effect: "NoSchedule"
  containers:
  - name: gpu-container
    image: nvidia/cuda:11.0-base

容忍操作符:

操作符 说明 示例
Equal key、value、effect 全部匹配 key: "dedicated", value: "gpu", effect: "NoSchedule"
Exists 仅匹配 key 和 effect,忽略 value key: "dedicated", effect: "NoSchedule"
关键规则:Pod 必须容忍节点上 所有 带有 NoSchedule 或 NoExecute 效果的污点,才能被调度到该节点。如果节点有多个污点,Pod 必须为每个污点都配置对应的容忍。

4.3 算法组合实战案例

以下案例展示如何组合使用多个调度算法,实现精细化的 Pod 调度策略:

apiVersion: v1
kind: Pod
metadata:
  name: production-web
spec:
  # 1. NodeName 不指定,让调度器自由选择
  # 2. NodeUnschedulable 由集群管理员控制,Pod 无需配置

  # 3. NodeSelector 选择 SSD 节点
  nodeSelector:
    disk-type: ssd

  # 4. NodeAffinity 偏好可用区
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: topology.kubernetes.io/region
            operator: In
            values:
            - us-east
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 60
        preference:
          matchExpressions:
          - key: topology.kubernetes.io/zone
            operator: In
            values:
            - us-east-1a

  # 5. TaintToleration 容忍专用节点污点
  tolerations:
  - key: "dedicated"
    operator: "Equal"
    value: "production"
    effect: "NoSchedule"

  containers:
  - name: web
    image: nginx:latest

调度决策过程:

  1. 调度器获取所有 Ready 节点
  2. NodeName 检查:未指定,通过所有节点
  3. NodeUnschedulable 检查:过滤掉被 cordon 的节点
  4. NodeSelector 检查:只保留 disk-type=ssd 的节点
  5. NodeAffinity 检查:必须位于 region=us-east,优先选择 zone=us-east-1a
  6. TaintToleration 检查:节点若有 dedicated=production:NoSchedule 污点,Pod 必须能容忍
  7. 通过所有过滤的节点进入优选阶段
性能优化建议:将计算量小的算法(NodeName、NodeUnschedulable)放在前面,将计算量大的算法(NodeAffinity、TaintToleration)放在后面,可以尽早过滤掉大量不匹配节点,提升调度效率。

4.4 常见问题与排错

问题现象 可能原因 排查命令
Pod 一直 Pending 节点污点未容忍 / 标签不匹配 kubectl describe pod <pod-name> 查看 Events
Pod 调度到错误节点 NodeSelector 或 NodeAffinity 配置错误 kubectl get nodes --show-labels 检查节点标签
节点无法调度新 Pod 节点被 cordon 或存在 NoExecute 污点 kubectl describe node <node-name> 查看 Taints 和 Conditions
容忍配置不生效 操作符或 effect 拼写错误 检查 tolerations 中 key/value/effect 是否与节点污点完全一致