首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >节点排水卡在 PDB:健康计数、取整与发布重叠

节点排水卡在 PDB:健康计数、取整与发布重叠

原创
作者头像
一只特立独行的兔先生
发布于 2026-11-12 21:15:16
发布于 2026-11-12 21:15:16
910
举报

TKE 上给节点做维护,有业务 Pod 时不要直接移出:先封锁,再驱逐,确认迁走后再移出。副本数够、驱逐却不结束,通常是 PodDisruptionBudget 的允许中断数已经是 0。它只拒绝走 Eviction API 的自愿驱逐。节点宕机和滚动发布不经过这条 API,但会把健康数打下去,反过来把排水堵死。

预算拒绝的是驱逐,不是发布

PDB 从 Kubernetes 1.21 起以 policy/v1 稳定提供。kubectl drain 在 API 支持驱逐时创建 Eviction 对象,而不是直接 DELETE。允许返回 200,Pod 按自己的 terminationGracePeriodSeconds 删除;不允许返回 429。drain 会重试,直到 Pod 终止或超时。429 也可能是 API 限流,要对照 status,不能只看状态码。--timeout 传 0 表示一直等,维护命令必须显式给超时。

动作

PDB 能否拒绝

对健康数的影响

kubectl drain(默认)

能,不满足时 429

驱逐成功后下降

kubectl drain --disable-eviction

不能,改走 DELETE

绕过检查

kubectl delete pod、删除 Deployment

不能

直接删除绕过 PDB

Deployment / StatefulSet 滚动更新

不能拒绝更新

不可用 Pod 计入预算

把 replicas 调小

不能

控制器直接删 Pod

节点宕机、kubelet 压力驱逐

不能预防

非自愿,健康数会掉

minAvailable: 100% 或 maxUnavailable: 0 等于要求零自愿驱逐,drain 永远完不成。发布照样能删 Pod。它挡住的是节点维护,不是上线。

TKE 文档的路径是:集群,节点管理,节点页签,更多,封锁,再更多,驱逐。这一步称为 drain,不迁移 DaemonSet。本地存储 Pod 可能导致驱逐失败,可勾选强制驱逐或手动删除。文档没写 PDB 不满足时控制台是等待、报错还是跳过。要可预期的行为就用 kubectl drain,动手前看 disruptionsAllowed。不要 kubectl delete node,官方说明可能导致节点关联资源泄露;移出也不会自动释放 CVM。

先看 status,再谈副本数

健康只认 Ready=True,记在 currentHealthy。Running 但探针失败不算。desiredHealthy 由 minAvailable 或 maxUnavailable,加上控制器期望副本数算出。控制面用 Pod 的 ownerReferences 找拥有者的 scale。disruptionsAllowed 是此刻还允许的自愿中断次数。observedGeneration 必须等于 generation,否则 status 是旧的,不能拿来放行。

条件 DisruptionAllowed 的 reason 有三种。SufficientPods 表示还允许,次数看 disruptionsAllowed。InsufficientPods 表示已到地板。SyncFailed 表示没算出预算,同样不允许。minAvailable 和 maxUnavailable 只能写一个。官方更建议后者,它跟着期望副本数走。绝对数 minAvailable: 3 在副本被调到 3 之后,允许中断就是 0。调小 replicas 不会被 PDB 拒绝,调完后这份绝对数却可能已经不允许再驱逐。

期望副本数为 1、maxUnavailable 为 30% 时,官方说明这一个副本仍然允许被驱逐,实际不可用是 100%。单副本写百分比,得不到「最多断百分之三十」。

百分比取整和选择器

百分比写成字符串,如 "50%"。minAvailable 向上取整。文档例子:7 个 Pod、50% 时 3.5 取整为 4,至少留 4 个健康 Pod。maxUnavailable 的百分比把可中断 Pod 数向上取整,实际中断可以超过所写百分比。副本少时,取整会把预算变成 0,或变成可以全断。看 status 里的 desiredHealthy,不要心算。

没有 scale 子资源,或选择器对不上拥有者时,只能用整数 minAvailable,不能用 maxUnavailable 和百分比。选择器应与控制器的 .spec.selector 一致。null selector 一个 Pod 都不保护;{} 选中整个命名空间,排水会被无关应用卡住。一个 Pod 被多份 PDB 选中时,Eviction API 返回 500,不是 429,重试也不会变允许。SyncFailed 先查是不是跨了多个控制器却写了 maxUnavailable。

不健康 Pod 默认留在节点上

未写 unhealthyPodEvictionPolicy 时等同 IfHealthyBudget。Running 但未健康的 Pod,只有 currentHealthy 已经不低于 desiredHealthy 才允许驱逐。应用已经低于预算时,这些 Pod 也不能被赶走。官方点名的后果就是 drain 被 CrashLoopBackOff、或不报告 Ready 的 Pod 堵住。Pending、Succeeded、Failed 始终允许驱逐。

所以「3 副本、minAvailable 为 2、应该还能驱逐 1 个」经常不成立。2 个探针失败时 currentHealthy 是 1。健康的不能驱逐,未健康的在默认策略下也不能。drain 一直 429。修探针,或让替代 Pod 先 Ready,预算才会回来。

AlwaysAllow 把 Running 但未健康的 Pod 视为已中断,无论预算是否满足都可驱逐;健康 Pod 仍受约束。安全排水文档建议用它,避免异常应用把节点维护钉死。代价是还没 Ready 的 Pod 也可能被赶走。该字段自 1.31 起 stable,特性门控已移除。更早的集群先执行 kubectl explain pdb.spec.unhealthyPodEvictionPolicy,没有字段就不要写入,否则 apply 会被拒绝。

节点 NotReady 时,PDB 拦不住那批 Pod 消失,健康数却会掉。掉到地板以下后,其余节点的自愿驱逐也会被拒。一台宕了,另外两台排不掉,是这个顺序,不是 PDB 失效。

发布已经把预算用掉

滚动更新不受 PDB 限制,不可用 Pod 却计入预算。Deployment 与 PDB 都是 maxUnavailable: 1 时,发布进行中 disruptionsAllowed 往往已是 0,再 drain 会被拒,直到新 Pod Ready。新 Pod 起不来,发布停在自己的不可用上限,排水停在 PDB。先停发布,不要删 PDB:删掉之后,同一窗口里别人的驱逐也不再受这份预算约束。

代码语言:bash
复制
kubectl rollout pause deployment/checkout -n pay

看新 Pod 为什么不 Ready,修完再 kubectl rollout resume deployment/checkout -n pay。

StatefulSet 要等旧 Pod 完全终止,才创建同序号的新 Pod,预算会空出整个终止加启动的时间。drain 的 --grace-period 默认 -1,用 Pod 自己的值,不要另写更短的秒数。替代 Pod 若 Pending,健康数也不会恢复。总数够仍 Pending,常见是反亲和或拓扑约束排不进去,放大 PDB 没有用。并行 drain 同样守预算:3 副本、minAvailable: 2 时,同一时刻只会自愿驱逐 1 个。

排水前的核对和配置

代码语言:bash
复制
kubectl get pdb -A -o custom-columns='NAME:.metadata.name,NS:.metadata.namespace,MIN:.spec.minAvailable,MAX:.spec.maxUnavailable,HEALTHY:.status.currentHealthy,DESIRED:.status.desiredHealthy,ALLOWED:.status.disruptionsAllowed,GEN:.metadata.generation,OBSERVED:.status.observedGeneration'
kubectl get pdb -n pay checkout-pdb -o jsonpath='{.status.conditions[?(@.type=="DisruptionAllowed")].reason}'

ALLOWED 为 0 不要开始 drain。GEN 与 OBSERVED 不一致时,status 还没跟上。reason 为 SyncFailed 时修选择器,不要重试 drain。

节点名用集群里的真实名字,下面只是示例:

代码语言:bash
复制
kubectl drain 10.0.0.12 --ignore-daemonsets --delete-emptydir-data --timeout=10m

不加 --ignore-daemonsets,命令不会继续:DaemonSet 会忽略不可调度污点并建回同一节点,drain 也不删它们。--delete-emptydir-data 只覆盖 emptyDir;hostPath 是否可丢要单独确认,TKE 文档写明这两类本地数据在移出后会丢失。超时后节点保持封锁。先区分 429 和 Pending,不要用 --disable-eviction 当常规手段,它改走 DELETE。--force 只表示裸 Pod 也删,不绕过 PDB。控制台的强制驱逐写的是本地存储失败,不是 PDB 开关。

无状态和多数派有状态服务都优先 maxUnavailable: 1,选择器与控制器一致。比写死绝对数 minAvailable 不容易在缩容后变成零驱逐。单实例若不能被自愿驱逐,维护前临时删 PDB,做完再创建,不要长期设 maxUnavailable: 0。1.31 及以上、explain 能看到字段时,再打开注释行。

代码语言:yaml
复制
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: checkout-pdb
  namespace: pay
spec:
  maxUnavailable: 1
  selector:
    matchLabels:
      app: checkout
  # unhealthyPodEvictionPolicy: AlwaysAllow
  • 有业务 Pod 先封锁、驱逐,确认只剩 DaemonSet 再移出。不要 kubectl delete node。
  • disruptionsAllowed 大于 0,observedGeneration 等于 generation,reason 不是 InsufficientPods 或 SyncFailed。
  • 选择器与控制器一致,没有第二份 PDB,也没有 selector: {}。绝对数 minAvailable 小于当前期望副本。
  • 没有进行中的滚动更新。其他节点放得下替代 Pod,含反亲和和拓扑约束。
  • drain 带 --ignore-daemonsets 和非零 --timeout。有 emptyDir 再加 --delete-emptydir-data。1.31 及以上且字段存在时,维护窗口用 AlwaysAllow。

参考

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 预算拒绝的是驱逐,不是发布
  • 先看 status,再谈副本数
  • 百分比取整和选择器
  • 不健康 Pod 默认留在节点上
  • 发布已经把预算用掉
  • 排水前的核对和配置
  • 参考
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档