4. 默认调度器调度策略:节点选择、亲和性与污点容忍
Kubernetes 默认调度器(kube-scheduler)在将 Pod 绑定到合适节点时,会依次评估一系列预选(Predicates)与优选(Priorities)策略。其中,节点选择(NodeSelector)、亲和性与反亲和性(Affinity / Anti-Affinity)、污点与容忍(Taint / Toleration)是控制 Pod 调度位置最核心的三类约束机制。理解它们的优先级、组合效果与性能影响,是进行调度调优和故障排查的基础。
4.1 节点选择(NodeSelector)
NodeSelector 是最简单、最直接的节点约束方式。通过在 Pod 的 spec.nodeSelector 字段中定义一组键值对,调度器只会将 Pod 调度到包含全部指定标签的节点上。如果没有任何节点满足所有标签条件,Pod 将处于 Pending 状态。
apiVersion: v1
kind: Pod
metadata:
name: nginx-selector
spec:
nodeSelector:
disktype: ssd # 节点必须包含 disktype=ssd 标签
region: us-east-1 # 节点必须包含 region=us-east-1 标签
containers:
- name: nginx
image: nginx:1.25
4.2 亲和性与反亲和性(Affinity / Anti-Affinity)
亲和性规则分为两类:节点亲和性(NodeAffinity)和 Pod 间亲和性/反亲和性(PodAffinity / PodAntiAffinity)。每种规则又支持 requiredDuringSchedulingIgnoredDuringExecution(硬性要求)和 preferredDuringSchedulingIgnoredDuringExecution(软性偏好)两种模式。
4.2.1 节点亲和性(NodeAffinity)
NodeAffinity 扩展了 NodeSelector 的匹配能力,支持 In、NotIn、Exists、DoesNotExist、Gt、Lt 等操作符。硬性规则不满足时 Pod 无法调度;软性规则不满足时调度器会尝试寻找其他更优节点。
apiVersion: v1
kind: Pod
metadata:
name: node-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: instance-type
operator: In
values:
- c5.large
containers:
- name: app
image: nginx
requiredDuringScheduling 在预选阶段执行,每个 Pod 需要遍历所有节点进行标签匹配。当集群节点数超过 5000 且规则复杂时,匹配计算量会显著增加。建议将常用标签提前索引,避免在 matchExpressions 中使用大量 NotIn 或 Gt 操作符。
4.2.2 Pod 间亲和性与反亲和性(PodAffinity / PodAntiAffinity)
PodAffinity 将 Pod 调度到已经运行了特定 Pod 的拓扑域中;PodAntiAffinity 则尽量将 Pod 分散到不同拓扑域。拓扑域通过 topologyKey 定义,例如 kubernetes.io/hostname(节点级别)或 topology.kubernetes.io/zone(可用区级别)。
apiVersion: v1
kind: Pod
metadata:
name: web-frontend
labels:
app: frontend
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- frontend
topologyKey: kubernetes.io/hostname
containers:
- name: nginx
image: nginx
requiredDuringScheduling 在预选阶段需要为每个候选节点查询该拓扑域内所有已运行 Pod 的标签。当集群 Pod 数量超过 10,000 时,这一步骤的 API 调用和内存消耗会急剧上升。建议:• 优先使用
preferredDuringScheduling 替代硬性要求。• 将
topologyKey 设置为较粗的粒度(如 zone 而非 hostname)。• 避免在
labelSelector 中使用过于宽泛的匹配条件。
4.3 污点与容忍(Taint / Toleration)
污点(Taint)是节点上的一种“排斥标记”,只有带有对应容忍(Toleration)的 Pod 才能被调度到该节点上。污点有三个核心属性:Key、Value(可选)、Effect。Effect 决定了 Pod 被排斥时的行为:
| Effect 类型 | 行为描述 | 典型场景 |
|---|---|---|
NoSchedule |
不满足容忍的 Pod 不会被调度到该节点 | 将专用 GPU 节点标记为仅允许 AI 工作负载 |
PreferNoSchedule |
调度器尽量不将 Pod 调度到该节点,但不强制 | 节点即将维护,优先将新 Pod 调度到其他节点 |
NoExecute |
不满足容忍的 Pod 会被立即驱逐(如果已在运行) | 节点故障时快速驱逐 Pod,避免服务中断 |
# 给节点添加污点
kubectl taint nodes node-gpu-1 gpu=true:NoSchedule
# Pod 中定义容忍
apiVersion: v1
kind: Pod
metadata:
name: gpu-pod
spec:
tolerations:
- key: "gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"
containers:
- name: cuda
image: nvidia/cuda:12.0
NoExecute 污点会触发 Pod 驱逐,对集群稳定性影响较大,应谨慎使用。
4.4 四种策略的优先级与组合效果
当 NodeSelector、NodeAffinity、PodAffinity/AntiAffinity、Taint/Toleration 同时存在时,调度器按以下顺序评估:
- 预选阶段:先检查 Taint/Toleration(节点是否排斥 Pod),再检查 NodeSelector(标签键值对匹配),然后检查 NodeAffinity 的硬性规则,最后检查 PodAffinity/AntiAffinity 的硬性规则。任一条件不满足则过滤该节点。
- 优选阶段:对通过预选的节点,根据 NodeAffinity 的软性规则、PodAffinity 的软性规则、以及其他优选策略(如资源水位、Pod 分布)进行加权评分。
• NodeSelector 与 NodeAffinity 同时定义时,两者是“与”关系——节点必须同时满足两者条件。
• PodAntiAffinity 的
requiredDuringScheduling 与 Taint 冲突时,即使节点容忍了 Pod,反亲和性仍可能阻止调度。• 多个 PodAffinity 规则之间是“与”关系,规则越多,可调度节点越少,调度失败概率越高。
4.5 性能调优建议与排查清单
| 策略 | 预选阶段复杂度 | 调优建议 | 常见排查点 |
|---|---|---|---|
| NodeSelector | O(1) 哈希查找 | 保持标签键值对简洁,避免使用通配符 | 检查节点标签是否存在、拼写是否正确 |
| NodeAffinity | O(n) 遍历 matchExpressions | 减少 matchExpressions 条目数,优先使用 In 而非 NotIn | 检查 operator 是否支持、values 是否为空 |
| PodAffinity/AntiAffinity | O(m * k) m=候选节点数, k=拓扑域内 Pod 数 | 使用粗粒度 topologyKey,避免 required 硬性规则 | 检查 labelSelector 是否匹配到目标 Pod、拓扑域是否一致 |
| Taint/Toleration | O(t) t=节点污点数 | 避免添加过多污点,NoExecute 需谨慎使用 | 检查 tolerationSeconds 是否设置、operator 是否匹配 |
kubectl describe pod <pod-name> — 查看 Events 中的调度失败原因。kubectl get nodes --show-labels — 确认节点标签是否符合 NodeSelector/NodeAffinity。kubectl describe node <node-name> | grep Taints — 查看节点污点信息。kubectl get pods --all-namespaces -o wide | grep <app-label> — 检查 Pod 亲和性依赖的 Pod 是否正常运行。