Application Deployment
Last verified against its sources on 23 September 2026
Application Deployment covers the second 20% of the CKAD exam: rolling a Deployment out and back safely, implementing release strategies like blue/green and canary with nothing but labels and Services, and using Helm and Kustomize to package and adapt manifests for real environments.
The exam rewards knowing which tool fits which job: a plain rolling update for routine changes, a second Deployment and a selector flip for an instant cutover, a small second Deployment for a gradual one, Helm when the app is a versioned package with its own release history, and Kustomize when the same base manifests need small, environment- specific differences without duplicating files.
Deployments and Rolling Updates
- Configure a Deployment's rolling update strategy with maxSurge and maxUnavailable to control rollout speed and safety.
- Monitor, pause, resume and roll back a Deployment rollout with kubectl.
A Deployment's default update strategy is RollingUpdate: old
Pods are replaced by new ones gradually rather than all at once.
Two fields tune how that gradual replacement behaves.
maxSurge caps how many Pods above the desired replica count
may exist at once — extra capacity created ahead of removing
old Pods. maxUnavailable caps how many Pods below the desired
count are tolerated at once — old Pods allowed to be removed
before their replacements are ready. Both default to 25% of the
replica count, rounded per Kubernetes' own rules, so a rollout
of 10 replicas may briefly run anywhere from 9 to 12 Pods
depending on where it is in the process.
Setting maxUnavailable: 0 guarantees full capacity throughout
the rollout at the cost of needing surge capacity to schedule
the extra Pods; setting maxSurge: 0 guarantees the replica
count is never exceeded at the cost of tolerating some
unavailability while old Pods are removed first.
bash
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2
maxUnavailable: 0
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: web:2.3kubectl rollout status deployment/web follows a rollout live
and reports when it finishes or gets stuck. kubectl rollout history deployment/web lists past revisions, each backed by
its own ReplicaSet — Kubernetes keeps old ReplicaSets around
(scaled to zero) up to revisionHistoryLimit, 10 by default, so
kubectl rollout undo has something to revert to.
kubectl rollout pause and kubectl rollout resume let you
stop a rollout mid-way to verify the new Pods before letting it
continue.
Older tutorials mention --record on kubectl set image or
kubectl apply to label why a revision was created; that flag
has been removed from kubectl's current command reference. The
supported way to label a revision is the kubernetes.io/change- cause annotation on the Deployment — set it directly in the
manifest, or with kubectl annotate — which kubectl rollout history reads into its CHANGE-CAUSE column.
A rollout's new Pods can never pull their image because of a typo'd tag. Does kubectl rollout status finish, fail fast, or hang?Answer it yourself first, then open this.
It hangs — the new Pods never become Ready, so the rollout stalls part-way rather than completing or rolling back on its own.
Blue/Green and Canary Deployment Strategies
- Implement a blue/green release by running two Deployments and switching a Service's selector between them.
- Implement a canary release by running a small second Deployment behind the same Service selector.
Kubernetes has no API object literally named "blue-green" or
"canary" — the CKAD curriculum calls for implementing these
with plain primitives: labels, a Service's selector, and
ordinary Deployments. A Service routes traffic to whichever
Pods currently match its spec.selector; it has no idea which
Deployment those Pods came from.
A blue/green release runs two full Deployments at once —
"blue" (live) and "green" (new) — each with its own distinct
label such as version: blue or version: green. The Service
selector initially matches blue. Once green is verified, the
cutover is a single edit to the Service's selector so it
matches green's Pods instead — new connections land on green
immediately, and rolling back is the same edit in reverse,
instant because blue's Pods never stopped running.
bash
# Before: Service routes to the "blue" Deployment's Pods
spec:
selector:
app: web
version: blue
# After: same Service, now routing to "green" instead
spec:
selector:
app: web
version: greenA canary release instead runs a small second Deployment
that shares the same labels the Service already selects on —
typically just app: web, without a version label in the
selector at all — so the Service load-balances across both the
stable Deployment's Pods and the canary's. If stable runs 9
replicas and canary runs 1, roughly one in ten connections
reaches the canary, because a Service spreads connections
across all matching Pods without favoring one Deployment over
another.
That "roughly" matters: a plain Kubernetes Service has no concept of a configured traffic percentage — the split just falls out of the replica ratio, connection by connection, with no guarantee any single request lands one way or the other. Precise, percentage-exact traffic splitting needs a service mesh or an Ingress controller with weighted-routing support; it isn't a feature of the core Service object itself.
A canary Deployment has 1 replica while stable has 9, both matched by the same Service selector. Roughly what share of requests hit the canary?Answer it yourself first, then open this.
Roughly 10% — the split follows the replica ratio, not an exact configured percentage, since a Service has no built-in weighted routing.
Deploying Applications with Helm
- Install and upgrade an application from a Helm chart, overriding its default values as needed.
- Roll back a Helm release to a previous revision using its release history.
Helm is the package manager for Kubernetes. A chart is a
versioned bundle of templated manifests — Chart.yaml for
metadata, values.yaml for the defaults its templates fill in,
and a templates/ directory of the manifests themselves. A
release is one named, installed instance of a chart in a
namespace; the same chart can back several independent releases
at once, each with its own values.
Helm 3 — the current major version — removed Tiller, the
server-side component Helm 2 required inside the cluster. The
helm CLI now talks to the Kubernetes API directly using the
same credentials kubectl uses, so there is nothing extra to
install or secure beyond the cluster itself. helm install <release> <chart> creates a release; helm upgrade <release> <chart> -f values.yaml updates one that already exists; helm upgrade --install does either, which makes it the natural
choice for an idempotent pipeline step that shouldn't care
whether the release exists yet.
bash
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm install my-nginx bitnami/nginx \
--namespace web --create-namespace \
--set replicaCount=3Helm keeps a release's history the way a Deployment keeps
ReplicaSet revisions. helm history <release> lists past
revisions; helm rollback <release> <revision> restores the
release's Kubernetes resources to that revision's state.
Passing --atomic to helm install or helm upgrade makes
Helm roll the release back automatically if the operation
fails or times out, instead of leaving it half-applied.
helm uninstall <release> removes every resource the release
created; add --keep-history if you want the revision record
to survive an uninstall.
A value set with --set on the command line overrides the
same key in values.yaml; a -f other-values.yaml file
overrides it too, and later flags win over earlier ones when
both are given. Nothing about this requires editing the
chart's own templates.
A helm upgrade with --atomic fails partway through. What happens?Answer it yourself first, then open this.
Helm automatically rolls the release back to its last successful revision instead of leaving it half-upgraded.
Customizing Manifests with Kustomize
- Structure a base and environment-specific overlays with Kustomize, applying them with kubectl apply -k.
- Patch a base manifest for one environment using the unified patches field.
Kustomize takes the opposite approach from Helm: no templating
language at all. You write plain, valid YAML manifests once as
a base, then layer overlays on top that patch or add
to them for each environment. kubectl has supported
kustomization.yaml files natively since v1.14 — kubectl apply -k <dir> applies what a directory's kustomization
produces, and kubectl kustomize <dir> just prints it without
applying anything, useful for reviewing the result first.
A kustomization.yaml (apiVersion: kustomize.config.k8s.io/ v1beta1, kind: Kustomization) lists its resources: — plain
manifest files, or another kustomization directory used as a
base — and can set cross-cutting fields such as namespace,
namePrefix, nameSuffix, labels and commonAnnotations
that get applied to everything it references, without editing
those referenced files at all.
bash
# base/kustomization.yaml
resources:
- deployment.yaml
- service.yaml
# overlays/production/kustomization.yaml
resources:
- ../../base
namespace: production
patches:
- path: increase-replicas.yaml
target:
kind: Deployment
name: webPatching a specific resource is done through the unified
patches: field, which accepts either a strategic-merge patch
or a JSON 6902 patch and auto-detects which one it's looking
at. The older patchesStrategicMerge and patchesJson6902
fields still work but are deprecated in favor of patches;
kustomize edit fix can migrate an old kustomization.yaml
to the current field automatically.
configMapGenerator and secretGenerator build a ConfigMap or
Secret from files or literal key-value pairs listed right in
the kustomization file. By default, the generated object's name
gets a content-hash suffix appended — so when the data changes,
the generated name changes too, which forces anything
referencing it by name (like a Deployment's volume) to roll.
generatorOptions.disableNameSuffixHash: true turns that
behavior off, at the cost of losing the automatic rollout.
An old kustomization.yaml uses patchesStrategicMerge. Does it still work, and what's the current recommendation?Answer it yourself first, then open this.
It still works but is deprecated; use the unified patches field instead — kustomize edit fix can migrate the file automatically.
Sources
- Kubernetes Documentation — Deployments (strategy types)
- kubectl Reference — kubectl apply
- Kubernetes Documentation — Service
- Helm Documentation — Using Helm
- Helm Documentation — Cheat Sheet
- Kubernetes Documentation — Declarative Management of Kubernetes Objects Using Kustomize
- Kustomize Source — Deprecated Patch Fields