Deployment strategies
RollingUpdate vs Recreate, and blue/green in practice.
A Deployment never upgrades a running Pod. When you push a new image, nothing reaches into the old container and swaps the binary out from under it. Kubernetes builds a fresh set of Pods, waits for them, and retires the old ones. A Pod is the smallest thing the cluster runs: one or more containers that share a network address and live and die together. The strategy field on a Deployment decides the choreography of that swap. How many new Pods come up before old ones go down, and whether both versions are ever alive at the same moment.
RollingUpdate vs Recreate
Two strategies ship in the box. RollingUpdate is the default, and it works like a relay race where the next runner is already sprinting alongside before the current one hands off the baton and peels away. New Pods start, they pass their health check, and only then do old Pods get terminated, a few at a time, until the whole fleet is the new version. The service stays up the entire time because there are always enough healthy Pods answering requests. Recreate is the opposite move. It kills every old Pod first, waits for them to fully die, then starts the new ones. You get a gap where nothing is serving, but you are guaranteed to never have two versions running together for even a second.
Here is the part that trips people up. A Deployment does not manage Pods directly. It manages ReplicaSets, and a ReplicaSet is the little controller that keeps a fixed number of copies of a Pod alive. Every time you change the Pod template (a new image, a new environment variable, anything at all), the Deployment controller creates a brand-new ReplicaSet and stamps its Pods with a pod-template-hash label so the two generations never get confused. A rollout is really just the controller scaling the new ReplicaSet up while scaling the old one down. Two dials govern the pace: maxSurge (how many extra Pods it may run above your desired count) and maxUnavailable (how many it is allowed to be short). Set maxUnavailable: 0 with maxSurge: 25% and you get the safe pattern, which adds new capacity first and never dips below full strength.
apiVersion: apps/v1kind: Deploymentmetadata:name: webspec:replicas: 4selector:matchLabels: { app: web }strategy:type: RollingUpdaterollingUpdate:maxSurge: 25% # up to 1 extra Pod (25% of 4) during the swapmaxUnavailable: 0 # never drop below 4 ready PodsminReadySeconds: 10 # a new Pod must stay Ready 10s before it countstemplate:metadata:labels: { app: web }spec:containers:- name: webimage: nginx:1.26ports: [{ containerPort: 80 }]readinessProbe:httpGet: { path: /, port: 80 }periodSeconds: 5
$ kubectl apply -f rolling.yamldeployment.apps/web created$ kubectl get deploy webNAME READY UP-TO-DATE AVAILABLE AGEweb 4/4 4 4 40s
Now roll a new version out and watch the controller work. kubectl set image edits the Pod template in place, which is exactly the change that spawns a new ReplicaSet and starts the swap.
$ kubectl set image deployment/web web=nginx:1.27deployment.apps/web image updated$ kubectl rollout status deployment/webWaiting for deployment "web" rollout to finish: 1 out of 4 new replicas have been updated...Waiting for deployment "web" rollout to finish: 2 out of 4 new replicas have been updated...Waiting for deployment "web" rollout to finish: 3 out of 4 new replicas have been updated...Waiting for deployment "web" rollout to finish: 3 of 4 updated replicas are available...deployment "web" successfully rolled out
Run kubectl get rs while that is happening and you can watch the two ReplicaSets trade places. The old one drains as the new one fills, and the pod-template-hash in each name is what keeps their Pods separate.
$ kubectl get rs -l app=webNAME DESIRED CURRENT READY AGEweb-6f8b9c7d5 2 2 2 11m # old, nginx:1.26, scaling downweb-7d4f9b8c6 3 3 2 18s # new, nginx:1.27, scaling up
One more control matters when things go wrong: progressDeadlineSeconds (default 600). If the new ReplicaSet cannot make progress in that window, say the new image crash-loops or never passes its readiness check, the Deployment stops waiting and sets a condition Progressing=False with reason ProgressDeadlineExceeded. That is your signal. kubectl rollout status exits non-zero, and kubectl describe deployment shows the reason. The important thing is the old ReplicaSet is still there and still serving traffic, so the service never went down. To bail out, kubectl rollout undo scales the previous ReplicaSet back up. Kubernetes keeps old ReplicaSets around for exactly this, capped by revisionHistoryLimit (default 10).
$ kubectl rollout status deployment/web --timeout=90sWaiting for deployment "web" rollout to finish: 1 out of 4 new replicas have been updated...error: deployment "web" exceeded its progress deadline$ kubectl rollout undo deployment/webdeployment.apps/web rolled back
To switch to Recreate you patch the strategy field, and there is a catch the cluster enforces: rollingUpdate and type: Recreate cannot both be set, so you have to null the old block out or the patch is rejected. Trigger a rollout afterward and watch the events. You will see the tell-tale order that proves versions never overlap: everything scales to zero first, then back up.
$ kubectl patch deployment web --type merge \-p '{"spec":{"strategy":{"type":"Recreate","rollingUpdate":null}}}'deployment.apps/web patched$ kubectl set image deployment/web web=nginx:1.28deployment.apps/web image updated$ kubectl describe deployment web | grep -A6 EventsEvents:Type Reason Age From Message---- ------ ---- ---- -------Normal ScalingReplicaSet 20s deployment-controller Scaled down replica set web-7d4f9b8c6 to 0 from 4Normal ScalingReplicaSet 6s deployment-controller Scaled up replica set web-5c9d7f6b4 to 4 from 0
Blue/green and canary: patterns you assemble
Neither blue/green nor canary is a strategy you can type into a Deployment. They are patterns you build from two Deployments and a Service. A Service is the stable front door for your app: it has one virtual IP address that never changes, and it forwards traffic to whichever Pods match its label selector. That selector is the switch you flip.
Blue/green means you run the current version (blue) and the new version (green) as two full Deployments at the same time, labelled so the Service can tell them apart. The Service points at blue. You test green privately, hitting its Pods directly, and when you are happy you flip the selector to green in a single edit. Cutover is instant because it is just a label change, and rollback is nothing more than flipping back. The bill for that safety is capacity: for a while you are paying to run two full copies of the app.
$ kubectl patch service web -p '{"spec":{"selector":{"app":"web","version":"green"}}}'service/web patched$ kubectl get endpoints webNAME ENDPOINTS AGEweb 10.244.1.31:80,10.244.2.44:80,10.244.3.12:80 23m
Canary is the cautious cousin, named after the caged bird miners once sent ahead to test the air. Instead of an all-at-once flip, you send a small slice of live traffic to the new version and watch the metrics. The crude version runs one green Pod next to nine blue Pods behind the same Service, so roughly 10% of requests land on green. The precise version hands traffic-splitting to an ingress controller (the component that routes outside traffic into the cluster) or a service mesh (a networking layer that sits between your services), either of which can weight traffic 95/5 and step it up on a schedule. Either way, the point is to expose the new version to real users a little at a time and roll back at the first sign of errors, before most of your traffic ever touched it.
Recreate is honest for apps that cannot run two versions against one database schema. Do not pretend RollingUpdate fixes incompatible migrations.
Blue/green needs twice the capacity and a crisp cutover checklist. Selector edits are fast and easy to get wrong under stress.
Canary is a percentage problem: metrics and a rollback switch matter more than the YAML shape.
Try this
Compare a RollingUpdate Deployment with a Recreate one in a namespace you can break. Then sketch how blue/green would look with two Deployments and a Service selector flip.
$ kubectl apply -f rolling.yamldeployment.apps/web created$ kubectl get deploy webNAME READY UP-TO-DATE AVAILABLE AGEweb 4/4 4 4 40s$ kubectl set image deployment/web web=nginx:1.27deployment.apps/web image updated$ kubectl rollout status deployment/webWaiting for deployment "web" rollout to finish: 1 out of 4 new replicas have been updated...Waiting for deployment "web" rollout to finish: 2 out of 4 new replicas have been updated...Waiting for deployment "web" rollout to finish: 3 out of 4 new replicas have been updated...Waiting for deployment "web" rollout to finish: 3 of 4 updated replicas are available...deployment "web" successfully rolled out$ kubectl get rs -l app=webNAME DESIRED CURRENT READY AGEweb-6f8b9c7d5 2 2 2 11m # old, nginx:1.26, scaling downweb-7d4f9b8c6 3 3 2 18s # new, nginx:1.27, scaling up$ kubectl rollout status deployment/web --timeout=90sWaiting for deployment "web" rollout to finish: 1 out of 4 new replicas have been updated...error: deployment "web" exceeded its progress deadline$ kubectl rollout undo deployment/webdeployment.apps/web rolled back$ kubectl patch deployment web --type merge \-p '{"spec":{"strategy":{"type":"Recreate","rollingUpdate":null}}}'deployment.apps/web patched$ kubectl set image deployment/web web=nginx:1.28deployment.apps/web image updated$ kubectl describe deployment web | grep -A6 EventsEvents:Type Reason Age From Message---- ------ ---- ---- -------Normal ScalingReplicaSet 20s deployment-controller Scaled down replica set web-7d4f9b8c6 to 0 from 4Normal ScalingReplicaSet 6s deployment-controller Scaled up replica set web-5c9d7f6b4 to 4 from 0$ kubectl patch service web -p '{"spec":{"selector":{"app":"web","version":"green"}}}'service/web patched$ kubectl get endpoints webNAME ENDPOINTS AGEweb 10.244.1.31:80,10.244.2.44:80,10.244.3.12:80 23m
Takeaway
RollingUpdate trades capacity for smoothness. Recreate is downtime by design. Blue/green and canaries are Service or mesh tricks on top of Deployments.