Ravindra BagaleCourses & study guides मराठी

Chapter 60: Kubernetes Workload Controllers in Depth: ReplicaSet, Deployment, StatefulSet, DaemonSet, Jobs and CronJobs

60.2 ReplicaSet: set-based selectors, ownership and adoption

Why: the problem

Suppose Zomato runs its menu API as 4 pods. A teammate writes a ReplicaSet with the selector app=menu. Another teammate, on the same namespace, starts a test pod with the label app=menu. A minute later the test pod is gone, or worse, one real pod is deleted. Nobody ran kubectl delete. The ReplicaSet did it. It counts every pod that matches its selector, even pods it did not create. This is called adoption, and it is the number one surprise with ReplicaSets. ReplicaSet replaced the ReplicationController. It does the same job, keep N pods alive, but it adds set-based selectors: In, NotIn, Exists and DoesNotExist. Deployments create ReplicaSets for you, so every pod you run from a Deployment is really owned by a ReplicaSet. In this lesson we run a ReplicaSet by hand, use matchExpressions, read ownerReferences, watch adoption and orphaning, and see why you almost never write a ReplicaSet yourself. Then we break it with an immutable selector, a label mismatch and a fight between two controllers.

What: every term in plain English

  • ReplicaSet (RS) — A controller in apps/v1 that keeps a fixed number of identical pods running.
  • selector.matchLabels — Simple equality: every listed key must have exactly this value.
  • selector.matchExpressions — Set-based rules: key In [a,b], NotIn [c], Exists, DoesNotExist. All rules must be true.
  • template — The pod blueprint. Its labels must satisfy the selector, or the API rejects the RS.
  • ownerReferences — Metadata on each pod naming its owner RS, with controller: true.
  • Adoption — An RS takes any running pod with matching labels and no controller owner.
  • Orphan — A pod whose owner was deleted with --cascade=orphan. It keeps running with no guardian.
  • Garbage collector — Part of kube-controller-manager. When an owner is deleted, it deletes the children too (cascade).
  • pod-template-hash — A label Deployments add to each RS and its pods, so two RSes of one Deployment never share pods.
  • Immutable selector — In apps/v1 you cannot change spec.selector after creation. Delete and recreate instead.
Pick labels. Write the selector. Copy labels into the template. Apply and check. Read ownership. Test healing and adoption. Prefer a Deployment.
ReplicaSet: selector picks pods, template makes new ones

How: step by step

  1. Pick labels. Use at least two labels, like app=menu and tier=api, so selectors are specific.
  2. Write the selector. matchLabels for simple cases, matchExpressions for sets.
  3. Copy labels into the template. The template must match the selector exactly or the apply fails.
  4. Apply and check. kubectl get rs, then kubectl get pods --show-labels.
  5. Read ownership. kubectl get pod NAME -o jsonpath on ownerReferences.
  6. Test healing and adoption. Delete a pod; add a bare pod with the same labels.
  7. Prefer a Deployment. Use an RS by hand only to learn. In real work a Deployment creates it.

Example 1: A ReplicaSet with matchLabels and matchExpressions

Goal. A ReplicaSet with matchLabels and matchExpressions.

menu-rs.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: menu
  namespace: shop
  labels:
    app: menu
spec:
  replicas: 3
  selector:
    matchLabels:
      app: menu
    matchExpressions:
      - key: tier
        operator: In
        values: [api, web]
      - key: canary
        operator: DoesNotExist
  template:
    metadata:
      labels:
        app: menu
        tier: api
    spec:
      containers:
        - name: web
          image: nginx:1.27
          ports:
            - containerPort: 80
          resources:
            requests: {cpu: 20m, memory: 32Mi}
            limits: {memory: 64Mi}

What each line does

  • apiVersion: apps/v1 — ReplicaSet lives in the apps group, unlike the v1 RC.
  • matchLabels: app: menu — The pod must have app=menu.
  • tier In [api, web] — The pod's tier label must be api or web.
  • canary DoesNotExist — Pods labelled canary are never counted. Useful to keep test pods out.
  • All rules are ANDed — A pod must pass matchLabels and every expression.
  • template labels — app=menu and tier=api pass all three rules, so the API accepts the RS.

Green output

$ kubectl apply -f menu-rs.yaml
replicaset.apps/menu created
$ kubectl get rs -n shop -o wide
NAME   DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES       SELECTOR
menu   3         3         3       15s   web          nginx:1.27   app=menu,canary!,tier in (api,web)
$ kubectl get pods -n shop -l app=menu --show-labels
NAME         READY   STATUS    RESTARTS   AGE   LABELS
menu-4xq8r   1/1     Running   0          15s   app=menu,tier=api
menu-b7m2k   1/1     Running   0          15s   app=menu,tier=api
menu-rl9tz   1/1     Running   0          15s   app=menu,tier=api

Expected result. The SELECTOR column shows the set rules in short form: canary! means the key must not exist.

Example 2: Ownership, adoption and orphans

Goal. Ownership, adoption and orphans.

menu-test.yaml

apiVersion: v1
kind: Pod
metadata:
  name: menu-test
  namespace: shop
  labels:
    app: menu
    tier: web
spec:
  containers:
    - name: web
      image: nginx:1.27

menu-canary.yaml

apiVersion: v1
kind: Pod
metadata:
  name: menu-canary
  namespace: shop
  labels:
    app: menu
    tier: web
    canary: "true"
spec:
  containers:
    - name: web
      image: nginx:1.27

What each line does

  • menu-test labels — app=menu and tier=web pass the selector. The RS will adopt it.
  • menu-canary labels — Same plus canary=true. DoesNotExist fails, so the RS ignores it.
  • controller: true — After adoption the pod's ownerReferences point to RS menu.
  • Scale down effect — With 4 matching pods for 3 desired, the RS deletes one. It may delete your test pod or a real one.

Green output

$ kubectl apply -f menu-canary.yaml -f menu-test.yaml
pod/menu-canary created
pod/menu-test created
$ kubectl get pods -n shop -l app=menu
NAME          READY   STATUS        RESTARTS   AGE
menu-4xq8r    1/1     Running       0          3m
menu-b7m2k    1/1     Running       0          3m
menu-canary   1/1     Running       0          4s
menu-rl9tz    1/1     Terminating   0          3m
menu-test     1/1     Running       0          4s
$ kubectl get pod menu-test -n shop -o jsonpath='{.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name} controller={.metadata.ownerReferences[0].controller}{"\n"}'
ReplicaSet/menu controller=true
$ kubectl get pod menu-canary -n shop -o jsonpath='{.metadata.ownerReferences}{"\n"}'

Expected result. menu-test was adopted and an original pod was removed to keep 3. menu-canary has no owner because the DoesNotExist rule kept it out.

Example 3: Why Deployments own ReplicaSets: pod-template-hash

Goal. Why Deployments own ReplicaSets: pod-template-hash.

menu-deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: menu-v2
  namespace: shop
spec:
  replicas: 3
  selector:
    matchLabels:
      app: menu-v2
  template:
    metadata:
      labels:
        app: menu-v2
    spec:
      containers:
        - name: web
          image: nginx:1.27
          resources:
            requests: {cpu: 20m, memory: 32Mi}
            limits: {memory: 64Mi}

What each line does

  • kind: Deployment — You write the Deployment; it writes the ReplicaSet.
  • selector app: menu-v2 — A new label so it does not fight RS menu.
  • pod-template-hash — Added automatically to the RS selector and pod labels, for example 7c9d8f6b4.
  • New image = new RS — Change the image and the Deployment creates a second RS with a new hash, then scales the old one to 0.

Green output

$ kubectl apply -f menu-deploy.yaml
deployment.apps/menu-v2 created
$ kubectl get rs -n shop -l app=menu-v2
NAME                 DESIRED   CURRENT   READY   AGE
menu-v2-7c9d8f6b4    3         3         3       10s
$ kubectl get rs menu-v2-7c9d8f6b4 -n shop -o jsonpath='{.spec.selector.matchLabels}{"\n"}{.metadata.ownerReferences[0].kind}{"\n"}'
{"app":"menu-v2","pod-template-hash":"7c9d8f6b4"}
Deployment
$ kubectl set image deploy/menu-v2 -n shop web=nginx:1.27-alpine
$ kubectl get rs -n shop -l app=menu-v2
NAME                 DESIRED   CURRENT   READY   AGE
menu-v2-5f6b8d7c9    3         3         3       12s
menu-v2-7c9d8f6b4    0         0         0       2m

Expected result. The chain is Deployment, then ReplicaSet, then Pod. The old RS stays at 0 so you can roll back. That is lesson 60.3.

Every command, step by step (kind lab with kubectl)

  1. Explain the selector field

    kubectl explain rs.spec.selector
    

    What it does: Unlike RC, it is a LabelSelector with matchLabels and matchExpressions.

    FIELD: selector <LabelSelector>
      matchExpressions <[]LabelSelectorRequirement>
      matchLabels <map[string]string>
    
  2. Create the ReplicaSet

    kubectl apply -f menu-rs.yaml
    kubectl get rs,pods -n shop -l app=menu
    

    What it does: Three pods with the RS name as prefix.

    replicaset.apps/menu   3   3   3   8s
    
  3. Delete one pod

    kubectl delete pod -n shop $(kubectl get pod -n shop -l app=menu -o name | head -1 | cut -d/ -f2)
    kubectl get pods -n shop -l app=menu
    

    What it does: A new pod appears with a fresh name.

    menu-k2p8w   1/1   Running   0   2s
    
  4. Test a set selector by hand

    kubectl get pods -n shop -l 'app=menu,tier in (api,web),!canary'
    

    What it does: The same rule the RS uses, typed on the command line.

    menu-4xq8r   1/1   Running   0   4m
    
  5. Scale

    kubectl scale rs menu -n shop --replicas=5
    kubectl get rs menu -n shop
    

    What it does: Two more pods.

    menu   5   5   5   4m
    
  6. Try to change the selector

    kubectl patch rs menu -n shop --type=merge -p '{"spec":{"selector":{"matchLabels":{"app":"menu2"}}}}'
    

    What it does: Fails: selectors are immutable in apps/v1.

    The ReplicaSet "menu" is invalid: spec.selector: Invalid value: ...: field is immutable
    
  7. Change the image in the RS

    kubectl set image rs/menu -n shop web=nginx:1.27-alpine
    kubectl get pods -n shop -l app=menu -o jsonpath='{range .items[*]}{.spec.containers[0].image}{"\n"}{end}'
    

    What it does: Old pods keep the old image. An RS does not roll out; only new pods use the new template.

    nginx:1.27
    nginx:1.27
    nginx:1.27-alpine
    
  8. Orphan the pods

    kubectl delete rs menu -n shop --cascade=orphan
    kubectl get pods -n shop -l app=menu
    

    What it does: Pods stay, without an owner.

    menu-4xq8r   1/1   Running   0   6m
    
  9. Let a new RS adopt them

    kubectl apply -f menu-rs.yaml
    kubectl get pod menu-4xq8r -n shop -o jsonpath='{.metadata.ownerReferences[0].uid}{"\n"}'
    

    What it does: The new RS adopts the orphans and only creates or deletes the difference.

    3f1d9a2e-6b0c-4d1e-9f7a-2c8e5b4a1d07
    
  10. Clean up

    kubectl delete rs menu -n shop
    kubectl delete pod menu-canary menu-test -n shop --ignore-not-found
    kubectl delete deploy menu-v2 -n shop
    

    What it does: Ready for Deployment internals in 60.3.

    replicaset.apps "menu" deleted
    

When it is red 1

The ReplicaSet "menu" is invalid: spec.template.metadata.labels: Invalid value: map[string]string{"app":"menu","tier":"db"}: `selector` does not match template `labels`

Cause. The template has tier=db, but the selector says tier In [api, web].

Fix, step by step

  1. Change the template label to api or web.
  2. Or add db to the values list.
  3. Apply again.

When it is red 2

spec.selector: Invalid value: v1.LabelSelector{...}: field is immutable

Cause. You changed the selector of an existing RS or Deployment.

Fix, step by step

  1. Delete the object, optionally with --cascade=orphan.
  2. Apply the new YAML; it will adopt matching pods.

When it is red 3

pods are created and deleted every few seconds; events show SuccessfulCreate and SuccessfulDelete in a loop

Cause. Two controllers have overlapping selectors and fight over the same pods.

Fix, step by step

  1. kubectl get rs,rc,deploy -n shop -o wide and compare SELECTOR.
  2. Give each a unique label, such as app=menu and app=menu-v2.
  3. Delete the extra controller.

When it is red 4

error: the server doesn't have a resource type "replicaset" in group "extensions/v1beta1"

Cause. Old tutorial YAML uses extensions/v1beta1, removed in Kubernetes 1.16.

Fix, step by step

  1. Change apiVersion to apps/v1.
  2. Add the now-required spec.selector.

Practice tasks

Easy Task 1: Create

Why: First RS.

What to do: Apply menu-rs.yaml and list pods with labels.

How:
  1. --show-labels.
Solution
kubectl get pods -n shop --show-labels

Easy Task 2: Heal

Why: Self-healing.

What to do: Delete a pod and watch it come back.

How:
  1. -w flag.
Solution
kubectl get pods -n shop -l app=menu -w

Easy Task 3: Scale

Why: Desired count.

What to do: Scale to 5 and back to 2.

How:
  1. kubectl scale rs.
Solution
kubectl scale rs menu -n shop --replicas=2

Medium Task 4: Selector test

Why: Set logic.

What to do: List pods with tier in (api,web) and no canary label.

How:
  1. Quote the selector.
Solution
kubectl get pods -n shop -l 'tier in (api,web),!canary'

Medium Task 5: Adopt

Why: Label risk.

What to do: Apply menu-test.yaml and find which pod was removed.

How:
  1. get events.
Solution
kubectl get events -n shop --sort-by=.lastTimestamp | tail

Medium Task 6: Exclude

Why: DoesNotExist.

What to do: Show menu-canary has no owner.

How:
  1. ownerReferences.
Solution
kubectl get pod menu-canary -n shop -o yaml | grep -A3 ownerReferences

Medium Task 7: Immutable

Why: Read the error.

What to do: Try to patch the selector.

How:
  1. kubectl patch.
Solution
kubectl patch rs menu -n shop --type=merge -p '{"spec":{"selector":{"matchLabels":{"app":"x"}}}}'

Medium Task 8: No rollout

Why: RS limit.

What to do: Change the image and count old and new images.

How:
  1. set image, then jsonpath.
Solution
kubectl set image rs/menu -n shop web=nginx:1.27-alpine

Hard Task 9: Orphan and adopt

Why: Ownership.

What to do: Orphan pods, re-apply the RS and show the uid changed.

How:
  1. --cascade=orphan.
Solution
kubectl delete rs menu -n shop --cascade=orphan

Hard Task 10: Fight

Why: Overlap.

What to do: Create two RSes with selector app=menu and watch the events, then fix it.

How:
  1. Two files, same labels.
Solution
kubectl get events -n shop -w

Homework: try at home

Run a ReplicaSet with one matchExpressions rule of each type (In, NotIn, Exists, DoesNotExist) and prove each rule with a test pod.

Recap

  • ReplicaSet keeps N pods alive and supports set-based selectors.
  • Template labels must match the selector, and the selector is immutable.
  • A ReplicaSet adopts any matching pod without a controller owner.
  • An RS never updates running pods; that is the Deployment's job.
  • Deployments create one RS per template version, tagged with pod-template-hash.
  • Write Deployments, not ReplicaSets, for real apps.

FAQ

Q. Should I ever write a ReplicaSet myself?

Rarely. Only when you need custom update logic and no rollouts. Use a Deployment.

Q. Why does my Deployment show many ReplicaSets?

Each template change makes a new RS. revisionHistoryLimit (default 10) keeps old ones for rollback.

Q. Can two Deployments share pods?

They should not. Overlapping selectors cause fights.

Q. What does --cascade=foreground do?

Deletes the pods first, then the owner, and waits for them.

Q. Is canary a special label?

No. It is our own label; DoesNotExist makes the RS skip it.

Q. Where does the RS loop run?

In the replicaset controller inside kube-controller-manager.

Interview questions

  1. ReplicaSet vs ReplicationController?

    Model answer: Both keep N pods. ReplicaSet is apps/v1, has set-based selectors and is managed by Deployments; RC is legacy v1 with equality selectors.

  2. ReplicaSet vs Deployment?

    Model answer: An RS only keeps a count. A Deployment manages RSes to give rolling updates, rollback and history.

  3. What is pod adoption?

    Model answer: A controller takes ownership of existing pods that match its selector and have no controller owner, and counts them toward replicas.

  4. How does Kubernetes know which RS owns a pod?

    Model answer: Through metadata.ownerReferences with controller: true and the RS uid.

  5. A pod you created keeps getting deleted. Why?

    Model answer: A ReplicaSet with a matching selector adopted it and scaled down. Use unique labels.

Quick quiz

Q1. Which API group is ReplicaSet in?

apps/v1.

Q2. Name the four matchExpressions operators.

In, NotIn, Exists, DoesNotExist.

Q3. Can you change an RS selector?

No, it is immutable.

Q4. Does set image on an RS update running pods?

No, only new pods get it.

Q5. What label links a Deployment's RS to its pods?

pod-template-hash.

Samajla ka?