🎓 Mission Debrief: PDB Blocks All Evictions¶
What Happened¶
Your PodDisruptionBudget (PDB) was configured with minAvailable: 3 for a deployment with 3 replicas.
This created an impossible requirement: "Keep all 3 pods available while trying to evict one" - mathematically impossible!
Result: All voluntary pod evictions were blocked, preventing node maintenance, cluster upgrades, and autoscaling operations.
How Kubernetes Behaved¶
PDB Evaluation Process:
Node drain requested (kubectl drain node-1)
↓
Kubernetes needs to evict pods on node-1
↓
Checks PDB for db-proxy pods
↓
PDB says: minAvailable = 3
Current healthy pods = 3
If we evict 1 pod: 3 - 1 = 2
Is 2 >= 3? NO ❌
↓
Eviction denied!
↓
Node drain FAILS
What operations were blocked:
❌ kubectl drain node → "Cannot evict pod: would violate PDB"
❌ Cluster upgrade → Cannot drain nodes
❌ Autoscaler scale-down → Cannot remove nodes
❌ Node maintenance → Stuck waiting
With fixed PDB (minAvailable: 2):
Node drain requested
↓
PDB says: minAvailable = 2
Current healthy pods = 3
If we evict 1 pod: 3 - 1 = 2
Is 2 >= 2? YES ✅
↓
Eviction allowed!
↓
Pod evicted, rescheduled on another node
↓
Node drain succeeds
The Correct Mental Model¶
PodDisruptionBudget Purpose¶
PDBs protect applications from voluntary disruptions:
| Voluntary Disruptions (PDB applies) | Involuntary Disruptions (PDB doesn't apply) |
|---|---|
kubectl drain |
Node crash |
| Cluster upgrades | Node hardware failure |
| Autoscaler scale-down | Pod killed by OOMKiller |
| Node decommissioning | Network partition |
PDBs ensure that during planned maintenance, you maintain minimum service availability.
minAvailable vs maxUnavailable¶
# Option 1: minAvailable (absolute number)
apiVersion: policy/v1
kind: PodDisruptionBudget
spec:
minAvailable: 2 # Keep at least 2 pods running
selector:
matchLabels:
app: myapp
# Option 2: minAvailable (percentage)
spec:
minAvailable: 67% # Keep at least 67% running
# Option 3: maxUnavailable (absolute number)
spec:
maxUnavailable: 1 # Allow at most 1 pod unavailable
# Option 4: maxUnavailable (percentage)
spec:
maxUnavailable: 33% # Allow at most 33% unavailable
Relationship (for 3 replicas):
minAvailable: 2 === maxUnavailable: 1
(keep 2) (lose 1)
minAvailable: 3 === maxUnavailable: 0
(keep all) ❌ (lose none) ❌
BLOCKS EVICTIONS!
Configuration Guidelines¶
| Replicas | minAvailable | maxUnavailable | Evictions Allowed | Use Case |
|---|---|---|---|---|
| 3 | 2 | 1 | 1 pod | Recommended |
| 3 | 3 | 0 | 0 pods ❌ | BLOCKS EVERYTHING |
| 5 | 3 | 2 | 2 pods | Good for larger deployments |
| 10 | 70% | 30% | 3 pods | Good for percentage-based |
Rule of thumb: Always allow at least 1 eviction!
Real-World Incident Example¶
Company: Financial services platform (24/7 uptime requirement) Impact: 6-hour cluster upgrade delay, missed security patch window Cost: Security compliance violation ($250K fine) + emergency weekend work
What happened:
The company had a critical Kubernetes cluster upgrade scheduled (security patches for a CVE). The SRE team started the rolling node upgrade at 2 AM (low-traffic window).
PDB configuration (set by a well-meaning developer):
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: payment-service-pdb
spec:
minAvailable: 5 # "Always keep all 5 pods for payment service!"
selector:
matchLabels:
app: payment-service
---
# Deployment had exactly 5 replicas
spec:
replicas: 5
Developer's logic: "Payment is critical, we need ALL 5 pods always running!"
The cascade (Saturday, 2 AM):
02:00 - Start cluster upgrade (using kops rolling-update)
02:01 - Attempt to drain first node (has 1 payment-service pod)
02:01 - kubectl drain node-1
02:01 - Eviction request for payment-service-xxx
02:01 - PDB check: minAvailable: 5, current: 5, after eviction: 4
02:01 - PDB denies eviction: "would violate policy"
02:01 - Node drain FAILS
02:01 - Automated upgrade pauses, alerts SRE
02:15 - SRE on-call investigates
02:30 - Identifies PDB blocking eviction
02:35 - Debates fixing PDB vs manual override
02:40 - Decides to temporarily delete PDB
02:41 - Delete PDB: kubectl delete pdb payment-service-pdb
02:42 - Resume node drain
02:45 - Node drain succeeds
02:50 - Payment pods rescheduled, all healthy
03:00 - Continue with upgrades
03:30 - Another node with payment pod...
03:30 - Realizes PDB was deleted, no protection!
03:35 - Recreate PDB with correct config (minAvailable: 4)
04:00 - Finish remaining nodes
08:00 - Upgrade complete (6 hours instead of planned 2 hours)
Why it was problematic: - Security window missed - Needed to patch before Monday, barely made it - Compliance violation - Required to patch within 48 hours of CVE disclosure - Emergency weekend work - Team worked all night - Risk period - 30 minutes with no PDB protection
The proper fix:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: payment-service-pdb
spec:
minAvailable: 4 # Allow 1 eviction, keep 4 running
# OR equivalently:
# maxUnavailable: 1
selector:
matchLabels:
app: payment-service
Process improvements implemented:
-
PDB validation in CI/CD:
-
Pre-upgrade PDB audit:
-
Documentation:
Lessons learned: 1. Test node drains before upgrades - Don't discover PDB issues during upgrade 2. PDBs should allow some evictions - Otherwise they block maintenance 3. Audit PDBs regularly - Check disruptionsAllowed status 4. Validate PDBs in CI/CD - Catch misconfigurations before deployment
Commands You Mastered¶
# List all PDBs
kubectl get pdb --all-namespaces
# Check PDB status
kubectl get pdb <name> -n <namespace>
# Look at ALLOWED column
# Describe PDB (see detailed status)
kubectl describe pdb <name> -n <namespace>
# Check if PDB allows any disruptions
kubectl get pdb <name> -n <namespace> -o jsonpath='{.status.disruptionsAllowed}'
# Edit PDB
kubectl edit pdb <name> -n <namespace>
# Test node drain (dry-run)
kubectl drain <node-name> --dry-run=server --ignore-daemonsets
# Actually drain a node
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
# If stuck, see which pods block drain
kubectl drain <node-name> --dry-run=server
# View all PDBs and their disruption allowance
kubectl get pdb --all-namespaces -o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name,MIN_AVAILABLE:.spec.minAvailable,MAX_UNAVAILABLE:.spec.maxUnavailable,ALLOWED:.status.disruptionsAllowed
Best Practices for PDBs¶
✅ DO:¶
-
Always allow some disruptions:
-
Use PDBs for all production deployments:
-
Test node drains before major operations:
-
Use percentages for large deployments:
-
Monitor PDB status:
❌ DON'T:¶
-
Don't set minAvailable == replicas:
-
Don't set maxUnavailable: 0:
-
Don't forget PDBs for critical services:
-
Don't use PDBs for single-replica deployments:
PDB Configuration Matrix¶
For Different Replica Counts¶
3 replicas (common for small services):
5 replicas (medium services):
10+ replicas (larger services):
Critical services (payment, auth):
What's Next?¶
You've learned how to configure PodDisruptionBudgets to protect applications while allowing necessary maintenance operations.
Next level: Blue-Green deployments! You'll learn how service selector misconfigurations can cause traffic routing issues.
Key takeaway: PDBs must allow at least 1 eviction, otherwise they block all maintenance operations. Balance protection with operational flexibility!