Skip to main content
mastering ckad certified kubernetes application developer

Deployment Mechanics and ReplicaSets

10 min read Chapter 20 of 87
Summary

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:

  1. 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.

  2. 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.

  3. 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 nginx with a label app=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 yaml pattern works exactly as it does for Pods. Set up the shell alias export 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:

  1. The Deployment’s spec.selector.matchLabels defines the labels it watches for: app: nginx.
  2. The Deployment creates a ReplicaSet whose Pods carry those labels (via spec.template.metadata.labels).
  3. The ReplicaSet’s spec.selector.matchLabels (inherited from the Deployment) watches for Pods with app: nginx.
  4. Each Pod created by the ReplicaSet carries the app: nginx label.

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.