K8s 调度不均衡排查实录:required 和 preferred 选错引发的连锁反应
2026/8/8 10:39:38 网站建设 项目流程

上篇我们聊了 Pod CrashLoopBackOff 时日志拿不到的排查思路,用 crictl 绕过 kubectl 直接读容器运行时日志。这篇看一个完全不同的方向——Pod 不是起不来,是起来了但分布不均衡。

场景:Deployment 20 副本上线后,Pod 全堆在 2 台 Node,其余 Node 资源充裕但无 Pod 被调度路径:Events 发现 FailedScheduling → 统计匹配标签的 Node 数 → 审阅亲和性配置 → 修正为 preferred → Pod 分布均匀

以下排查基于 K8s v1.28+,使用 apps/v1 API


【坐标】— Pod 调度困局

20 副本 Deployment 上线,新 Pod 全部堆在 node-1 和 node-2——node-3、node-4、node-5 一台 Pod 都没有。

"资源不够吧?"第一反应。kubectl top node一看——node-3 CPU 18%、内存 32%,node-4、node-5 也类似,资源充裕得很。

不是资源不够——是 affinity 配置把调度范围锁死了。

kubectl describe pod看 Events,答案直接贴脸了:

调度器(kube-scheduler,K8s 集群的控制面组件,负责将 Pod 分配到合适的 Node)在 predicate 阶段过滤掉了 3 台 Node。剩下 2 台,刚好塞下 20 个副本——但这不是我们想要的效果。

金句:K8s 不是没有告诉你问题在哪——Events 已经说了,只是你没看


【分层】— 从 Pod 到集群逐层排查

调度问题发生在容器启动前,CRI 层(容器运行时)不涉及,跳过。排查从 Pod 层直接进入 Node 层 → 集群层。

金句:排查 K8s 问题不是在查 Pod——是在查 Pod 所在的整条链路

① Pod 层 — Events 缩小范围

Events 给出了两条线索:

  • node selector 不匹配—— nodeAffinity(节点亲和性,Pod 声明"我只想跑在打了特定标签的 Node 上")只匹配了 2 台 Node
  • pod anti-affinity rules—— podAntiAffinity(Pod 反亲和性,Pod 声明"我不想和其他同标签 Pod 挤在同一台 Node 上")让同组 Pod 互斥

Pod 的 YAML 是这样声明的:

两个都是requiredDuringSchedulingIgnoredDuringExecution(硬约束,Pod 调度时必须满足的条件,调度器在 predicate 阶段直接过滤不满足的 Node;不满足直接报 Unschedulable,不会降级尝试)——调度器必须满足才能调度,不满足就直接判 Unschedulable。

② Node 层 — 统计匹配标签的 Node

nodeAffinity 要求 Node 有workload-type=ai标签。查一下集群里有多少 Node 满足:

只有 2 台。node-3、node-4、node-5 没有这个标签。

那 podAntiAffinity 的作用是什么?topologyKey: kubernetes.io/hostname表示"同一台 Node 上的 Pod 互斥"——每台 Node 最多一个同组 Pod。但 Node 池只有 2 台,所以上限就是 2 个 Pod?

不对,requiredDuringScheduling的 podAntiAffinity 只是不允许同组 Pod 在同一台 Node,只是说"不同 Node 可以各放一个",并不限制每台 Node 的数量。但加上 nodeAffinity 后 Node 池只剩 2 台:

  • Predicate 阶段:nodeAffinity过滤掉 3 台(无workload-type=ai),podAntiAffinity不作额外过滤(没有同 Node 冲突)
  • 剩下 2 台 → Score 阶段按资源打分 → 均匀分布 → 各 10 个

验证 node-3 是否被污点拒绝——kubectl describe node node-3显示 Taints:<none>。没有污点,没有资源压力,单纯是workload-type=ai标签不匹配,调度器在 predicate 阶段就没把它列入候选。


【路径】— 🔍 下次先跑这套命令

排查调度不均衡的核心三板斧:

# ① 看调度失败原因kubectl describe pod<pod>|grep-A15Events# ② 统计匹配 nodeAffinity 的 Node 数kubectl get nodes-l<key>=<value>--show-labels# ③ 看 Pod 跨 Node 分布密度(黄金命令)kubectl get pods-owide|awk'{print $8}'|sort|uniq-c|sort-rn

异常判断标准

  • Events 出现didn't match pod anti-affinity rules+didn't match node selector→ 亲和性配置过严,两个约束将可用 Node 池的交集缩小了
  • kubectl get nodes -l <label>匹配数量 < 3 → nodeAffinity 的 label 覆盖太窄,考虑扩标签或改preferredDuringScheduling
  • 分布密度uniq -c显示某个 Node 集中度 > 60% → 调度不均衡,检查亲和性 + 调度器打分策略

【定位】— ❌ 两个常见误判

Pod 调度不均衡这个现象,最容易被带偏的方向有两个。

误判 1:“加副本就能自然打散”

Naive 思路:"Pod 分布不均 → 加副本让它自己散开。"但 podAntiAffinity 只是互斥同 Node,Node 池只有 2 台的话加再多副本也只堆在这 2 台上。

有经验的人会说:"加 Node 就能分散。"新 Node 没有workload-type=ai标签,不匹配 nodeAffinity,加了也白加。

正确做法:查 nodeAffinity 和 podAntiAffinity 的组合范围。两个亲和性叠加,可用 Node 是两者调度的交集,不是并集。取交集后只剩 2 台——加副本只在 2 台里堆,加 Node 不打对应标签仍然进不来。

误判 2:“requiredDuringScheduling是更安全的选择”

很多人直觉认为"required 比 preferred 靠谱,它能保证规则被执行"。但requiredDuringScheduling是硬约束——调度器在 predicate 阶段必须找到满足条件的 Node,找不到就直接报Unschedulable,不会尝试打分阶段的软性优化。

preferredDuringScheduling是软约束——调度器在 scoring 阶段优先推荐满足条件的 Node,但如果不满足,仍然可以调度到其他 Node。

选择规则:

  • Node 池 < 5 台 → 一律用preferredDuringScheduling
  • Node 池 ≥ 5 台且对分布有硬要求 → 才考虑requiredDuringScheduling,但必须评估缩减后的 Node 池是否足够

【标点】— 修复 + Check-list

YAML 修正

两个关键改动:

  • requiredDuringSchedulingpreferredDuringScheduling:调度器不再因不匹配而拒绝调度,改为优先调度到匹配的 Node
  • podAntiAffinity 权重 50 < nodeAffinity 权重 100:尽量走 AI 标签的 Node,但如果都满了可以落到其他 Node

金句:一个 YAML 字段没配,不是 K8s 会帮你兜底——是它会按默认值运行,而默认值往往不适合你的场景

Check-list

  • kubectl describe pod <pod> | grep -A 15 Events→ 看调度失败原因
  • kubectl get nodes -l <key>=<value>→ 统计匹配 nodeAffinity 的 Node 数量
  • kubectl get pods -o wide | awk '{print $8}' | sort | uniq -c | sort -rn→ 看 Pod 跨 Node 分布
  • kubectl get pod <pod> -o yaml | grep -A 30 affinity→ 审阅完整亲和性配置
  • Node < 5 台一律用preferredDuringScheduling,≥ 5 台才考虑requiredDuringScheduling

金句:故障排查的终点不是修好了——是把排查路径写成 check-list


附:完整命令清单

# ============ 排查阶段 ============# 查看调度 Eventskubectl describe pod<pod>|grep-A15Events# 查看 Pod 分布kubectl get pods-owide# 统计匹配标签的 Nodekubectl get nodes-lworkload-type=ai --show-labels# 查看 Node 详情(污点、标签、资源)kubectl describenode<node># ============ 修复阶段 ============# 应用修正后的 YAMLkubectl apply-fdeployment-fixed.yaml# 观察分布变化kubectl get pods-owide-w# ============ 持续监控 ============# 分布密度快速检视kubectl get pods-owide|awk'{print $8}'|sort|uniq-c|sort-rn

下篇我们聊 DaemonSet 更新策略导致节点逐个宕机的排查——当maxUnavailable设错,rolling update 会一台接一台地干掉你的 Pod。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询