☸️ Day 3 of 30 — Deployments & ReplicaSets
Kubernetes Day 3: Deployments & ReplicaSets
Deployment vs ReplicaSet vs Pod, Auto Healing, and a full hands-on demo
📅 March 2026 · ☸️ Kubernetes Preparation Series · Day 3 of 30
#Kubernetes
#KubernetesDeployment
#ReplicaSet
#AutoHealing
#DevOps
#CloudNative
#CKAPrep
#LearnK8s
1. Deployment vs ReplicaSet vs Pod — The Hierarchy
On Day 2 we learned that a Pod is the smallest deployable unit in Kubernetes. But in real production systems, you never create bare Pods. You create a Deployment, which creates a ReplicaSet, which creates and manages your Pods. These three objects form a hierarchy — each layer adds a responsibility the one below it cannot handle.
🚀 Deployment
Manages updates, rollbacks & history
↓ creates & owns
📦 ReplicaSet
Ensures N replicas are always running
↓ creates & owns
🟡 Pod (×3)
Runs your containers on a node
↓ contains
🐳 Container
Your actual application process
When you create a Deployment with replicas: 3, here is the exact chain of events:
- You apply the Deployment YAML → API Server stores it in etcd.
- The Deployment Controller sees the new Deployment → creates a ReplicaSet matching the Pod template.
- The ReplicaSet Controller sees it needs 3 Pods → creates 3 Pod objects.
- The Scheduler assigns each Pod to the best available node.
- The kubelet on each node pulls the image and starts the containers.
🔑 One Deployment, one ReplicaSet, many Pods. When you do a rolling update, the Deployment creates a second ReplicaSet with the new version and gradually scales it up while scaling the old one down. This is how zero-downtime updates work.
Side-by-side comparison
| Feature | Pod | ReplicaSet | Deployment |
| Purpose | Run containers | Keep N Pods alive | Manage RS + updates |
| Self-healing | ✗ None | ✓ Yes | ✓ Yes |
| Scaling | ✗ Manual only | ✓ Via replicas field | ✓ Via replicas field |
| Rolling updates | ✗ No | ✗ No | ✓ Yes |
| Rollback | ✗ No | ✗ No | ✓ Yes — full history |
| Update history | ✗ No | ✗ No | ✓ Revision tracking |
| Used directly? | Debugging only | Rarely — prefer Deployment | ✓ Always in production |
✅ Simple rule to remember: Pod = what runs. ReplicaSet = how many run. Deployment = how they change over time.
2. What is a ReplicaSet?
A ReplicaSet has one job and it does it obsessively: make sure the desired number of Pod replicas are always running. It watches the cluster continuously. If a Pod dies, it creates a new one. If there are too many Pods (say, you manually created an extra one with the same labels), it deletes the excess.
How a ReplicaSet identifies its Pods — the selector
A ReplicaSet does not track Pods by name. It uses a label selector. Any Pod in the cluster that matches the selector is considered "owned" by the ReplicaSet. This is powerful — and it is a common source of confusion for beginners.
⚠️ The label trap: If you manually create a Pod with the same labels as a ReplicaSet's selector, the ReplicaSet will immediately delete one of its existing Pods because it now sees too many matching Pods. Labels are ownership, not just metadata.
ReplicaSet YAML
apiVersion: apps/v1 # Note: apps/v1, not v1
kind: ReplicaSet
metadata:
name: nginx-rs
spec:
replicas: 3 # Keep 3 Pods running at all times
selector:
matchLabels:
app: nginx # Own any Pod with this label
template: # Blueprint for new Pods
metadata:
labels:
app: nginx # MUST match selector above
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
Why not use ReplicaSet directly?
You could — but you would lose the most important feature: rolling updates. When you change the image in a ReplicaSet, it does NOT automatically update running Pods. You have to delete and recreate them manually, which means downtime. A Deployment wraps the ReplicaSet and handles this for you automatically. That is why in practice, you always use Deployments.
💡 Quick check: Run kubectl get rs after creating a Deployment — you will see a ReplicaSet with an auto-generated name like nginx-deployment-7d4b8c9f6. The suffix is the hash of the Pod template, which is how Kubernetes tracks which RS belongs to which version of the Deployment.
3. What is a Deployment?
A Deployment is the standard way to run stateless applications in Kubernetes. It is a higher-level object that manages ReplicaSets on your behalf and adds three critical capabilities that ReplicaSets lack: rolling updates, rollbacks, and revision history.
Think of a Deployment as a contract with Kubernetes: "I want this application running at this image version, with this many replicas, updated this way." Kubernetes honours that contract continuously — not just at creation time, but forever.
Deployment YAML — the production standard
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
strategy: # How to replace old Pods with new ones
type: RollingUpdate # Default — zero downtime
rollingUpdate:
maxSurge: 1 # Max extra Pods above desired during update
maxUnavailable: 0 # Never let replicas drop below desired
revisionHistoryLimit: 5 # Keep last 5 ReplicaSets for rollback
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "250m"
livenessProbe: # Is the app alive?
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe: # Is the app ready for traffic?
httpGet:
path: /
port: 80
initialDelaySeconds: 3
periodSeconds: 5
Update strategies — RollingUpdate vs Recreate
✅ Recommended
RollingUpdate
Replaces old Pods with new ones gradually. At no point does your app go fully offline. Use maxSurge and maxUnavailable to control the pace. Default strategy — always use this for web apps.
⚠️ Use with caution
Recreate
Kills ALL old Pods first, then creates new ones. Causes downtime. Only use when your app cannot run two versions simultaneously — for example, apps that hold an exclusive DB lock.
4. Auto Healing — How Kubernetes Keeps Your App Alive
Auto healing is one of the most powerful features of Kubernetes and the main reason teams move from bare Docker to K8s for production. The idea is simple: you tell Kubernetes your desired state, and Kubernetes continuously works to maintain it — no matter what happens.
This is not magic. It is the reconciliation loop running inside the Controller Manager on the Control Plane, every few seconds, forever:
# The reconciliation loop (simplified):
loop every few seconds:
desired = read from etcd # e.g. replicas: 3
actual = count running Pods # e.g. 2 (one crashed)
if actual < desired:
create (desired - actual) new Pods
elif actual > desired:
delete (actual - desired) excess Pods
else:
nothing to do — all good ✅
What triggers auto healing?
| Failure scenario | What Kubernetes does | Time to heal |
| Container process crashes (exit code ≠ 0) | kubelet restarts the container on the same Pod immediately | Seconds |
| Liveness probe fails repeatedly | kubelet kills and restarts the container | Seconds |
| Pod is manually deleted | ReplicaSet creates a replacement Pod | 5–10 seconds |
| Node goes offline / crashes | Node Controller marks node NotReady → Pods rescheduled on healthy nodes | ~5 minutes |
| Pod evicted (node running low on memory) | ReplicaSet creates replacement on another node | 10–30 seconds |
| Accidental kubectl delete deployment | Nothing — Deployment is gone. Always use RBAC to prevent this! | N/A |
Auto healing in action — visualised
🔄 Auto Healing Sequence — nginx-deployment (replicas: 3)
1
Initial state — 3 Pods running
2
nginx-def crashes — process exits with error
3
ReplicaSet detects: desired=3, actual=2 → creates new Pod
4
Healed — back to 3 running replicas. No human action needed.
✅ This is the "no 3 AM pager alert" feature. A container crash used to mean an on-call engineer waking up at 3 AM to ssh into a server and restart a process. With Kubernetes auto healing, minor crashes heal automatically before any human even notices.
5. Rolling Updates and Rollbacks
Updating a running application without downtime is one of the hardest problems in operations. Kubernetes Deployments solve it with rolling updates — the most important daily operation you will perform.
How a rolling update works under the hood
Before update (v1)
nginx:1.25 — Pod 1 ✅
nginx:1.25 — Pod 2 ✅
nginx:1.25 — Pod 3 ✅
During update (v1 → v2)
nginx:1.25 — Pod 2 ✅
nginx:1.25 — Pod 3 ✅
nginx:1.26 — Pod 4 🔄
Kubernetes creates a new ReplicaSet for the new version, gradually increases its replica count, and decreases the old ReplicaSet's count — until the old RS has 0 Pods and the new RS has all 3. At every step, your app stays available.
Rolling update commands
# Update the image to a new version
kubectl set image deployment/nginx-deployment nginx=nginx:1.26
# Watch the rollout happen in real time
kubectl rollout status deployment/nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish...
1 out of 3 new replicas have been updated...
2 out of 3 new replicas have been updated...
3 out of 3 new replicas have been updated...
deployment "nginx-deployment" successfully rolled out
# See rollout history — all revisions
kubectl rollout history deployment/nginx-deployment
REVISION CHANGE-CAUSE
1 <none>
2 <none>
# Add a change cause note (best practice — helps you recall why each change was made)
kubectl annotate deployment/nginx-deployment \
kubernetes.io/change-cause="Updated nginx to 1.26 for security patch"
Rollback — when things go wrong
# Something is broken — roll back to the previous version immediately
kubectl rollout undo deployment/nginx-deployment
deployment.apps/nginx-deployment rolled back
# Roll back to a specific revision number
kubectl rollout undo deployment/nginx-deployment --to-revision=1
# Pause a rollout (to inspect mid-way)
kubectl rollout pause deployment/nginx-deployment
# Resume it
kubectl rollout resume deployment/nginx-deployment
🔑 Rollback is instant because Kubernetes kept the old ReplicaSet around (controlled by revisionHistoryLimit). It does not rebuild anything — it just scales the old RS back up and scales the new one down. This is why keeping your revision history is important.
6. Full Hands-on Demo — From Scratch to Auto Healing
Let us now put everything together in a real end-to-end demo. Make sure your Minikube cluster is running (minikube start) before you begin.
Step 1 — Create the Deployment YAML
Save this as nginx-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
Step 2 — Apply and observe
# Apply the Deployment
kubectl apply -f nginx-deployment.yaml
deployment.apps/nginx-deployment created
# Watch Pods appear in real time (-w = watch mode)
kubectl get pods -w
NAME READY STATUS RESTARTS AGE
nginx-deployment-7d4b8c9f6-2xkpq 0/1 ContainerCreating 0 2s
nginx-deployment-7d4b8c9f6-9fvlm 0/1 ContainerCreating 0 2s
nginx-deployment-7d4b8c9f6-rnbtp 0/1 ContainerCreating 0 2s
nginx-deployment-7d4b8c9f6-2xkpq 1/1 Running 0 8s
nginx-deployment-7d4b8c9f6-9fvlm 1/1 Running 0 9s
nginx-deployment-7d4b8c9f6-rnbtp 1/1 Running 0 10s
# See the Deployment status
kubectl get deployment nginx-deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 30s
# See the ReplicaSet that was auto-created
kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-7d4b8c9f6 3 3 3 30s
Step 3 — Test auto healing (the fun part)
# Open a second terminal and watch Pods continuously
kubectl get pods -w
# In your first terminal — delete one Pod manually
kubectl delete pod nginx-deployment-7d4b8c9f6-2xkpq
# Watch what happens in terminal 2:
nginx-deployment-7d4b8c9f6-2xkpq 1/1 Terminating 0 2m
nginx-deployment-7d4b8c9f6-hj9kp 0/1 Pending 0 0s # ← NEW Pod!
nginx-deployment-7d4b8c9f6-hj9kp 0/1 ContainerCreating 0 1s
nginx-deployment-7d4b8c9f6-hj9kp 1/1 Running 0 6s # ← Healed ✅
# Still 3 Pods running — ReplicaSet replaced the deleted one
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-7d4b8c9f6-9fvlm 1/1 Running 0 3m
nginx-deployment-7d4b8c9f6-rnbtp 1/1 Running 0 3m
nginx-deployment-7d4b8c9f6-hj9kp 1/1 Running 0 15s
Step 4 — Scale up and down
# Scale up to 5 replicas
kubectl scale deployment nginx-deployment --replicas=5
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-7d4b8c9f6-9fvlm 1/1 Running 0 4m
nginx-deployment-7d4b8c9f6-rnbtp 1/1 Running 0 4m
nginx-deployment-7d4b8c9f6-hj9kp 1/1 Running 0 1m
nginx-deployment-7d4b8c9f6-mn3pq 1/1 Running 0 5s # ← new
nginx-deployment-7d4b8c9f6-qr7st 1/1 Running 0 5s # ← new
# Scale back down to 2
kubectl scale deployment nginx-deployment --replicas=2
# Alternatively, edit the YAML and re-apply (preferred — keeps YAML as source of truth)
kubectl apply -f nginx-deployment.yaml
Step 5 — Perform a rolling update
# Update image from nginx:1.25 to nginx:1.26
kubectl set image deployment/nginx-deployment nginx=nginx:1.26
# Watch the rolling update — old Pods terminate as new ones come up
kubectl rollout status deployment/nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish...
1 out of 3 new replicas have been updated...
2 out of 3 new replicas have been updated...
3 out of 3 new replicas have been updated...
deployment "nginx-deployment" successfully rolled out
# Simulate a bad deploy — use an image that does not exist
kubectl set image deployment/nginx-deployment nginx=nginx:BROKEN
kubectl rollout status deployment/nginx-deployment
Waiting for deployment... (1/3 updated)
error: deployment "nginx-deployment" exceeded its progress deadline
# ROLLBACK immediately — back to nginx:1.26
kubectl rollout undo deployment/nginx-deployment
deployment.apps/nginx-deployment rolled back
# Verify all Pods are running the good version again
kubectl get pods -o jsonpath='{.items[*].spec.containers[*].image}'
nginx:1.26 nginx:1.26 nginx:1.26
Step 6 — Clean up
# Delete everything created in this demo
kubectl delete -f nginx-deployment.yaml
deployment.apps "nginx-deployment" deleted
# Verify — no Pods should remain
kubectl get pods
No resources found in default namespace.
# Stop Minikube when done for the day (saves laptop resources)
minikube stop
✅ Always clean up with kubectl delete -f <file> rather than deleting individual Pods. Deleting the Deployment removes the ReplicaSet and all its Pods in one command — the right way to tear down a workload.
📌 Day 3 Recap — What You Learned
- The hierarchy: Deployment → ReplicaSet → Pod → Container. Each layer adds a responsibility. Always use Deployments in production — never bare Pods or standalone ReplicaSets.
- ReplicaSet: Watches the cluster and keeps your desired replica count running at all times using label selectors. Creates new Pods when existing ones die.
- Deployment: Wraps a ReplicaSet and adds rolling updates, rollback capability, and revision history. The
strategy field controls how updates happen — RollingUpdate (zero downtime) or Recreate (with downtime).
- Auto healing: The reconciliation loop runs continuously. Crashed containers, deleted Pods, and evicted workloads are all replaced automatically without human intervention.
- Rolling update:
kubectl set image triggers a rolling update. New Pods replace old ones gradually — your app stays live throughout. kubectl rollout undo rolls back instantly.
- Demo: You applied a Deployment, watched auto healing replace a deleted Pod in seconds, scaled replicas up and down, and performed a rolling update with rollback.
🚀 Day 4 preview: Your Pods are running and self-healing — but how do users reach them? Tomorrow we cover Kubernetes Services — ClusterIP, NodePort, and LoadBalancer — and how Kubernetes handles networking and service discovery between Pods.
Comments
Post a Comment