A node upgrade is stuck: the drain has been retrying for an hour with "Cannot evict pod as it would violate the pod's disruption budget". Walk through your diagnosis and the options for unblocking it.
answer
- kubectl get pdb -A → ALLOWED DISRUPTIONS 0
- currentHealthy vs desiredHealthy vs expectedPods
- unready pods pin the budget → AlwaysAllow
- replacements Pending = capacity, not budget
- scale up before you weaken the PDB
basics
~20 sFind which PDB has disruptionsAllowed = 0 with kubectl get pdb -A, then check why: replicas equal to minAvailable, maxUnavailable 0, pods failing readiness so they never count as healthy, a selector spanning several workloads, or overlapping PDBs. Fix the cause — scale up, correct the budget, fix readiness — rather than deleting the PDB.
solid answer
~60 s**Diagnose first.** 1. `kubectl get pdb -A` — find budgets with `ALLOWED DISRUPTIONS` = 0. Compare `currentHealthy`, `desiredHealthy`, `expectedPods`. 2. Identify the blocking pod from the drain output, then check whether its replicas are actually **Ready** — a workload in CrashLoopBackOff or failing a readiness probe has `currentHealthy` below `desiredHealthy` permanently, so no eviction can ever be allowed. 3. Check the PDB's selector against the pods it really matches; a broad selector spanning two Deployments, or two PDBs matching one pod (which blocks eviction outright), are common causes. **Then pick a fix by root cause:** - Budget is unsatisfiable by construction (`maxUnavailable: 0`, `minAvailable` = replica count, `minAvailable: 1` on one replica) → correct the budget, or scale the Deployment up so headroom exists. - Pods are unhealthy → fix the app; set `unhealthyPodEvictionPolicy: AlwaysAllow` so broken pods stop pinning the budget. - Genuinely slow but progressing → wait; drain is paced by replacement readiness. **Last resorts, with the tradeoff stated:** temporarily relax the PDB, or `kubectl drain --disable-eviction` / delete pods directly, which ignores the budget and risks the outage it was written to prevent.
code
bash · 5 lineskubectl get pdb -A
kubectl get pdb -n prod api -o jsonpath='{.status}{"\n"}'
kubectl get pods -n prod -l app=api -o wide
kubectl get pods -n prod --field-selector=status.phase=Pending
kubectl scale -n prod deploy/api --replicas=7go deeper
Know the message means the budget allows zero disruptions, and that kubectl get pdb is the first command to run.
Separate the causes — unsatisfiable budget versus unready pods — and know that scaling up is a safe unblock.
Run the ordered playbook including Pending replacements and overlapping selectors, and choose the fix by root cause with the risk stated.
Prevent it structurally: admission checks against unsatisfiable budgets, AlwaysAllow by default, and alerting on budgets stuck at zero well before an upgrade window.
## The message is a symptom, not a cause `Cannot evict pod as it would violate the pod's disruption budget` means only that `disruptionsAllowed` was 0 for a PDB matching that pod. Five distinct root causes produce it, and they need different fixes. ## Step 1 — locate the budget ``` kubectl get pdb -A kubectl get pdb -n prod api -o yaml ``` Read `status`: `currentHealthy`, `desiredHealthy`, `expectedPods`, `disruptionsAllowed`. The gap between the first two tells you immediately whether you have a *budget arithmetic* problem or a *pod health* problem. ## Cause A — the budget can never be satisfied `maxUnavailable: 0`, `minAvailable: 100%`, or `minAvailable: N` where the Deployment runs exactly N replicas. Here `currentHealthy == desiredHealthy` with every pod perfectly healthy. Nothing will change on its own; waiting is futile. Fix: either scale the workload up so there is headroom (`kubectl scale deploy/api --replicas=N+2`, the least invasive option because it *adds* availability rather than removing protection), or correct the budget to `maxUnavailable: 1`. Scaling up is usually the right emergency action: it unblocks the drain while honoring the intent of the budget. ## Cause B — pods are not Ready Only pods whose `Ready` condition is true count toward `currentHealthy`. A workload with 6 replicas where 3 are CrashLooping has `currentHealthy: 3` against `desiredHealthy: 5` — the budget is *already* violated, so every eviction is refused. Ironically the pods blocking maintenance are the ones serving nothing. Check with `kubectl get pods -l <selector> -o wide` and look at the READY column, then at events and probe configuration. Sometimes readiness fails because a dependency is down, not because the pod is broken. Fix: repair the workload. Structurally, set `spec.unhealthyPodEvictionPolicy: AlwaysAllow` (GA in Kubernetes 1.31) on the PDB so running-but-unready pods may be evicted even when the budget is exhausted. That is the purpose-built escape from this deadlock and belongs in your default PDB template. ## Cause C — selector problems PDB selectors match labels, independent of ownership: - A selector like `app: web` may cover `web-canary` and `web-stable` at once, so one Deployment's health consumes the other's budget. - Two PDBs matching the same pod cause the eviction to be **refused outright**, regardless of either budget's numbers. `kubectl get pdb -A -o json` plus a check of which selectors match the blocking pod's labels finds this. - A PDB whose selector matches nothing has `expectedPods: 0`; harmless for drains but a sign the labels drifted, and it means a workload someone believes is protected is not. ## Cause D — no capacity for replacements An eviction only restores budget when a replacement pod becomes Ready — and the replacement must be **scheduled somewhere else**. If the cluster is full, or the pods have node affinity / topology spread / a specific zone requirement that only the draining node satisfied, replacements stay `Pending`, the budget never recovers, and the drain retries forever. Symptom: `disruptionsAllowed` pinned at 0 *and* pods in Pending with `FailedScheduling` events. The fix is capacity, not the PDB. ## Cause E — it is working, just slowly With `maxUnavailable: 1` and a 2-minute readiness warm-up, a node with 25 pods of that workload takes ~50 minutes. `disruptionsAllowed` cycling between 0 and 1 and a falling pod count on the node prove progress. Distinguish this before doing anything destructive. ## Ordered playbook 1. `kubectl get pdb -A` → which budget is at zero. 2. Are the matching pods Ready? If not, that is the cause. 3. Is `desiredHealthy == expectedPods`? Then the budget is unsatisfiable by construction. 4. Are replacement pods Pending? Then it is a capacity/scheduling problem. 5. Otherwise watch for two minutes — it may be pacing normally. Actions in increasing order of risk: **scale the workload up** → **fix readiness** → **amend the PDB** (raise maxUnavailable / add `AlwaysAllow`) → **temporarily delete the PDB** → **`--disable-eviction` or delete pods directly**. The last two discard the protection entirely and should be a conscious, time-boxed decision with the service owner, not a reflex. ## Preventing the recurrence Validate at admission that no PDB is unsatisfiable (`maxUnavailable: 0`, or `minAvailable` ≥ the workload's `minReplicas`), require budgets to be expressed relative to the fleet, standardize `unhealthyPodEvictionPolicy: AlwaysAllow`, and alert on any PDB sitting at `disruptionsAllowed: 0` for longer than a few minutes — that alert fires long before an upgrade window, which is exactly when you want to hear about it.
- A drain is blocked and every pod of the workload is Ready with 5 replicas and `minAvailable: 5`. What is the least risky way to unblock it?Scale the Deployment up — for example to 7 replicas — so currentHealthy exceeds desiredHealthy and disruptions become allowed. This unblocks the drain while preserving the availability guarantee the budget was written to express, unlike deleting or relaxing the PDB. Afterwards, correct the budget to a fleet-relative form such as `maxUnavailable: 1`.
- How does `unhealthyPodEvictionPolicy: AlwaysAllow` change a blocked drain?With the default `IfHealthyBudget`, a running-but-not-Ready pod can only be evicted if the budget is currently healthy — so a broken workload pins disruptionsAllowed at zero and blocks maintenance indefinitely. `AlwaysAllow` permits evicting those unready pods regardless of the budget, on the reasoning that they are serving no traffic anyway, which breaks the deadlock without weakening protection for healthy pods.
saying these in an interview costs you the question
- Deleting the PodDisruptionBudget as a first response instead of diagnosing
- Not checking pod readiness, and assuming the budget arithmetic must be wrong
- Overlooking that replacement pods stuck in Pending prevent the budget from ever recovering
- Reaching for --disable-eviction routinely rather than as an explicit, agreed last resort
- Missing overlapping PDBs, which refuse eviction outright no matter what the numbers say