Warrior, now we're entering production deployment territory.
So far we've learned how Kubernetes runs, scales, secures, monitors, and troubleshoots applications.
But there's a major production question:
How do I deploy version 2 of my application without taking version 1 offline?
That's what this chapter is about.
Suppose your current production application is:
node-api:v1You want to deploy:
node-api:v2A naive approach would be:
Stop v1
β
Deploy v2
β
Start v2During this period:
Users
β
β Application unavailableThis is downtime.
Kubernetes Deployments are designed to support controlled replacement of old Pods with new Pods.
A Rolling Update gradually replaces old Pods with new Pods.
Suppose you have:
3 replicasCurrent:
v1 v1 v1During the rollout:
v1 v1 v2then:
v1 v2 v2then:
v2 v2 v2So users can continue receiving traffic while the new version is rolled out.
Remember:
Deployment
β
ReplicaSet
β
PodsWhen you update the image:
Deployment
β
ββββββββββββββββ
β β
Old ReplicaSet New ReplicaSet
β β
v1 Pods v2 PodsThe Deployment controls the transition.
Suppose your Deployment currently has:
spec:
replicas: 3
template:
spec:
containers:
- name: node-api
image: node-api:1.0You release:
node-api:2.0Change:
image: node-api:2.0Then:
kubectl apply -f deployment.yamlKubernetes starts the rollout.
Run:
kubectl rollout status deployment/node-apiYou might see:
Waiting for deployment "node-api" rollout to finish...
Waiting for deployment "node-api" rollout to finish...
deployment "node-api" successfully rolled outYou can also watch Pods:
kubectl get pods -w-w means:
Watch for changes.
You might see:
node-api-v1-abc Running
node-api-v1-def Running
node-api-v1-ghi Running
node-api-v2-xyz ContainerCreating
node-api-v2-xyz Running
node-api-v1-abc TerminatingSuppose initially:
Deployment
β
ReplicaSet v1
β
3 PodsAfter updating:
Deployment
β
βββ ReplicaSet v1
β βββ Pod v1
β βββ Pod v1
β βββ Pod v1
β
βββ ReplicaSet v2
βββ Pod v2
βββ Pod v2During the rollout, both ReplicaSets can temporarily exist.
Eventually:
Deployment
β
ReplicaSet v2
β
3 PodsThe old ReplicaSet is scaled down.
A Deployment normally uses:
strategy:
type: RollingUpdateYou can configure:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1These two settings are extremely important.
maxUnavailable?#Suppose:
replicas = 3and:
maxUnavailable: 1This means Kubernetes can have at most approximately one fewer available replica than the desired count during the rollout, subject to Deployment rollout behavior.
So instead of allowing all 3 to disappear:
v1 v1 v1Kubernetes maintains availability while replacing them.
maxSurge?#maxSurge controls how many Pods can temporarily exist above the desired replica count during the rollout.
Suppose:
replicas = 3
maxSurge = 1Kubernetes can temporarily have:
4 PodsFor example:
v1
v1
v1
v2 β newThen eventually:
v1
v1
v2
v2and finally:
v2
v2
v2maxSurge + maxUnavailable#Suppose:
replicas = 3
maxSurge = 1
maxUnavailable = 1A simplified rollout could look like:
START
v1 v1 v1Create one extra v2:
v1 v1 v1 v2Once the new Pod is ready, remove an old one:
v1 v1 v2Create another v2:
v1 v1 v2 v2Remove old:
v1 v2 v2Create final:
v1 v2 v2 v2Remove final old:
v2 v2 v2The exact sequence depends on readiness, availability and rollout state, but this gives you the correct mental model.
This connects directly to Chapter 14.
Imagine new Pod starts:
v2but the application isn't actually ready.
If Kubernetes immediately sends traffic to it:
Users
β
v2
β
β FailureWith a readiness probe:
v2 starts
β
Readiness probe
β
Not Ready
β
No normal Service trafficOnce healthy:
Readiness = success
β
Pod becomes Ready
β
Service can route trafficTherefore:
Readiness probes are a critical part of safe rolling deployments.
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-api
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
selector:
matchLabels:
app: node-api
template:
metadata:
labels:
app: node-api
spec:
containers:
- name: node-api
image: node-api:2.0
ports:
- containerPort: 3000
readinessProbe:
httpGet:
path: /ready
port: 3000
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 3000
periodSeconds: 10This combines several concepts you've already learned:
Deployment
+
RollingUpdate
+
Replicas
+
Readiness
+
LivenessmaxUnavailable: 0 Can Be Useful#For applications where you want all existing replicas to remain available during rollout:
maxUnavailable: 0combined with:
maxSurge: 1means Kubernetes can create extra capacity before removing old Pods.
For example:
3 desired
v1 v1 v1Create:
v1 v1 v1 v2Wait until v2 becomes Ready.
Then remove v1:
v1 v1 v2And continue.
This can reduce availability risk, but it temporarily requires additional capacity.
maxSurge Has a Cost#Suppose:
100 replicasand:
maxSurge: 50During rollout, you may temporarily need:
150 PodsThat means:
More CPU
More memory
More nodes potentially
More costSo deployment configuration is a tradeoff between:
Availability
β
Resource usage
β
Deployment speedYou can use percentages.
Example:
maxUnavailable: 25%
maxSurge: 25%For:
replicas = 20approximately:
25% = 5 PodsSo the rollout can use a surge/unavailability budget based on the desired replica count.
This is an important interview point.
Someone might say:
"Kubernetes rolling updates guarantee zero downtime."
That's too strong.
Zero downtime depends on several things:
Enough replicas
+
Readiness probes
+
Healthy application
+
Correct Service
+
Correct rollout strategy
+
Sufficient cluster capacity
+
Backward-compatible changes
+
Proper termination behaviorFor example:
3 replicasbut all three depend on one database migration that breaks the old version.
You can still have downtime.
When Kubernetes terminates a Pod, your application should gracefully stop accepting new work and finish active requests when possible.
For Node.js:
process.on("SIGTERM", async () => {
console.log("SIGTERM received");
server.close(() => {
console.log("HTTP server closed");
process.exit(0);
});
});Conceptually:
Pod termination
β
SIGTERM
β
Node.js receives signal
β
Stop accepting new requests
β
Finish active requests
β
ExitThis is very important for production deployments.
terminationGracePeriodSeconds#Kubernetes gives a Pod a grace period for termination.
Example:
spec:
terminationGracePeriodSeconds: 30Conceptually:
SIGTERM
β
Grace period
β
Application shuts downIf the process doesn't terminate within the grace period, Kubernetes can eventually force termination.
You can also configure a lifecycle hook:
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- "sleep 5"But don't blindly add sleeps.
The goal should be:
Give your application and networking enough time to transition cleanly.
A real graceful-shutdown implementation is preferable to using arbitrary delays as a substitute for correct lifecycle handling.
Imagine:
Service
β
βββ v1 Pod
βββ v1 Pod
βββ v2 PodService doesn't care that these are different versions.
It selects Pods using labels.
As Pods become Ready/unready:
Service endpoints
β
Ready PodsDuring a rollout:
v1 β v2the Service gradually sees the new Pods become available and old Pods disappear.
Kubernetes keeps Deployment revision information.
Check:
kubectl rollout history deployment/node-apiExample:
deployment.apps/node-api
REVISION CHANGE-CAUSE
1 <none>
2 <none>
3 <none>You can inspect a revision:
kubectl rollout history deployment/node-api --revision=2Suppose:
v1 β v2You deploy v2.
Everything looks fine.
Then users report:
Checkout brokenYou can rollback:
kubectl rollout undo deployment/node-apiKubernetes moves the Deployment back toward its previous revision.
Then:
kubectl rollout status deployment/node-apiYou can specify:
kubectl rollout undo deployment/node-api --to-revision=2Then:
kubectl rollout status deployment/node-apiDon't just execute:
kubectl rollout undo deployment/node-apiThen assume everything is fixed.
Check:
kubectl get podsThen:
kubectl rollout status deployment/node-apiThen:
kubectl logs <pod>And ideally:
Error rate β
Latency normal
Pods Ready
Application functionalSuppose:
Revision 1
Revision 2
Revision 3Kubernetes can retain old ReplicaSets so that previous Deployment revisions can be rolled back.
You can see them:
kubectl get rsExample:
node-api-abc123
node-api-def456
node-api-ghi789The current ReplicaSet has active replicas; old ones can remain with zero replicas depending on revision history settings.
revisionHistoryLimit#You can control how many old ReplicaSets are retained:
spec:
revisionHistoryLimit: 10This helps balance:
Rollback history
vs
Resource/object clutterRolling update isn't the only deployment strategy.
Another strategy is:
Blue-Green Deployment
Imagine:
Blue = v1
Green = v2You run both versions simultaneously.
Service
β
β
Blue v1Then deploy:
Blue v1
Green v2Test Green.
If everything is good, switch the Service:
Service
β
β
Green v2Now traffic goes to v2.
Internet
β
β
Service
β
ββββββββ΄βββββββ
β β
Blue v1 Green v2Initially:
Service β BlueAfter switch:
Service β GreenThe major advantage:
Very fast traffic switching and rollback.
If v2 is broken:
Service β Blue v1again.
But there is a cost:
v1 resources
+
v2 resourcesYou may temporarily need roughly double the application capacity.
Another strategy:
Canary deployment
Instead of sending everyone to v2:
100% β v1you gradually expose v2:
95% β v1
5% β v2Then:
90% β v1
10% β v2Then:
50% β v1
50% β v2Eventually:
0% β v1
100% β v2Suppose v2 has a bug affecting only certain requests.
With a normal rolling update, the new version may spread across the cluster.
With a canary:
Small traffic
β
v2
β
Monitor
β
Errors?
Latency?
Business metrics?If something goes wrong:
Stop rolloutand keep most users on v1.
| Strategy | Idea | Resource Usage | Rollback |
|---|---|---|---|
| Rolling | Gradually replace Pods | Lower | Relatively straightforward |
| Blue-Green | Run both versions | Higher | Very fast |
| Canary | Gradually shift traffic | Variable | Stop/shift traffic back |
Don't think one strategy is universally "best."
The right choice depends on:
Application
Traffic
Risk
Cost
Infrastructure
Release frequency
Rollback requirementsBasic Kubernetes Deployment rolling updates aren't automatically equivalent to sophisticated percentage-based canary routing.
For advanced canary deployments, teams often use:
For example:
Ingress
β
βββ 95% β v1
β
βββ 5% β v2Then gradually change the split.
This is one of the most important production topics.
Suppose v1 expects:
users.nameYou release v2 that expects:
users.first_name
users.last_nameYou immediately change the database:
name β first_name + last_nameNow:
v1 Pods
β
β Can't find nameEven though you're doing a rolling deployment.
A safer strategy is often:
Add new columns:
name
first_name
last_nameBoth versions can work.
Deploy application version that understands both schemas.
Backfill data.
Move traffic fully to new version.
Only later remove old schema elements.
This is often called an expand-and-contract approach.
Visualize:
OLD
βββ name
β
EXPAND
βββ name
βββ first_name
βββ last_name
β
NEW APP
Uses new fields
β
CONTRACT
Remove old nameThis makes rolling deployments safer because old and new application versions can coexist temporarily.
During a rolling update:
v1 + v2can coexist.
Therefore your database schema often needs to support:
v1
+
v2simultaneously.
This is a fundamental production deployment principle.
Avoid relying on:
image: node-api:latestfor controlled production releases.
Prefer:
node-api:1.0.0
node-api:1.1.0
node-api:1.2.0or immutable image digests:
node-api@sha256:...Why?
Because:
v1.0.0has a clear identity.
But:
latestcan change.
A realistic production workflow:
Developer
β
Git commit
β
CI pipeline
β
Run tests
β
Build Docker image
β
Push image to registry
β
Update Kubernetes manifest
β
Deployment rollout
β
New Pods
β
Startup Probe
β
Readiness Probe
β
Service traffic
β
Monitoring
β
Success / RollbackWe'll go much deeper into CI/CD in Chapter 30.
Before deploying:
β Image exists
β Image version is immutable
β Readiness probe works
β Liveness probe works
β Resources configured
β Enough replicas
β Cluster has capacity
β Database migration is compatible
β Configuration exists
β Secrets exist
β Monitoring exists
β Rollback procedure knownHere's a stronger example:
apiVersion: apps/v1
kind: Deployment
metadata:
name: node-api
spec:
replicas: 3
revisionHistoryLimit: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app: node-api
template:
metadata:
labels:
app: node-api
spec:
terminationGracePeriodSeconds: 30
containers:
- name: node-api
image: registry.example.com/node-api:2.1.0
ports:
- name: http
containerPort: 3000
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
startupProbe:
httpGet:
path: /health
port: 3000
periodSeconds: 5
failureThreshold: 12
readinessProbe:
httpGet:
path: /ready
port: 3000
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /health
port: 3000
periodSeconds: 10
failureThreshold: 3Notice how many previous chapters come together:
Deployment
Replicas
RollingUpdate
Resources
Startup Probe
Readiness
Liveness
Graceful termination
Versioned imageThat's the point of the course: concepts start connecting together.
kubectl apply -f deployment.yamlkubectl rollout status deployment/node-apikubectl rollout history deployment/node-apikubectl rollout history deployment/node-api --revision=2kubectl rollout undo deployment/node-apikubectl rollout undo deployment/node-api --to-revision=2kubectl rollout restart deployment/node-apiImportant:
rollout restartrestarts the Pods; it is not the same as rolling back to an older application version.
kubectl rollout restart vs undo#kubectl rollout restart deployment/node-apiMeans:
Recreate the Pods using the current Deployment specification.
kubectl rollout undo deployment/node-apiMeans:
Revert the Deployment toward a previous revision.
Very common interview question.
You can pause:
kubectl rollout pause deployment/node-apiThen make changes.
Resume:
kubectl rollout resume deployment/node-apiThis can be useful for controlled rollout workflows, though automated deployment systems often manage releases differently.
During deployments, you may have:
v1
v1
v2
v2Therefore:
v1 and v2 may need to work simultaneously.
That applies to:
API contracts
Database schemas
Message formats
Cache formats
Events
Environment variables
External integrationsThis is one of the biggest differences between:
"Works on my machine"and:
"Safe production deployment"A Deployment strategy that gradually replaces old Pods with new Pods while maintaining application availability according to the configured rollout constraints.
maxUnavailable?#It controls how many desired replicas may be unavailable during a rolling update.
maxSurge?#It controls how many additional Pods may temporarily be created above the desired replica count during a rolling update.
They prevent Pods that aren't ready from receiving normal Service traffic.
kubectl rollout status deployment/<name>kubectl rollout undo deployment/<name>kubectl rollout history deployment/<name>Rolling:
Gradually replace Pods.
Blue-Green:
Run two environments and switch traffic.Gradually expose a new version to a subset of traffic/users before increasing its traffic share.
Because old and new application versions may run simultaneously and may expect incompatible database schemas.
latest in production?#Because it doesn't provide a stable, immutable version identity and can make deployments/reproduction/rollback less predictable.
No. Availability depends on application design, readiness, replicas, capacity, traffic routing, graceful shutdown, compatible changes, and rollout configuration.
v1 v1 v1
β
v1 v1 v2
β
v1 v2 v2
β
v2 v2 v2strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1kubectl rollout status deployment/app
kubectl rollout history deployment/app
kubectl rollout undo deployment/app
kubectl rollout undo deployment/app --to-revision=2
kubectl rollout restart deployment/app
kubectl get rs
kubectl get pods -wRolling
β
Gradual replacement
Blue-Green
β
Two versions + traffic switch
Canary
β
Small traffic β new version β gradually increaseA production deployment isn't simply:
Docker image β KubernetesIt's:
NEW VERSION
β
β
Docker Image
β
β
Deployment
β
β
New ReplicaSet
β
β
New Pods
β
β
Startup Probe
β
β
Readiness Probe
β
β
Service
β
β
Users
β
β
Monitoring
β
βββββββ΄ββββββ
β β
Healthy Failure
β β
Continue RollbackAnd the most important production rule is:
A deployment is successful only when the new version is healthyβnot merely when the new Pods are created.
You now know:
maxUnavailablemaxSurgeThis is where Kubernetes YAML starts becoming much more manageable.
We'll learn:
Chart.yamlvalues.yamlhelm installhelm upgradehelm rollbackif conditionsMark it as complete to track your mastery.