Deployment Mechanics and ReplicaSets
SummaryExplains the three-tier ownership chain (Deployment → ReplicaSet...
Explains the three-tier ownership chain (Deployment → ReplicaSet...
Explains the three-tier ownership chain (Deployment → ReplicaSet → Pod), demonstrates imperative Deployment creation and YAML generation, covers the Deployment spec fields (replicas, selector, template, strategy), scaling operations, ReplicaSet observation, and how labels and selectors bind resources together.
Deployment Mechanics and ReplicaSets
A Deployment is not a Pod factory. It is a controller that manages ReplicaSets, which in turn manage Pods. This two-layer indirection — Deployment → ReplicaSet → Pod — is the mechanism that enables rolling updates, rollback history, and declarative scaling. Understanding this hierarchy is essential for every Deployment-related task on the exam and for debugging production issues where Pods are not behaving as expected.
The Ownership Chain
Three resources participate in every Deployment:
Deployment — the top-level resource you create and interact with. It holds the desired state: how many replicas, which container image, what update strategy. You never modify ReplicaSets or Pods directly when a Deployment manages them — you modify the Deployment, and the Deployment controller handles everything downstream.
ReplicaSet — created and managed by the Deployment controller. A ReplicaSet’s job is singular: maintain a stable set of Pods matching a specific Pod template. When the Deployment’s Pod template changes (because you updated the image, for example), the controller creates a new ReplicaSet with the new template and scales down the old ReplicaSet. This is how rolling updates work — the Deployment does not update Pods in place. It replaces them by shifting replicas between ReplicaSets.
Pod — created and managed by the ReplicaSet controller. Each Pod runs your application containers. If a Pod is deleted or crashes, the ReplicaSet controller creates a replacement to maintain the desired replica count.
The ownership chain is enforced through metadata.ownerReferences. Each ReplicaSet has an owner reference pointing to its Deployment. Each Pod has an owner reference pointing to its ReplicaSet. You can see this with:
kubectl get rs -o yaml | grep -A 5 ownerReferences
kubectl get pods -o yaml | grep -A 5 ownerReferences
This ownership matters for garbage collection. When you delete a Deployment, Kubernetes automatically deletes its ReplicaSets. When a ReplicaSet is deleted, Kubernetes automatically deletes its Pods. The cascade is automatic — you do not need to clean up child resources manually.
Why Deployments Over ReplicaSets
If ReplicaSets already manage Pods and maintain replica counts, why add the Deployment layer? Because ReplicaSets lack three capabilities that production deployments require:
-
Rolling updates. A ReplicaSet has one Pod template. To update the image, you would need to delete the old ReplicaSet and create a new one — causing downtime. A Deployment creates the new ReplicaSet, gradually shifts Pods to it, and keeps the old ReplicaSet around (scaled to zero) for rollback.
-
Revision history. A Deployment maintains a history of ReplicaSets (controlled by
revisionHistoryLimit, default 10). Each represents a previous configuration. You can roll back to any prior revision with a single command. A standalone ReplicaSet has no concept of versions. -
Declarative update strategy. A Deployment lets you configure how updates happen — how many extra Pods can exist during the transition (
maxSurge), how many Pods can be unavailable (maxUnavailable), and whether to use a rolling update or a full recreate. A ReplicaSet does not manage transitions at all.
On the exam, you will never create a ReplicaSet directly. Every workload task uses Deployments. ReplicaSets are an implementation detail you observe when debugging — never a resource you create manually.
Creating Deployments
Imperative Creation
The fastest way to create a Deployment:
kubectl create deployment nginx --image=nginx:1.25 --replicas=3
This single command creates:
- A Deployment named
nginxwith a labelapp=nginx. - A ReplicaSet (named something like
nginx-6d4cf4f94b) managed by the Deployment. - Three Pods (named with the ReplicaSet name plus random suffixes) managed by the ReplicaSet.
Verify the entire hierarchy:
kubectl get deployment nginx
kubectl get rs
kubectl get pods
Output:
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 3/3 3 3 12s
NAME DESIRED CURRENT READY AGE
nginx-6d4cf4f94b 3 3 3 12s
NAME READY STATUS RESTARTS AGE
nginx-6d4cf4f94b-2xkp7 1/1 Running 0 12s
nginx-6d4cf4f94b-8mwjt 1/1 Running 0 12s
nginx-6d4cf4f94b-vn4ql 1/1 Running 0 12s
Notice the naming convention: the ReplicaSet name is the Deployment name plus a hash of the Pod template. The Pod names are the ReplicaSet name plus random suffixes. This convention lets you trace any Pod back to its ReplicaSet and Deployment by reading the name.
Generating YAML
For tasks that require specific fields beyond what the imperative command provides (resource limits, environment variables, volume mounts), generate the YAML scaffold and edit it:
kubectl create deployment nginx --image=nginx:1.25 --replicas=3 \
--dry-run=client -o yaml > deploy.yaml
The generated manifest:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:1.25
name: nginx
Edit deploy.yaml to add any additional configuration, then apply:
kubectl apply -f deploy.yaml
Exam tip: The
--dry-run=client -o yamlpattern works exactly as it does for Pods. Set up the shell aliasexport do='--dry-run=client -o yaml'at the beginning of your exam session:kubectl create deployment nginx --image=nginx $do > deploy.yaml.
The Deployment Spec
Four fields define a Deployment’s behavior:
replicas
The desired number of identical Pods:
spec:
replicas: 3
The Deployment controller ensures exactly this many Pods are running at all times (accounting for update strategy parameters during rollouts). If a Pod is deleted, the ReplicaSet creates a replacement. If you manually create extra Pods with the same labels, the ReplicaSet deletes the excess.
selector
Tells the Deployment which Pods it owns:
spec:
selector:
matchLabels:
app: nginx
The selector is immutable after creation — you cannot change it. It must match the labels in spec.template.metadata.labels. If they do not match, the Deployment is rejected by the API server at creation time.
template
The Pod template — the specification for every Pod the Deployment creates:
spec:
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
When you change any field inside template, the Deployment controller detects the change and triggers a rollout. This includes image versions, environment variables, resource limits, volume mounts — anything inside the Pod spec. Changes to fields outside template (like replicas) do not trigger a rollout — they are handled by scaling, not rolling update.
strategy
Defines how updates are applied. Covered in detail in the next section, but the two options are:
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
or:
spec:
strategy:
type: Recreate
The default is RollingUpdate with maxSurge: 25% and maxUnavailable: 25%.
Scaling
Scaling changes the replicas count without triggering a rollout. No new ReplicaSet is created. The existing ReplicaSet adds or removes Pods.
Imperative Scaling
kubectl scale deployment nginx --replicas=5
This updates the Deployment’s spec.replicas from 3 to 5. The ReplicaSet controller creates two new Pods to reach the desired count.
To verify:
kubectl get deployment nginx
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 5/5 5 5 3m
Declarative Scaling
Edit the YAML (or use kubectl edit):
kubectl edit deployment nginx
This opens the Deployment manifest in your default editor. Change replicas: 3 to replicas: 5, save, and exit. The controller reconciles immediately.
Alternatively, modify the YAML file and reapply:
# In deploy.yaml, change replicas to 5
kubectl apply -f deploy.yaml
Both methods produce the same result. On the exam, kubectl scale is faster for a simple replica change. Use kubectl edit or kubectl apply when you need to change multiple fields at once.
Scaling to Zero
Scaling to zero replicas is valid:
kubectl scale deployment nginx --replicas=0
This removes all Pods while preserving the Deployment and its ReplicaSet. The application is offline, but the configuration is intact. Scale back up whenever needed. This technique is useful for temporarily deactivating a workload without deleting its definition.
Observing ReplicaSets
During normal operation, a Deployment has one active ReplicaSet. During and after an update, it has multiple — one active (managing the new Pods) and one or more scaled to zero (previous revisions kept for rollback).
kubectl get rs
After a fresh Deployment:
NAME DESIRED CURRENT READY AGE
nginx-6d4cf4f94b 3 3 3 5m
After updating the image (covered in the next section):
NAME DESIRED CURRENT READY AGE
nginx-6d4cf4f94b 0 0 0 5m
nginx-7c45b84548 3 3 3 30s
The old ReplicaSet (nginx-6d4cf4f94b) is scaled to zero but not deleted. It represents revision 1 of the Deployment. The new ReplicaSet (nginx-7c45b84548) runs the updated Pods and represents revision 2. The Deployment keeps up to revisionHistoryLimit old ReplicaSets (default 10) for rollback.
To see which revision each ReplicaSet represents:
kubectl get rs -o wide
Or inspect the Deployment’s rollout history:
kubectl rollout history deployment nginx
Labels and Selectors: Binding the Hierarchy
Labels and selectors are the glue that connects Deployments, ReplicaSets, and Pods. No resource in Kubernetes “contains” another in the traditional object-oriented sense. Instead, resources discover each other through label matching.
The flow works as follows:
- The Deployment’s
spec.selector.matchLabelsdefines the labels it watches for:app: nginx. - The Deployment creates a ReplicaSet whose Pods carry those labels (via
spec.template.metadata.labels). - The ReplicaSet’s
spec.selector.matchLabels(inherited from the Deployment) watches for Pods withapp: nginx. - Each Pod created by the ReplicaSet carries the
app: nginxlabel.
If you manually create a Pod with app: nginx in the same namespace, the ReplicaSet’s controller notices a Pod matching its selector that it does not own. It counts this “orphan” toward the desired replica count, and if the total exceeds spec.replicas, it deletes the excess — which might be your manually-created Pod. This is why adding labels carelessly can cause unexpected Pod deletions.
You can add additional labels to Pods without disrupting the selector:
kubectl label pod nginx-6d4cf4f94b-2xkp7 version=v1 environment=staging
These extra labels do not interfere with the ReplicaSet’s selector — the selector only requires app: nginx, and additional labels are ignored. But removing the app: nginx label from a Pod will cause the ReplicaSet to lose track of it and create a replacement:
# This orphans the Pod — the ReplicaSet creates a new one
kubectl label pod nginx-6d4cf4f94b-2xkp7 app-
The - suffix removes the label. The ReplicaSet sees its replica count drop by one and launches a replacement. The orphaned Pod continues running — it is now a bare Pod with no controller managing it.
Exam Strategy
Deployment creation is one of the fastest categories of CKAD tasks if you know the imperative commands:
# Create the Deployment in one command
kubectl create deployment <name> --image=<image> --replicas=<n>
# Need a specific namespace
kubectl create deployment <name> --image=<image> --replicas=<n> -n <namespace>
# Need to add more fields — generate and edit
kubectl create deployment <name> --image=<image> --replicas=<n> --dry-run=client -o yaml > deploy.yaml
vim deploy.yaml
kubectl apply -f deploy.yaml
For scaling, always use kubectl scale — there is no faster option. For inspecting the hierarchy, remember the diagnostic trio:
kubectl get deploy,rs,pods
kubectl describe deployment <name>
kubectl rollout history deployment <name>
The first command shows all three levels of the hierarchy in one output. The second gives detailed information about the Deployment’s current state and events. The third shows the revision history for rollback operations — covered in the next section.