Certified Kubernetes Application Developer (CKAD) #5 Workloads 1: Deployment, ReplicaSet, Rolling Update, and Rollback
The resource you run into most often on the CKAD exam is the Deployment. In real-world operations too, a Deployment is the standard unit for running one set of an application, and the exam’s Application Deployment domain (20%) starts here. You almost never spin up a single Pod directly. Apps in production are nearly always shipped as Deployments, swapped without downtime via rolling updates when bumping a version, and rolled back in one line when a new version causes trouble.
In this post we will go hands-on, typing out the whole process: from quickly creating a Deployment imperatively, to how ReplicaSet and Pod are wired up beneath a Deployment, to managing deployment history with kubectl rollout and reverting a failed version. If the big picture of how a cluster operates is still fuzzy, it helps to read K8s Practical Track #4 Workloads first.
Creating a Deployment imperatively #
We reuse the do alias set up in #1 as-is. We pull a Deployment skeleton in one line with k create deploy.
# Create it right away
k create deploy web --image=nginx:1.25 --replicas=3
# Pull just the manifest skeleton to edit (dry-run)
k create deploy web --image=nginx:1.25 --replicas=3 $do > deploy.yamlWithout the --replicas flag, the replica count defaults to 1. When the exam tells you to specify a count, it is faster to pass it along at creation time. The generated manifest looks like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25The key point here is that selector.matchLabels and template.metadata.labels must match. The selector is the criterion deciding which Pods this Deployment treats as its own, and the template’s labels must meet that criterion for the Pods to be managed correctly. If the two diverge, creation itself is rejected.
The relationship between Deployment, ReplicaSet, and Pod #
Creating a Deployment doesn’t mean the Deployment makes Pods directly. A ReplicaSet sits one level in between.
Deployment ──owns──> ReplicaSet ──owns──> Pod (×replicas)- A Deployment declares “which version to run and how many,” and whenever the version changes, it creates a new ReplicaSet and orchestrates the swap.
- A ReplicaSet only does the job of “always maintaining the specified number of Pods.” If a Pod dies, it recreates it; if there are too many, it trims them.
- A Pod is the smallest unit where the actual containers run.
Let’s check what was actually created right after creation.
k get deploy web
k get rs # a web-<hash> ReplicaSet appears
k get pod # web-<rs hash>-<random> Pods appear, as many as replicasYou can see that the name in the k get rs output gets a hash appended after the Deployment name, like web-7d9c8f6b54. This hash is computed from the contents of the Pod template, so if you change the image or environment variables, a new ReplicaSet with a different hash appears. This structure is precisely the foundation of rolling updates and rollbacks — it creates the new version as a new ReplicaSet and retains the old ReplicaSet as history by simply scaling its count down to 0.
Scale: changing the count #
Increasing or decreasing the Pod count is handled instantly with k scale.
# replicas to 5
k scale deploy web --replicas=5
# Change to 5 only when the current count is 3 (conditional)
k scale deploy web --current-replicas=3 --replicas=5You can also edit the manifest and k apply, but if the exam task is just to change the count, k scale is fastest. After the change, check that the READY column of k get deploy web becomes 5/5.
If the task calls for autoscaling, attach a HorizontalPodAutoscaler with k autoscale.
k autoscale deploy web --min=2 --max=10 --cpu-percent=70Rolling update: laying down a new version without downtime #
When bumping the image of a running Deployment to a new version, the default strategy is RollingUpdate. Instead of killing all the existing Pods at once, it brings up new Pods bit by bit while taking old Pods down bit by bit, swapping them out so the service never goes dark.
maxSurge and maxUnavailable #
The speed and stability of a rolling update are determined by two values.
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # how many more than desired can be brought up
maxUnavailable: 1 # how many can be missing during the swap- maxSurge: the number of Pods that can be temporarily brought up beyond the desired replicas during the update. A larger value brings up more new Pods in advance, speeding up the swap.
- maxUnavailable: the number of Pods allowed to be in an unavailable state during the update. A value of 0 means old Pods are only taken down after new Pods become Ready, giving the highest availability.
Both values can be given as an integer or a percentage (25%). If unspecified, both default to 25%. For a task that says to make availability the top priority, remember the pattern of setting maxUnavailable: 0.
Triggering an update by swapping the image #
The most common update is bumping the image version. k set image does it in one line.
# Swap to v2 when the container name is nginx
k set image deploy/web nginx=nginx:1.26
# If there are multiple containers, specify each
k set image deploy/web nginx=nginx:1.26 sidecar=busybox:1.36The nginx= part of set image is the container name, not the image name. If you’re unsure of the container name, check it first with k get deploy web -o jsonpath='{.spec.template.spec.containers[*].name}'.
If you need to change fields other than just the image, edit the manifest directly with k edit.
k edit deploy webIf you change something inside the template with k edit, a new ReplicaSet is created and a rolling update begins. Conversely, if you change only outside the template (replicas) like with k scale, no new ReplicaSet is created. In other words, a new revision is created only when the Pod template changes.
Managing deployments with rollout #
Whether the update is progressing well, how the history piles up, and how to revert if something goes wrong—kubectl rollout handles all of it. It’s the command group you most need to get into your fingers for CKAD.
Checking progress #
# Shows progress until the rolling update finishes
k rollout status deploy/webRunning this command right after set image lets you wait for and confirm that everything has become Ready. It’s useful for self-verifying task completion before grading.
Checking history #
# revision list
k rollout history deploy/web
# Detail of a specific revision (the template at that point)
k rollout history deploy/web --revision=2Revisions count up from 1. If you want to leave a note (CHANGE-CAUSE) recording which change corresponds to which revision, add --record when making the change or attach the kubernetes.io/change-cause annotation directly.
k annotate deploy web kubernetes.io/change-cause="update to nginx:1.26"Pause and resume #
When you want to bundle several changes into a single rollout, pause to halt, make several edits, then resume.
k rollout pause deploy/web
k set image deploy/web nginx=nginx:1.26
k set resources deploy/web -c=nginx --limits=cpu=200m,memory=256Mi
k rollout resume deploy/web # rolls out all at once at this pointWhile paused, changing the template won’t bring up new Pods. The moment you resume, the accumulated changes are reflected as a single revision. It’s the foundation of strategies like canary; detailed deployment strategies are covered in #8.
How much history to keep #
How many old ReplicaSets to retain is controlled by revisionHistoryLimit. The default is 10, and setting it to 0 wipes out all rollback history, so be careful.
spec:
revisionHistoryLimit: 5Hands-on rollback scenario #
Now let’s type out, from start to finish, a flow that shows up often on the exam. The scenario: running a healthy version, deploying a bad version, detecting the failure, and reverting to the previous version.
# 1) Deploy a healthy version (revision 1)
k create deploy web --image=nginx:1.25 --replicas=3
k rollout status deploy/web
# 2) Update with a bad image (revision 2), a non-existent tag
k set image deploy/web nginx=nginx:does-not-exist
# 3) Detect the failure
k rollout status deploy/web # progress stalls and never finishes
k get pod # the new Pod is in ImagePullBackoff stateBecause the image is bad, the new Pod can’t pull the image and falls into ImagePullBackOff. The important point here is that since maxUnavailable is 25%, some of the old Pods are still alive, so the service doesn’t go down completely. That’s RollingUpdate protecting availability. Still, the new version is broken, so we revert.
# 4) Roll back to the previous revision
k rollout undo deploy/web
k rollout status deploy/web # converges back to healthy
# 5) Verify: whether it went back to 1.25
k get deploy web -o jsonpath='{.spec.template.spec.containers[0].image}'You can also revert by pinpointing a specific revision.
# Look at the revision list
k rollout history deploy/web
# Revert to revision 3
k rollout undo deploy/web --to-revision=3undo works by scaling the old ReplicaSet’s count back up and scaling the broken ReplicaSet’s count down to 0. That’s why rollback is fast, and the reverted state is recorded as a new revision. Remember that revision numbers don’t disappear — they keep accumulating.
RollingUpdate and Recreate #
A Deployment’s strategy includes Recreate besides RollingUpdate. Recreate takes down all existing Pods first and then brings up new ones, so the service briefly goes dark during the swap. Use it for apps that must not have two versions up at the same time (for example, a single-writer database migration).
spec:
strategy:
type: RecreateThe default is the zero-downtime swap, RollingUpdate, and Recreate is for special situations that can tolerate downtime. The trade-offs between the two strategies and advanced zero-downtime strategies like blue-green and canary are covered in detail in #8 Deployment Strategies.
Exam points #
- Creating a Deployment is fastest with
k create deploy <name> --image=<img> --replicas=N. Omitting--replicasgives 1. - selector.matchLabels and template.labels must match. If they diverge, creation is rejected.
- The ownership chain is Deployment → ReplicaSet → Pod. A new revision is created only when the Pod template changes. Changing only replicas creates no new ReplicaSet.
- Scale is
k scale deploy <name> --replicas=N. - Image swap is
k set image deploy/<name> <container-name>=<img>. The left of the equals sign is the container name, not the image. - maxSurge is the allowed excess count, maxUnavailable is the allowed shortfall count. Both default to 25%, and for availability-first it’s
maxUnavailable: 0. - The rollout command group:
status(progress),history(history),history --revision=N(detail),undo(revert to previous),undo --to-revision=N(specific version),pause,resume(bundle and deploy)—get them into your fingers. - Revisions accumulate even after a rollback. The number kept is set by
revisionHistoryLimit(default 10).
Wrap-up #
What we nailed down in this post:
- A Deployment is the standard unit of app delivery, operating in a three-tier structure of Deployment → ReplicaSet → Pod.
- Creation and scaling are handled quickly and imperatively with
k create deployandk scale. - A rolling update tunes speed and availability with maxSurge and maxUnavailable, triggered by
k set imageork edit. kubectl rollouttracks deployment state and manages history, andundoreverts a failed version in one line.- Recreate is for special situations that tolerate downtime; the default is the zero-downtime RollingUpdate.
Next: Workloads 2 #
We’ve nailed down how to run stateless apps with zero downtime using a Deployment. But not every workload fits the “just spin up N copies on any node” shape.
In #6 Workloads 2: DaemonSet, StatefulSet, we cover the DaemonSet (log collectors, monitoring agents), which must place exactly one Pod on every node, and the StatefulSet (databases, message queues), which needs stable network identifiers and ordered, guaranteed storage. We’ll build them ourselves to lay out how they differ from a Deployment and what form they take on the exam.