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 能否拒绝 | 对健康数的影响 |
|---|---|---|
| 能,不满足时 429 | 驱逐成功后下降 |
| 不能,改走 DELETE | 绕过检查 |
| 不能 | 直接删除绕过 PDB |
Deployment / StatefulSet 滚动更新 | 不能拒绝更新 | 不可用 Pod 计入预算 |
把 | 不能 | 控制器直接删 Pod |
节点宕机、kubelet 压力驱逐 | 不能预防 | 非自愿,健康数会掉 |
minAvailable: 100% 或 maxUnavailable: 0 等于要求零自愿驱逐,drain 永远完不成。发布照样能删 Pod。它挡住的是节点维护,不是上线。
TKE 文档的路径是:集群,节点管理,节点页签,更多,封锁,再更多,驱逐。这一步称为 drain,不迁移 DaemonSet。本地存储 Pod 可能导致驱逐失败,可勾选强制驱逐或手动删除。文档没写 PDB 不满足时控制台是等待、报错还是跳过。要可预期的行为就用 kubectl drain,动手前看 disruptionsAllowed。不要 kubectl delete node,官方说明可能导致节点关联资源泄露;移出也不会自动释放 CVM。
健康只认 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。
未写 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:删掉之后,同一窗口里别人的驱逐也不再受这份预算约束。
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 个。
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。
节点名用集群里的真实名字,下面只是示例:
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 能看到字段时,再打开注释行。
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: checkout-pdb
namespace: pay
spec:
maxUnavailable: 1
selector:
matchLabels:
app: checkout
# unhealthyPodEvictionPolicy: AlwaysAllowkubectl delete node。disruptionsAllowed 大于 0,observedGeneration 等于 generation,reason 不是 InsufficientPods 或 SyncFailed。selector: {}。绝对数 minAvailable 小于当前期望副本。--ignore-daemonsets 和非零 --timeout。有 emptyDir 再加 --delete-emptydir-data。1.31 及以上且字段存在时,维护窗口用 AlwaysAllow。原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。