HPA 缩容被 PDB minAvailable 卡死、drain 被 PDB 挂起:这两个控制回路谁优先?对齐规则怎么定、drain 前有没有工程化的预检?
该问题的正文与回答。
背景
我维护一个 24 节点(16C64G,K8s 1.28.9,containerd 1.7.13,cgroup v2,Calico 路由模式)的生产集群,核心服务是 Java 17 无状态 API(订单查询)。这个服务上同时挂了两个「控制回路」,且它们互相不知道对方存在:
- HPA:minReplicas=5,maxReplicas=14,target CPU=70%,behavior 配了 scaleDown stabilizationWindow=300s、selectPolicy=Max(这套是我之前调 HPA 震荡时定下来的,见引用①);
- PDB:minAvailable=6。
现象(三个打架场景)
-
缩容被 PDB 卡死。凌晨低峰 CPU 掉到 30%,HPA 算出目标从 8 缩到 5,但 PDB 的 minAvailable=6 把「可驱逐数」限制在 2,于是 HPA 缩到 6 就停住、缩不动。HPA status conditions 一直报缩容不生效,查 controller-manager 日志确认是 eviction 被 PDB 的 ValidatingAdmission 拒绝后 HPA 反复重试。结果副本永远停在 6,比 HPA 想要多 1,成本白付,且监控里完全看不出是 PDB 卡住的——这是我最痛的点,冲突是静默的。
-
drain 被 PDB 挂起。做节点维护
kubectl drain --delete-emptydir-data --ignore-daemonsets,当待驱逐的 Pod 会让可用副本数 < minAvailable 时,drain 直接卡住不返回(--force没敢用,怕把副本砍穿)。有一次在 4 节点池里 drain 一个节点,命令挂了 12 分钟,最后人工把 PDB minAvailable 临时改成 3 才放行,完事再改回 6。 -
最隐蔽的竞态:HPA 缩容和 drain 同时发生时,谁先「拿走」那 1 个副本?我观察到 HPA 缩掉的那个副本,恰好是让 PDB 从「满足」滑到「不满足」的那个,于是 drain 反而被自己刚触发的缩容卡住了。
已尝试的排查与结果
- 把 PDB 从 minAvailable 改成 maxUnavailable:语义等价,没解决问题,只是把冲突换了个写法;
- 把 HPA 的 minReplicas 直接提到 6(与 minAvailable 对齐):解决了「缩到 5 卡住」,但代价是低峰期永远多养 1 个副本,而且一旦有人改了 PDB 没同步 HPA,两边又会漂移;
- 确认根因链:eviction 被 PDB 拒绝 → HPA 侧只表现为「缩容不生效」→ 没有任何显式告警/事件可直接定位到 PDB。
期望得到的答案形态
- HPA 的 minReplicas 与 PDB 的 minAvailable 这两个数,业界有没有可落地的对齐规则?比如 minReplicas 必须 ≥ minAvailable+1,还是 = minAvailable?给我一条能写进 runbook 的公式/判断,别只说「注意两边保持一致」;
- drain 被 PDB 卡住时,除了临时改 PDB,有没有更工程化的做法?(drain 前用脚本读
disruptionsAllowed做 pre-check?自动临时下调 minAvailable 再恢复?还是别的?) - 「HPA 缩容和 drain 抢同一个副本」这个竞态,有没有人踩过?你们怎么避免自己触发的缩容反过来卡住自己的 drain?
- 这个静默冲突(eviction 被拒、HPA 缩不动、无告警),你们怎么把它变成可观测的?有没有 Prometheus 指标或告警规则能直接抓到「HPA desiredReplicas < 实际 replicas,且差值被 PDB 卡住」这种状态?
欢迎贴你们线上真实的 HPA + PDB 配置片段、drain 卡住时的 kubectl get pdb / HPA events,以及你们最终的对齐方案。