60.1 ReplicationController: the first controller, and why it is legacy
Why: the problem
Suppose we run the IRCTC ticket search service as one bare pod. At 10 AM, tatkal booking opens, the node runs out of memory, and the pod is evicted. Nobody brings it back. Search is down until someone notices and runs kubectl apply again. A bare pod has no guardian. The first guardian Kubernetes ever had was the ReplicationController, or RC. You tell it "keep 3 copies of this pod", and it watches day and night. If one dies, it makes a new one. If there are too many, it deletes the extra. Today nobody writes new RCs. ReplicaSet and Deployment replaced them. But old clusters, old tutorials and interviews still use them, and the idea behind them, a control loop, is the idea behind every controller. In this lesson we run an RC, kill its pods, scale it, see its equality-only selector, and migrate it to a Deployment. Then we break it with a selector that does not match and with a pod that an RC adopts by accident.
What: every term in plain English
- Controller — A loop in kube-controller-manager that compares desired state with actual state and fixes the difference.
- ReplicationController (RC) — The original controller that keeps a fixed number of identical pods running. apiVersion v1, kind ReplicationController.
- replicas — How many pods the controller must keep.
- selector — Which pods the controller counts as its own. RC supports only equality, like app=search.
- template — The pod blueprint the controller uses to create new pods.
- Reconcile loop — Observe, compare, act, repeat. Every controller works this way.
- Adoption — A controller takes ownership of any existing pod whose labels match its selector.
- ownerReferences — Metadata on a pod that names the controller that owns it.
- Rolling update for RC — Old kubectl rolling-update did it on the client side. It was removed in kubectl 1.18.
- Legacy — Still works and is still in the API, but not used for new work.
How: step by step
- Write the RC. apiVersion: v1, kind: ReplicationController, replicas, selector, template.
- Match labels. template.metadata.labels must contain every key in selector.
- Apply and list. kubectl get rc and kubectl get pods -l app=search.
- Kill a pod. kubectl delete pod one of them, and watch a new one appear.
- Scale. kubectl scale rc search --replicas=5.
- Migrate. Write a Deployment with the same labels, apply it, then delete the RC with --cascade=orphan or normally.
- Clean up. kubectl delete rc search deletes its pods too.
Example 1: Run an RC and watch it heal
Goal. Run an RC and watch it heal.
search-rc.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: search
namespace: shop
spec:
replicas: 3
selector:
app: search
template:
metadata:
labels:
app: search
tier: backend
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: v1— RC lives in the core API group, like Pod and Service.replicas: 3— Desired state: three pods.selector: app: search— A plain map. Only equality matching, no matchLabels or matchExpressions.template.metadata.labels— Must include app: search, or the RC would keep creating pods it cannot count.tier: backend— Extra labels are fine.
Green output
$ kubectl apply -f search-rc.yaml
replicationcontroller/search created
$ kubectl get rc -n shop
NAME DESIRED CURRENT READY AGE
search 3 3 3 12s
$ kubectl delete pod -n shop $(kubectl get pods -n shop -l app=search -o name | head -1 | cut -d/ -f2)
pod "search-7xk2d" deleted
$ kubectl get pods -n shop -l app=search
NAME READY STATUS RESTARTS AGE
search-9mfq4 1/1 Running 0 3s
search-h2lzt 1/1 Running 0 40s
search-wq8cn 1/1 Running 0 40s
Expected result. One pod was deleted and a new one, 3 seconds old, took its place. That is the reconcile loop in action.
Example 2: Scale, read ownership, and see adoption
Goal. Scale, read ownership, and see adoption.
stray-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: stray
namespace: shop
labels:
app: search
spec:
containers:
- name: web
image: nginx:1.27
What each line does
labels app: search— Same label as the RC selector.No owner— A bare pod has no ownerReferences when created.What happens— The RC now counts 4 pods for 3 desired, and deletes one to get back to 3.
Green output
$ kubectl scale rc search -n shop --replicas=3
$ kubectl get pod -n shop search-h2lzt -o jsonpath='{.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name}{"\n"}'
ReplicationController/search
$ kubectl apply -f stray-pod.yaml
pod/stray created
$ kubectl get pods -n shop -l app=search
NAME READY STATUS RESTARTS AGE
search-9mfq4 1/1 Running 0 2m
search-h2lzt 1/1 Running 0 3m
search-wq8cn 1/1 Terminating 0 3m
stray 1/1 Running 0 2s
Expected result. The RC adopted the stray pod and removed one of its own to keep 3. Labels decide ownership, so choose them carefully.
Example 3: Migrate an RC to a Deployment with no downtime
Goal. Migrate an RC to a Deployment with no downtime.
search-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: search
namespace: shop
spec:
replicas: 3
selector:
matchLabels:
app: search
version: v2
template:
metadata:
labels:
app: search
version: v2
tier: backend
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests: {cpu: 20m, memory: 32Mi}
limits: {memory: 64Mi}
search-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: search
namespace: shop
spec:
selector:
app: search
ports:
- port: 80
What each line does
apiVersion: apps/v1— Deployments live in the apps group.matchLabels with version: v2— A new label so the Deployment does not fight the RC over the same pods.Service selector app: search— Matches both old and new pods, so traffic flows during the switch.Order— Apply Service, apply Deployment, wait Ready, then delete the RC.
Green output
$ kubectl apply -f search-svc.yaml -f search-deploy.yaml
$ kubectl rollout status deploy/search -n shop
deployment "search" successfully rolled out
$ kubectl get endpointslices -n shop -l kubernetes.io/service-name=search -o jsonpath='{range .items[*].endpoints[*]}{.addresses[0]} {end}{"\n"}'
10.244.1.12 10.244.2.8 10.244.1.13 10.244.2.11 10.244.1.15 10.244.2.12
$ kubectl delete rc search -n shop
replicationcontroller "search" deleted
$ kubectl get pods -n shop -l app=search
NAME READY STATUS RESTARTS AGE
search-6d9f7c8b5d-4kq2x 1/1 Running 0 40s
search-6d9f7c8b5d-p8wzn 1/1 Running 0 40s
search-6d9f7c8b5d-vt7hc 1/1 Running 0 40s
Expected result. For a moment the Service had 6 endpoints, old and new. Then the RC was removed and only Deployment pods stayed. Users saw no gap.
Every command, step by step (kind lab with kubectl)
-
Check the cluster
kubectl get nodes kubectl get ns shopWhat it does: Reuse the ch59 cluster.
ch59-control-plane Ready ch59-worker Ready ch59-worker2 Ready -
Read the RC docs
kubectl explain rc.spec.selectorWhat it does: Note: a map of string, not a LabelSelector.
FIELD: selector map[string]string -
Create the RC
kubectl apply -f search-rc.yaml kubectl get rc,pods -n shop -l app=searchWhat it does: Three pods with random suffixes.
replicationcontroller/search 3 3 3 -
Watch healing live
kubectl get pods -n shop -l app=search -wWhat it does: Leave it running in a second terminal.
search-9mfq4 0/1 ContainerCreating 0 0s search-9mfq4 1/1 Running 0 2s -
Delete two pods at once
kubectl delete pod -n shop -l app=search --wait=falseWhat it does: All go; the RC makes three new ones.
pod "search-9mfq4" deleted pod "search-h2lzt" deleted pod "search-wq8cn" deleted -
Scale up and down
kubectl scale rc search -n shop --replicas=5 kubectl scale rc search -n shop --replicas=2What it does: The RC adds or removes pods to match.
replicationcontroller/search scaled -
See controller events
kubectl describe rc search -n shop | tail -4What it does: SuccessfulCreate and SuccessfulDelete lines.
Normal SuccessfulCreate replication-controller Created pod: search-z4c9k Normal SuccessfulDelete replication-controller Deleted pod: search-z4c9k -
Orphan the pods
kubectl delete rc search -n shop --cascade=orphan kubectl get pods -n shop -l app=searchWhat it does: The RC is gone but its pods stay, with no owner.
search-6b8tq 1/1 Running 0 5m -
Clean up
kubectl delete pod -n shop -l app=search kubectl delete deploy,svc -n shop searchWhat it does: Ready for ReplicaSet in 60.2.
pod "search-6b8tq" deleted
When it is red 1
The ReplicationController "search" is invalid: spec.template.metadata.labels: Invalid value: map[string]string{"app":"serch"}: `selector` does not match template `labels`
Cause. Typo in the template label.
Fix, step by step
- Make template labels contain every selector key and value.
- Apply again.
When it is red 2
error: unknown command "rolling-update" for "kubectl"
Cause. Old tutorials use kubectl rolling-update, removed in kubectl 1.18.
Fix, step by step
- Migrate to a Deployment and use kubectl set image and kubectl rollout.
When it is red 3
pods keep disappearing right after I create them
Cause. An RC, ReplicaSet or Deployment with a matching selector is adopting and deleting your pods.
Fix, step by step
- kubectl get pod -o jsonpath on ownerReferences.
- kubectl get rc,rs,deploy -A -o wide and check selectors.
- Use unique labels.
When it is red 4
error: unable to recognize "rc.yaml": no matches for kind "ReplicationController" in version "apps/v1"
Cause. RC is in the core group, not apps.
Fix, step by step
- Use apiVersion: v1 for ReplicationController.
Practice tasks
Easy Task 1: Create
Why: First controller.
What to do: Apply search-rc.yaml and list pods.
- kubectl get rc.
Solution
kubectl apply -f search-rc.yamlEasy Task 2: Heal
Why: Self-healing.
What to do: Delete one pod and see the replacement.
- Watch AGE.
Solution
kubectl delete pod -n shop NAMEEasy Task 3: Scale
Why: Desired state.
What to do: Scale to 5, then back to 3.
- kubectl scale.
Solution
kubectl scale rc search -n shop --replicas=5Medium Task 4: Owner
Why: Ownership.
What to do: Print the owner of one pod.
- ownerReferences jsonpath.
Solution
kubectl get pod NAME -n shop -o jsonpath='{.metadata.ownerReferences[0].name}'Medium Task 5: Adopt
Why: Label danger.
What to do: Create stray and see what the RC does.
- Apply stray-pod.yaml.
Solution
kubectl apply -f stray-pod.yamlMedium Task 6: Explain
Why: Know the API.
What to do: Compare kubectl explain rc.spec.selector and rs.spec.selector.
- Two explain commands.
Solution
kubectl explain rs.spec.selectorMedium Task 7: Bad selector
Why: Learn the error.
What to do: Break the template label and read the error.
- Change app to serch.
Solution
app: serchMedium Task 8: Orphan
Why: Cascade.
What to do: Delete the RC with --cascade=orphan and see pods stay.
- --cascade=orphan.
Solution
kubectl delete rc search -n shop --cascade=orphanHard Task 9: Migrate
Why: Zero downtime.
What to do: Move search from RC to Deployment behind a Service with a curl loop running.
- Apply Deployment first.
- Then delete RC.
Solution
while true; do kubectl exec -n shop deploy/search -- curl -s -o /dev/null -w '%{http_code} ' http://search; sleep 1; doneHard Task 10: Controller logs
Why: See the loop.
What to do: Read kube-controller-manager logs for replication-controller lines.
- Static pod from 59.7.
Solution
kubectl logs -n kube-system kube-controller-manager-ch59-control-plane | grep -i replicationHomework: try at home
Find one old tutorial that uses a ReplicationController and rewrite its YAML as a Deployment, explaining each change.
Recap
- A controller is a loop that makes actual state match desired state.
- RC keeps N identical pods; it is v1, equality-selector only, and legacy.
- Labels decide ownership; a matching stray pod gets adopted.
- kubectl rolling-update is gone; use Deployments for updates.
- Migrate RC to Deployment by running both behind one Service, then deleting the RC.
- Next, 60.2 covers the ReplicaSet that replaced it.
FAQ
Q. Is ReplicationController deprecated?
It is not removed from the API, but the docs recommend Deployments and ReplicaSets for all new work.
Q. Why learn it at all?
Old clusters and interviews use it, and it shows the reconcile loop in its simplest form.
Q. Can RC do rolling updates?
Not by itself. kubectl rolling-update did it from the client and is removed.
Q. RC vs ReplicaSet in one line?
Same job; ReplicaSet adds set-based selectors and is managed by Deployments.
Q. Who runs the RC loop?
The replication controller inside kube-controller-manager, a static pod from lesson 59.7.
Q. Does RC work on EKS?
Yes, the API still serves it, but use Deployments.
Q. How do I find old RCs in a cluster I just joined?
Run kubectl get rc -A. On a clean modern cluster the list is empty. If you see some, note their selectors and owners, then plan a migration to Deployments one app at a time.
Q. Can an RC and a Deployment share pods?
They should never share labels. If both selectors match the same pods they fight: one adopts, the other creates more, and the replica counts never settle. Always add a unique label such as controller: deploy before migrating.
Q. What happens to RC pods when a node dies?
The node controller marks the node NotReady, the pods are evicted after the timeout (about five minutes by default), and the RC creates replacements on healthy nodes. The same loop protects Deployments; we test it in 63.4.
Q. Does kubectl scale work the same on RC and ReplicaSet?
Yes. Both expose a scale subresource, so kubectl scale and even an autoscaler can change the replica count. Chapter 63 uses HPA on Deployments instead.
Interview questions
-
What is a ReplicationController?
Model answer: A legacy v1 controller that ensures a specified number of pod replicas run, using an equality-based selector.
-
Difference between ReplicationController and ReplicaSet?
Model answer: ReplicaSet supports set-based selectors (matchExpressions: In, NotIn, Exists) and is used by Deployments; RC only supports equality and is legacy.
-
Explain the reconcile loop.
Model answer: Controllers watch the API, compare desired spec with actual status, and create, delete or update objects to remove the difference, repeating forever.
-
Why did my bare pod disappear?
Model answer: A controller with a matching selector adopted it and scaled down to its replica count.
-
How would you migrate an RC workload?
Model answer: Create a Deployment with a distinguishing label behind the same Service, wait until ready, then delete the RC.
-
An interviewer says: an RC shows 3 desired but 0 current pods. How do you debug it?
Model answer: Run kubectl describe rc and read the events. Common causes are a ResourceQuota that forbids new pods, an image pull error, an admission webhook rejecting the pod, or a node selector that no node matches. Fix the cause and the RC creates pods on its own.
Quick quiz
Q1. Which apiVersion does RC use?
v1.
Q2. What kind of selector does RC support?
Equality only, a plain map of labels.
Q3. What happens if you delete an RC pod?
The RC creates a new one.
Q4. What replaced RC?
ReplicaSet, managed by Deployment.
Q5. What does --cascade=orphan do?
Deletes the controller but keeps its pods.
Samajla ka?