This is where Kubernetes starts becoming really powerful.
So far you've learned:
Container
↓
PodBut we discovered a problem:
What happens if the Pod crashes?
And another:
What if I need 5 copies of my application?
And another:
How do I safely deploy version 2 of my application?
The answer is primarily:
A Kubernetes Deployment manages a set of identical application Pods.
Instead of saying:
"Run this Pod."
you say:
"I want 3 replicas of this application running."
For example:
Deployment
│
↓
3 replicas
│
├── Pod 1
├── Pod 2
└── Pod 3If one Pod disappears:
Pod 1 ✅
Pod 2 ❌
Pod 3 ✅The Deployment's controllers work to get back to:
Pod 1 ✅
Pod 3 ✅
Pod 4 ✅You learned in Chapter 5 that we can create:
kubectl run nginx --image=nginxThat creates a standalone Pod.
But if it dies:
Pod ❌there's no Deployment saying:
"I require one replica."
So it stays gone.
A Deployment changes the situation:
Deployment
Desired replicas = 3
Current:
Pod 1
Pod 2
Pod 3If Pod 2 disappears:
Current:
Pod 1
Pod 3
Desired:
3Kubernetes works toward:
Pod 1
Pod 3
Pod 4This hierarchy is extremely important:
Deployment
↓
ReplicaSet
↓
Pods
↓
ContainersFor example:
Deployment
│
↓
ReplicaSet
│
┌────────┼────────┐
↓ ↓ ↓
Pod 1 Pod 2 Pod 3
│ │ │
Container Container ContainerYou generally create the Deployment.
Kubernetes creates/manages the ReplicaSet and Pods.
A ReplicaSet maintains a specified number of Pod replicas.
Suppose:
replicas: 3The ReplicaSet's responsibility is essentially:
"Keep 3 matching Pods running."
So:
Desired = 3
Current = 3Everything is fine.
If:
Desired = 3
Current = 2it works toward creating another Pod.
Good question.
You could technically use a ReplicaSet directly.
But Deployment gives you higher-level application management.
Deployment provides things such as:
So the normal hierarchy is:
Deployment
↓
ReplicaSet
↓
PodsThink:
Deployment = application release manager
ReplicaSet = replica manager
Let's create an Nginx Deployment.
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.27
ports:
- containerPort: 80Don't worry about every line yet.
Focus on:
kind: Deploymentand:
replicas: 3Save it as:
deployment.yamlThen:
kubectl apply -f deployment.yamlYou'll get something similar to:
deployment.apps/nginx-deployment createdkubectl get deploymentsYou may see:
NAME READY UP-TO-DATE AVAILABLE
nginx-deployment 3/3 3 3Meaning:
Desired replicas = 3
Ready replicas = 3
Available = 3kubectl get podsYou might see:
NAME READY STATUS
nginx-deployment-7d8f9c7f5b-abc12 1/1 Running
nginx-deployment-7d8f9c7f5b-def34 1/1 Running
nginx-deployment-7d8f9c7f5b-ghi56 1/1 RunningNotice something interesting:
You didn't create these Pods manually.
The Deployment caused Kubernetes to create them.
Run:
kubectl get replicasetsor:
kubectl get rsYou'll see something like:
NAME DESIRED CURRENT READY
nginx-deployment-7d8f9c7f5b 3 3 3So:
Deployment
↓
nginx-deployment-7d8f9c7f5b
↓
3 Podsselector and labels#This is one of the most important concepts in Kubernetes.
You have:
selector:
matchLabels:
app: nginxAnd the Pod template has:
labels:
app: nginxThese need to match appropriately.
Think:
Deployment
↓
"I'm responsible for Pods labeled app=nginx"
↓
Pod
labels:
app=nginxLabels are how Kubernetes identifies and groups resources.
Labels are key-value metadata.
For example:
labels:
app: nginx
environment: production
team: backendA Pod might therefore have:
app=nginx
environment=production
team=backendLabels are used heavily by:
This is where things get interesting.
Suppose you have:
3 Podsand traffic increases.
You can manually scale:
kubectl scale deployment nginx-deployment --replicas=5Now:
Before:
Pod 1
Pod 2
Pod 3
After:
Pod 1
Pod 2
Pod 3
Pod 4
Pod 5Check:
kubectl get podsYou can also reduce replicas:
kubectl scale deployment nginx-deployment --replicas=2Now Kubernetes works toward:
Pod 1
Pod 2The extra Pods are terminated.
Instead of:
kubectl scale deployment nginx-deployment --replicas=5you can modify:
spec:
replicas: 5Then:
kubectl apply -f deployment.yamlThis is the declarative model:
YAML
↓
Desired state = 5
↓
Kubernetes
↓
5 PodsSuppose:
Deployment
replicas = 3
Pod A ✅
Pod B ✅
Pod C ✅Now you manually delete Pod B:
kubectl delete pod <pod-name>For a moment:
Pod A
Pod CBut Kubernetes sees:
Desired = 3
Current = 2So a new Pod appears:
Pod A
Pod C
Pod DThis is the self-healing behavior you learned earlier.
Remember:
Desired State
vs
Current StateThe Deployment/ReplicaSet controllers continuously work toward:
Current State = Desired StateFor example:
Desired = 3
Current = 2
↓
Create Pod
↓
Current = 3This reconciliation model is one of the core ideas behind Kubernetes.
Now we get to one of the biggest reasons Deployments are useful.
Suppose your application currently runs:
nginx:1.27You want:
nginx:1.28You don't necessarily want:
Stop everything
↓
Start new versionbecause that could cause downtime.
Instead, Kubernetes can perform a rolling update.
Conceptually:
OLD VERSION
Pod 1 → v1
Pod 2 → v1
Pod 3 → v1Gradually:
Pod 1 → v2
Pod 2 → v1
Pod 3 → v1Then:
Pod 1 → v2
Pod 2 → v2
Pod 3 → v1Finally:
Pod 1 → v2
Pod 2 → v2
Pod 3 → v2Change:
image: nginx:1.27to:
image: nginx:1.28Then:
kubectl apply -f deployment.yamlKubernetes detects that the Pod template changed.
It creates a new ReplicaSet for the new version and gradually transitions from the old ReplicaSet to the new one.
Conceptually:
Deployment
│
├── Old ReplicaSet
│ └── v1 Pods
│
└── New ReplicaSet
└── v2 PodsThis is a crucial detail:
A Deployment manages ReplicaSets, and during a rolling update it can use multiple ReplicaSets.
Run:
kubectl rollout status deployment/nginx-deploymentYou'll see progress such as:
Waiting for deployment to finish...
deployment successfully rolled outYou can see Deployment revisions:
kubectl rollout history deployment/nginx-deploymentThis helps track changes to the Deployment.
Suppose you deploy version 2:
v1 → v2But v2 has a bug.
You can roll back:
kubectl rollout undo deployment/nginx-deploymentConceptually:
v1
↓
v2 ❌
↓
rollback
↓
v1That's one of the major advantages of using Deployments.
Useful command:
kubectl describe deployment nginx-deploymentYou can see:
Let's revisit the 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.28
ports:
- containerPort: 80Let's break it down.
apiVersion#apiVersion: apps/v1The Deployment resource belongs to the apps/v1 API group/version.
kind#kind: DeploymentYou're creating a Deployment.
metadata#metadata:
name: nginx-deploymentThe Deployment's name.
replicas#replicas: 3Desired number of Pods.
selector#selector:
matchLabels:
app: nginxThe Deployment identifies its Pods using labels.
template#template:This defines the template from which Pods are created.
Inside it:
metadata:
labels:
app: nginxgives the Pods their label.
Then:
spec:
containers:defines the containers inside those Pods.
This YAML contains two metadata sections conceptually:
Deployment metadata
↓
Deployment name
Pod template metadata
↓
Pod labelsDon't confuse them.
| Pod | Deployment |
|---|---|
| Smallest deployable unit | Higher-level workload controller |
| Runs containers | Manages Pods |
| Can be ephemeral | Maintains desired replicas |
| Doesn't provide rolling updates itself | Supports rolling updates |
| Can be created directly | Usually preferred for stateless apps |
| Individual workload instance | Manages a set of replicas |
| Deployment | ReplicaSet |
|---|---|
| Higher-level abstraction | Lower-level controller |
| Manages ReplicaSets | Manages Pods |
| Rolling updates | Maintains replica count |
| Rollbacks | No application rollout strategy by itself |
| Usually created directly by developers | Usually managed by Deployment |
Think:
Deployment
"I want this version of my application."
↓
ReplicaSet
"I need 3 Pods for this version."
↓
Pods
"Run the containers."Deployments use a rolling update strategy by default.
Two useful parameters are:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1maxUnavailable#How many Pods can be unavailable during the update.
maxSurge#How many extra Pods can temporarily exist above the desired count.
For example:
Desired replicas = 3
maxSurge = 1Kubernetes may temporarily have:
4 Podsduring the update.
Suppose:
replicas = 3You don't necessarily want:
3 old Pods
↓
0 Pods
↓
3 new PodsInstead, you want a controlled transition:
3 old
↓
2 old + 1 new
↓
1 old + 2 new
↓
0 old + 3 newThis is the basic idea behind rolling updates.
A rolling update can reduce downtime, but a Deployment alone doesn't guarantee that users will never see failures.
For robust production deployments, you also need things like:
We'll connect these concepts later.
Imagine:
Node.js APIDocker image:
mycompany/node-api:v1Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-api
spec:
replicas: 3
selector:
matchLabels:
app: node-api
template:
metadata:
labels:
app: node-api
spec:
containers:
- name: node-api
image: mycompany/node-api:v1
ports:
- containerPort: 3000Kubernetes produces:
Deployment
↓
ReplicaSet
↓
┌──────────┬──────────┬──────────┐
Pod 1 Pod 2 Pod 3
│ │ │
Node.js Node.js Node.jsThen we'll eventually add:
Service
↓
Podsso users can access the application.
Earlier you asked:
Can Kubernetes increase Pods when traffic increases by 80%?
Now you can see where that fits.
Manual:
Deployment
↓
replicas = 3Automatic:
Deployment
↑
│
HPA
↑
MetricsFor example:
Traffic increases
↓
CPU increases
↓
HPA
↓
Deployment replicas
3 → 5
↓
More PodsWe'll study HPA much later.
Let's create a Deployment.
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.28
ports:
- containerPort: 80Apply:
kubectl apply -f deployment.yamlCheck:
kubectl get deploymentThen:
kubectl get replicasetsThen:
kubectl get podsYou should see:
Deployment
↓
ReplicaSet
↓
3 PodsRun:
kubectl get podsPick one Pod and delete it:
kubectl delete pod <pod-name>Immediately run:
kubectl get podsYou'll see something like:
Pod A Running
Pod B Terminating
Pod C Running
Pod D ContainerCreatingThen eventually:
Pod A Running
Pod C Running
Pod D RunningYou just witnessed Kubernetes reconciliation.
Run:
kubectl scale deployment nginx-deployment --replicas=5Then:
kubectl get podsYou should eventually have:
5 PodsScale back:
kubectl scale deployment nginx-deployment --replicas=3Change:
image: nginx:1.28to another valid Nginx version.
Then:
kubectl apply -f deployment.yamlWatch:
kubectl rollout status deployment/nginx-deploymentYou can also watch Pods:
kubectl get pods -w-w means watch.
You'll see Pods being replaced.
Press:
Ctrl + Cto stop watching.
Check history:
kubectl rollout history deployment/nginx-deploymentThen:
kubectl rollout undo deployment/nginx-deploymentCheck:
kubectl rollout status deployment/nginx-deploymentYou've now performed:
v1
↓
v2
↓
rollback
↓
v1This is the diagram I want you to remember:
Deployment
│
│
↓
ReplicaSet
│
┌──────────┼──────────┐
↓ ↓ ↓
Pod 1 Pod 2 Pod 3
│ │ │
↓ ↓ ↓
Container Container ContainerWhen updating:
Deployment
│
┌────────┴────────┐
↓ ↓
Old ReplicaSet New ReplicaSet
↓ ↓
v1 Pods v2 Pods
│ │
└──── gradually ──┘
↓
v2A Deployment manages the lifecycle and desired number of Pods and supports features such as rolling updates and rollbacks.
A controller that maintains the desired number of matching Pod replicas.
The ReplicaSet/Deployment controllers work to create a replacement Pod.
Gradually replacing old application Pods with new-version Pods instead of replacing everything simultaneously.
kubectl scale deployment <name> --replicas=5or declaratively:
replicas: 5followed by:
kubectl apply -f deployment.yamlkubectl rollout status deployment/<name>kubectl rollout undo deployment/<name>Deployment
↓
ReplicaSet
↓
Pod
↓
ContainerIf you remember only one thing from this chapter, remember:
You normally don't manage production Pods individually. You define the desired application state in a Deployment, and Kubernetes uses ReplicaSets to maintain the required Pods.
So instead of thinking:
"Create 3 Pods."think:
"I want 3 replicas of my application."Kubernetes handles the mechanics.
We now have a problem.
Suppose we have:
Deployment
↓
┌────┬────┬────┐
Pod Pod PodEach Pod has an IP.
But Pods can be destroyed and recreated, so their IPs can change.
How does your frontend reliably communicate with your backend?
How does a user access your application?
The answer is:
We'll learn:
Pod IP
Service IP
ClusterIP
NodePort
LoadBalancer
Selectors
Service Discovery
DNS
Port vs targetPort
Load balancing across PodsAnd we'll expose the Nginx/Node.js application we've been building.
Mark it as complete to track your mastery.