Deployments & ReplicaSets
Deployments & ReplicaSets
TL;DR
- Deployment doesn’t manage pods directly; it creates and manages ReplicaSets, which manage pods
- ReplicaSet ensures exact replica count via label selectors; Deployment adds rolling updates and history
- Rolling updates with
maxSurge=1, maxUnavailable=0maintain 100% capacity during upgrade (zero downtime) - Static image tags (
:latest,:prod) don’t trigger rollouts—spec unchanged, no reconciliation - Manually editing pod labels orphans the pod from its ReplicaSet; RS spins up replacement
- Rollback with
kubectl rollout undo deployment/app --to-revision=Nrestores historical ReplicaSets
Core Concepts
Controller Hierarchy
Deployment → ReplicaSet → Pods
Deployment: Manages rolling update strategies, release history, and version tracking. Doesn’t directly manage pods.
ReplicaSet: Lower-level controller guaranteeing exact replica count. Uses label selectors to monitor and adopt pods matching criteria.
Replication Controller (RC): Legacy predecessor to ReplicaSets. Supports only equality-based selectors (app=web); ReplicaSets add set-based selectors (In, NotIn). Don’t use RC.
Deployment Strategies
Recreate: Kill all old pods, spin up new ones. Downtime occurs; prevents concurrent versions (good for database schema changes).
RollingUpdate: Gradually replace old pods with new ones. Zero-downtime but requires backward compatibility.
maxSurge: Max pods above desired count during update (allows temporary overcapacity)maxUnavailable: Max pods that can be unavailable during update (maintains min capacity)
Example: maxSurge=1, maxUnavailable=0 maintains 100% capacity; one new pod starts before old pod dies.
Label Selectors
ReplicaSets use spec.selector to find and adopt pods. If pod labels change, pod becomes orphaned—RS creates replacement. Orphaned pod continues running unmanaged.
Comparison Table
| Aspect | ReplicaSet | Deployment |
|---|---|---|
| Manages pods directly | Yes | No (via ReplicaSets) |
| Supports rolling updates | No | Yes |
| Tracks revision history | No | Yes (via controller) |
| Handles rollbacks | No | Yes |
| Suitable for stateless apps | Yes | Yes (primary choice) |
| Suitable for stateful apps | Not recommended | No (use StatefulSet) |
Command & Configuration Reference
Deployment YAML Template
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-processor
labels:
app: payment-processor
spec:
replicas: 4
revisionHistoryLimit: 10 # Keep last 10 versions
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # 100% capacity maintained
selector:
matchLabels:
app: payment-processor
template:
metadata:
labels:
app: payment-processor
spec:
containers:
- name: app
image: payment-service:v1.1.0
ports:
- containerPort: 8080
Rollout Commands
# Apply deployment and monitor rollout
kubectl apply -f deployment.yaml
kubectl rollout status deployment/payment-processor
# Update image (imperative—discouraged in production)
kubectl set image deployment/payment-processor app=payment-service:v1.2.0
# View rollout history
kubectl rollout history deployment/payment-processor
# Rollback to previous version
kubectl rollout undo deployment/payment-processor
# Rollback to specific revision
kubectl rollout undo deployment/payment-processor --to-revision=2
# Pause rollout (for canary or manual validation)
kubectl rollout pause deployment/payment-processor
# Resume paused rollout
kubectl rollout resume deployment/payment-processor
Common Pitfalls
Pitfall: Static Image Tags Block Rollouts
Using :latest or :prod in deployment spec (e.g., image: payment-service:latest). After pushing new code with same tag, kubectl apply does nothing.
- Why: Deployment spec string unchanged; controller detects no diff; no reconciliation triggered.
- Fix: Use unique, immutable tags (semantic versions like
v1.2.0or commit hashes). Enable automatic rollouts via CI/CD that updates spec with new tag.
Pitfall: Orphaned Pods from Label Editing Manually editing a pod’s labels so they no longer match the ReplicaSet selector.
- Why: ReplicaSet detects mismatch via label selector; counts pod as missing; spins up replacement. Original pod continues running but unmanaged.
- Fix: Never manually edit pod labels. Modify the ReplicaSet selector to re-adopt orphaned pod, or delete the orphaned pod and let RS recreate it.
Pitfall: Downtime During Rolling Updates
Using default rolling update with maxUnavailable > 0 (e.g., maxUnavailable: 1) on single-replica deployments.
- Why: Old pod terminates before new pod starts; brief downtime occurs.
- Fix: Set
maxUnavailable: 0andmaxSurge: 1to start new pod before old pod dies, ensuring zero downtime.
Interview Questions
Q — Does a Deployment directly manage Pods? Explain the management chain. No. Deployment creates and updates ReplicaSets. ReplicaSets manage Pods using label selectors. When you update a Deployment spec, the Deployment controller creates a new ReplicaSet with the new spec. The old ReplicaSet scales down while the new ReplicaSet scales up, orchestrating the rolling update. This hierarchy allows Deployment to track version history and enable rollbacks.
Q — Explain rolling updates. What do maxSurge and maxUnavailable control?
Rolling updates gradually replace old pods with new ones. maxSurge limits pods created above the desired count (allows temporary overcapacity to start new pods before killing old ones). maxUnavailable limits pods that can be offline during update (maintains min capacity). Example: 4 replicas with maxSurge=1, maxUnavailable=0 means create 1 new pod (5 total), wait for it to be ready, then kill 1 old pod, maintaining 4 running always—zero downtime.
Q — You use image: nginx:latest in your Deployment. After pushing new code with the same tag, kubectl apply triggers no rollout. Why? How do you fix it
The Deployment spec string is unchanged (still says :latest), so kubectl apply detects no diff and doesn’t update anything. Kubernetes doesn’t re-pull images just because tag changed. Fix: Use unique, immutable image tags (semantic versions or commit hashes). Update the Deployment spec to reference the new tag. Example: change image: nginx:v1.0.0 to image: nginx:v1.1.0 so kubectl apply sees a spec change and triggers reconciliation.