bigywater.tech
Kubernetes · 43

PodDisruptionBudget:drain 时怎么被尊重

PDB 到底保护了什么?drain 的时候它是怎么起作用的?为什么 kubectl delete pod 拦不住?这篇用 Eviction API 直接发请求,看 allowed 数怎么算、429 长什么样、drain 怎么重试,以及不健康的 Pod 在两种策略下的不同命运。

429 → 6.0 秒
预算用完时 Eviction API 的返回,以及替代 Pod 就绪后预算恢复所用的时间
50% of 5 → 3 / 2
minAvailable: "50%" 时 desiredHealthy 为 3(向上取整)、允许中断 2 个
IfHealthyBudget vs AlwaysAllow
同一个不健康的 Pod,驱逐请求分别返回 429 与 201

PDB 怎么算

实测 · 本机执行输出
  minAvailable: 2        → currentHealthy=5 desiredHealthy=2 disruptionsAllowed=3 expectedPods=5
  minAvailable: "50%"    → currentHealthy=5 desiredHealthy=3 disruptionsAllowed=2 expectedPods=5
  maxUnavailable: 1      → currentHealthy=5 desiredHealthy=4 disruptionsAllowed=1 expectedPods=5
  maxUnavailable: "50%"  → currentHealthy=5 desiredHealthy=2 disruptionsAllowed=3 expectedPods=5
  minAvailable: "100%"   → currentHealthy=5 desiredHealthy=5 disruptionsAllowed=0 expectedPods=5

PDB 的 status 里 disruptionsAllowed = currentHealthy − desiredHealthy。同样 5 个健康 Pod:minAvailable: 2 允许 3 个;minAvailable: "50%" 的 desiredHealthy 是 3(2.5 向上取整),允许 2 个;maxUnavailable: 1 允许 1 个;maxUnavailable: "50%" 允许 3 个;minAvailable: "100%" 一个都不让动。写成 100% 的 PDB 会让节点 drain 永远卡住。

Eviction API

实测 · 本机执行输出
  3 个 Pod、minAvailable: 2:currentHealthy=3 desiredHealthy=2 disruptionsAllowed=1 expectedPods=3
  驱逐第 1 个 Pod:退出码 0;{"kind":"Status","apiVersion":"v1","metadata":{},"status":"Success","code":201}
  紧接着驱逐第 2 个 Pod:退出码 1;Error from server (TooManyRequests): Cannot evict pod as it would violate the pod's disruption budget.
    此时 PDB:currentHealthy=2 desiredHealthy=2 disruptionsAllowed=0 expectedPods=3
  从第 1 次驱逐算起,Deployment 补出新 Pod 并就绪用了 6.0 秒(就绪探针 initialDelaySeconds=5);PDB:currentHealthy=3 desiredHealthy=2 disruptionsAllowed=1 expectedPods=3
  现在再驱逐一个:退出码 0;{"kind":"Status","apiVersion":"v1","metadata":{},"status":"Success","code":201}

3 个 Pod、minAvailable: 2(允许 1 个)。直接调 Eviction API:第 1 次 201;紧接着第 2 次 TooManyRequests: Cannot evict pod as it would violate the pod's disruption budget.。这时 currentHealthy=2、disruptionsAllowed=0,要等 Deployment 补出的新 Pod 真的 Ready(这里就绪探针延迟 5 秒,总共 6.0 秒)预算才恢复,再驱逐才会成功。429 是让调用方重试,不是失败。

drain 与 delete

实测 · 本机执行输出
  3 个 web Pod 的分布:worker:1, worker2:2;要清空的节点:worker2(2 个 Pod)
  kubectl drain worker2(退出码 0,用了 6 秒)输出里和驱逐有关的行:
    node/kx-main-worker2 cordoned
    evicting pod k37/web-c4d4744bd-m4wpg
    evicting pod k37/web-c4d4744bd-lhvnt
    pod/web-c4d4744bd-m4wpg evicted
    evicting pod k37/web-c4d4744bd-lhvnt
    pod/web-c4d4744bd-lhvnt evicted
    node/kx-main-worker2 drained
    error when evicting pods/"web-c4d4744bd-lhvnt" -n "k37" (will retry after 5s): Cannot evict pod as it would violate the pod's disruption budget.
  对比:kubectl delete pod 一次删掉 2 个(不经过 Eviction API,PDB 拦不住):0.5 秒后 ready 的 Pod 有 1 个;PDB:currentHealthy=1 desiredHealthy=2 disruptionsAllowed=0 expectedPods=3

kubectl drain 对节点上的 2 个 Pod 同时发起驱逐:第 1 个成功,第 2 个被 PDB 拦住,输出 error when evicting … (will retry after 5s),5 秒后重试成功,一共 6 秒。而 kubectl delete pod 一次删 2 个不是驱逐,PDB 拦不住:0.5 秒后只剩 1 个 Ready,PDB 变成 currentHealthy=1 < desiredHealthy=2。PDB 只约束自愿中断(驱逐),节点宕机、直接删除这类它管不了。

不健康的 Pod

实测 · 本机执行输出
  unhealthyPodEvictionPolicy=IfHealthyBudget:3 个 Pod 里 1 个就绪探针失败、minAvailable: 3:currentHealthy=2 desiredHealthy=3 disruptionsAllowed=0 expectedPods=3
    驱逐那个不健康的 Pod:退出码 1;Error from server (TooManyRequests): Cannot evict pod as it would violate the pod's disruption budget.
  unhealthyPodEvictionPolicy=AlwaysAllow:3 个 Pod 里 1 个就绪探针失败、minAvailable: 3:currentHealthy=2 desiredHealthy=3 disruptionsAllowed=0 expectedPods=3
    驱逐那个不健康的 Pod:退出码 0;code=201 message=None

3 个 Pod 里 1 个就绪探针失败、minAvailable: 3:currentHealthy=2,disruptionsAllowed=0。默认的 unhealthyPodEvictionPolicy: IfHealthyBudget 下,连这个不健康的 Pod 也驱逐不了(429),drain 会一直卡;改成 AlwaysAllow 之后不健康的 Pod 随时可以驱逐(201),应用本来就坏了,没必要再为它保留名额。

分步演示

PDB 与驱逐
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。三节点 kind 集群 kx-main(Kubernetes v1.37.0);两个 worker 打上标签 k37=yes,Deployment 用 nodeSelector 只放在它们上面;Eviction API 用 kubectl create --raw /api/v1/namespaces//pods//eviction 直接请求;第 2、3 节的就绪探针用 initialDelaySeconds=5 让替代 Pod 需要几秒才就绪(没有这个延迟时替代 Pod 0.4 秒就就绪,drain 里看不到重试);kubectl drain 带 --ignore-daemonsets --delete-emptydir-data --force,结束后 uncordon;实验后删除了节点标签与命名空间;每种情形各一次
    • 没有验证:PDB 的 selector 为空或匹配到带 ownerReferences 的非标准控制器时 expectedPods 的计算;一个 Pod 被多个 PDB 选中时的错误;抢占对 PDB 的遵守程度;节点自动伸缩(cluster-autoscaler)对 PDB 的使用;drain 的 --disable-eviction 与 --grace-period;PDB 对 Pod 级别 DisruptionTarget 条件的关系
    京ICP备2026067502号-1京公网安备11010502063487号