🎓 Mission Debrief: Zero-Downtime Deployment Failure¶
What Happened¶
Your deployment was configured with maxUnavailable: 100% and maxSurge: 0, which allowed Kubernetes to terminate all pods simultaneously during a rolling update.
This caused complete service outage every time you deployed a new version!
This is a critical production mistake that can turn routine deployments into incidents.
How Kubernetes Behaved¶
With broken config (maxUnavailable: 100%, maxSurge: 0):
Rolling update starts (3 replicas → new version)
↓
maxUnavailable: 100% means can terminate 100% of 3 = all 3 pods
maxSurge: 0 means cannot create extra pods
↓
Step 1: Terminate all 3 old pods ❌
↓
Step 2: Create 3 new pods
↓
Step 3: Wait for new pods to be ready
↓
Service has 0 pods for 10-30 seconds → DOWNTIME!
With fixed config (maxUnavailable: 1, maxSurge: 1):
Rolling update starts (3 replicas → new version)
↓
maxUnavailable: 1 means max 1 pod down (keep 2 running)
maxSurge: 1 means can create 1 extra pod (total 4)
↓
Step 1: Create 1 new pod (now 4 total)
Step 2: Wait for new pod to be ready
Step 3: Terminate 1 old pod (back to 3 total, 2 old + 1 new)
Step 4: Create another new pod (4 total again)
Step 5: Wait for it to be ready
Step 6: Terminate another old pod
Step 7-8: Repeat for last pod
↓
Service ALWAYS has at least 2 pods running → ZERO DOWNTIME! ✅
The Correct Mental Model¶
RollingUpdate Parameters Explained¶
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1 # or "25%" - Max pods that can be down
maxSurge: 1 # or "25%" - Max extra pods during rollout
maxUnavailable: Maximum number of pods that can be unavailable during the update.
With 3 replicas:
- maxUnavailable: 0 → Must keep all 3 running (requires maxSurge > 0)
- maxUnavailable: 1 → Can take down 1, keep 2 running (safe)
- maxUnavailable: 2 → Can take down 2, keep only 1 running (risky)
- maxUnavailable: 3 → Can take down all 3 (DOWNTIME!)
- maxUnavailable: 100% → Same as 3 (DOWNTIME!)
maxSurge: Maximum number of pods that can be created above desired replicas.
With 3 replicas:
- maxSurge: 0 → Cannot create extras (must terminate first)
- maxSurge: 1 → Can have 4 pods during rollout
- maxSurge: 2 → Can have 5 pods during rollout
- maxSurge: 100% → Can have 6 pods during rollout (2x)
Common Configuration Patterns¶
Conservative (safest, slower):
maxUnavailable: 0 # Never take down any pod
maxSurge: 1 # Create one new pod at a time
# Guarantees: 100% availability, slowest rollout
Balanced (recommended for most apps):
maxUnavailable: 1 # Take down 1 pod at a time
maxSurge: 1 # Allow 1 extra pod
# Guarantees: High availability, reasonable speed
Aggressive (faster, less safe):
maxUnavailable: 25% # Can take down 25% of pods
maxSurge: 25% # Can create 25% extra pods
# Guarantees: Faster rollout, some availability risk
DANGEROUS (causes downtime):
maxUnavailable: 100% # Can take down ALL pods ❌
maxSurge: 0 # Cannot create extras ❌
# Result: Complete outage during rollout!
Using Percentages¶
# For 10 replicas:
maxUnavailable: 25% # = 2.5 → rounds down to 2 pods
maxSurge: 25% # = 2.5 → rounds up to 3 pods
# For 3 replicas:
maxUnavailable: 33% # = 0.99 → rounds down to 0 pods
maxSurge: 33% # = 0.99 → rounds up to 1 pod
Rounding rules: - maxUnavailable: rounds down (conservative, keeps more pods) - maxSurge: rounds up (generous, allows more extra pods)
Real-World Incident Example¶
Company: SaaS CRM platform (100K business users) Impact: 8-minute complete outage during business hours Cost: $450K in SLA credits + major customer churn
What happened:
A DevOps engineer was trying to optimize resource usage. They noticed that during deployments, Kubernetes temporarily created extra pods (due to maxSurge: 1), using extra memory.
To "save resources," they changed the deployment strategy:
# Before (safe)
strategy:
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
# After (BROKEN)
strategy:
rollingUpdate:
maxUnavailable: 100% # "No downtime if we update fast!"
maxSurge: 0 # "No extra resource usage!"
The engineer's logic: "If we update really fast, there won't be downtime!"
The cascade (Tuesday, 2 PM):
14:00 - Deploy new feature (routine update)
14:00 - RollingUpdate starts for 'auth-service' (5 replicas)
14:00:05 - Kubernetes terminates ALL 5 pods (maxUnavailable: 100%)
14:00:06 - Starts creating 5 new pods
14:00:06 - Service has 0 healthy endpoints
14:00:06 - All users see "Service Unavailable"
14:00:08 - First new pod ready (2 seconds startup)
14:00:10 - Second pod ready
14:00:12 - Third pod ready
14:00:13 - Fourth pod ready
14:00:14 - All 5 pods ready
14:08 - Service fully restored
Why it was catastrophic: - Authentication service - When it's down, entire platform is unusable - Business hours - 2 PM = peak usage time - 100K users affected - All simultaneously logged out - No warning - Routine deployment caused incident - 8 minutes downtime - For what should be zero-downtime deployment
Customer impact:
14:00 - Users in middle of important sales calls
14:00 - CRM goes down, all data inaccessible
14:01 - Support tickets flood in (1,200 tickets in 1 minute)
14:02 - Social media complaints start
14:05 - Major customers (Fortune 500) threaten to switch
14:08 - Service restored
14:15 - Damage control begins
14:30 - CEO personally calls top 10 customers to apologize
The fix:
-
Immediate rollback of deployment strategy:
-
Deployment validation added to CI/CD:
-
Policy enforcement with OPA (Open Policy Agent):
Lessons learned: 1. Never sacrifice availability for resource savings - Extra pods during 30-second rollout are worth it 2. Test rollout strategies in staging - With actual traffic 3. Validate deployment configs - Automated checks in CI/CD 4. Monitor during deployments - Alert if healthy pods drop to zero 5. Know your critical services - Auth, API gateway need extra safety
Commands You Mastered¶
# Check deployment strategy
kubectl get deployment <name> -n <namespace> -o yaml | grep -A 5 strategy
# Edit deployment strategy
kubectl edit deployment <name> -n <namespace>
# Watch rollout in progress
kubectl rollout status deployment/<name> -n <namespace>
# Watch pods during rollout
kubectl get pods -n <namespace> -l app=<label> -w
# Trigger a rollout (for testing)
kubectl set image deployment/<name> <container-name>=new-image:tag -n <namespace>
# Pause a problematic rollout
kubectl rollout pause deployment/<name> -n <namespace>
# Resume after fixing
kubectl rollout resume deployment/<name> -n <namespace>
# Rollback if deployment went bad
kubectl rollout undo deployment/<name> -n <namespace>
# Check rollout history
kubectl rollout history deployment/<name> -n <namespace>
Best Practices for Rollout Strategies¶
✅ DO:¶
-
Use safe defaults:
-
For critical services, be extra conservative:
-
Use percentages for large deployments:
-
Add readiness probes (essential for RollingUpdate):
-
Monitor deployments:
❌ DON'T:¶
-
Never use maxUnavailable: 100%:
-
Don't set both to 0:
-
Don't skip readiness probes:
-
Don't use aggressive settings for critical services:
Rollout Strategy Decision Tree¶
Is this a critical service (auth, payment, core API)?
├─ YES: Use maxUnavailable: 0, maxSurge: 1
│ (Create new before removing old)
└─ NO: Continue ↓
Do you have many replicas (>20)?
├─ YES: Use percentages
│ maxUnavailable: 10%
│ maxSurge: 10%
└─ NO: Continue ↓
Do you have enough resources for extra pods?
├─ YES: Use maxUnavailable: 1, maxSurge: 1
│ (Balanced, recommended)
└─ NO: Use maxUnavailable: 1, maxSurge: 0
(Resource-constrained, slower rollout)
What's Next?¶
You've learned how to configure rollout strategies for zero-downtime deployments.
Next level: PodDisruptionBudgets! You'll learn how to protect your pods from being evicted during cluster maintenance.
Key takeaway: Always ensure maxUnavailable is less than your total replicas, and prefer having maxSurge > 0 for smoother rollouts!