ReplicaSets Behind Deployments
Learn how ReplicaSets work under Kubernetes Deployments for Python workloads. Step-by-step lesson with kubectl and Python client examples, plus troubleshooting tips.
Focus: ReplicaSets behind Python deployments
You’ve rolled out a Python service with kubectl create deployment, watched your pods come up, and maybe scaled to three replicas. But when a pod crashes, who exactly restarts it? The Deployment gets the credit, but the real work is done by a middleman you’ve never met: the ReplicaSet. This hidden controller is what guarantees your Python API stays available, and understanding it is the key to debugging scaling issues, rollout rollbacks, and even the dreaded CrashLoopBackOff. Let’s pull back the curtain and see how ReplicaSets keep your Python deployments alive.
The problem this lesson solves
You can run a Python container locally with docker run, but that’s a single process on a single machine—if it dies, your service dies. In Kubernetes, you need redundancy: multiple copies (replicas) of your app running so that if one fails, traffic still flows. Many developers assume the Deployment is the thing that manages replicas. It’s not. The Deployment creates and supervises a ReplicaSet, and the ReplicaSet is the actual pod-level controller. Without understanding this layer, you’ll be confused when:
kubectl get podsshows a mix of old and new versions during a rollout.- You scale a Deployment but the pod count doesn’t change immediately.
- Multiple ReplicaSets linger after a deployment update—eating memory and confusing your
kubectl get alloutput.
By the end of this lesson, you’ll be able to inspect ReplicaSets behind your Python deployments, explain their role in scaling and self-healing, and troubleshoot replica-related issues with confidence.
Core concept / mental model
Imagine you run a food truck with three cooks. The Deployment is your business plan: it defines the menu (container image), how many cooks you want (replicas), and the strategy for hiring/firing (rolling update). The ReplicaSet is the shift supervisor: it constantly counts cooks and hires replacements if someone quits or gets sick. The pods are the individual cooks—ephemeral, replaceable workers that actually cook the food.
In Kubernetes terms:
- Deployment: Declarative intent—
kind: Deployment,spec.replicas,spec.template. - ReplicaSet: The controller that enforces the desired number of pod replicas, using a selector to identify which pods it owns.
- Pod: The smallest unit of execution—a Python container (or several) with shared networking and storage.
The ReplicaSet is not a high-level abstraction; it’s a lower-level primitive that Deployment uses under the hood. When you create a Deployment, Kubernetes automatically creates a ReplicaSet with a unique hash in its name (e.g., myapp-7d8f9). That hash comes from a pod template hash—a deterministic hash of the pod spec. If you change the image or env vars, a new hash is generated, and the Deployment creates a new ReplicaSet, scaling it up while scaling the old one down (rolling update).
Why not just use ReplicaSets directly?
You could create a ReplicaSet directly, but you’d miss out on:
- Rolling updates—Deployments manage them with two ReplicaSets.
- Rollback—Deployment keeps revision history.
- Declarative scaling—Deployment updates the ReplicaSet’s
replicasfield.
In practice, you’ll almost always deploy with Deployments, but you need to know ReplicaSets to debug them.
How it works step by step
Let’s trace what happens when you run kubectl apply -f deployment.yaml for a Python app:
- API server receives the Deployment object and stores it in etcd.
- Deployment controller watches for the new Deployment and creates a ReplicaSet object with a unique name (e.g.,
myapp-7d8f9) and a selector matching the pod labels from the template. - ReplicaSet controller sees the new ReplicaSet and reads the desired
replicascount. It then queries the API server for existing pods that match its selector. - ReplicaSet controller creates missing pods by sending a request to the API server with the pod template. The scheduler places each pod on a node, and the kubelet starts containers.
- ReplicaSet controller reconciles continuously—if a pod dies (node failure, OOM, crash), it creates a new one to maintain the desired count.
- During a Deployment update, a new ReplicaSet is created with a new template hash. The Deployment scales the new RS up and the old RS down according to
maxSurgeandmaxUnavailable(defaults: 25% surge, 25% unavailable). The old RS is not deleted—it’s kept for rollback (retention controlled byrevisionHistoryLimit).
Key definitions
- Selector: A set of label requirements (e.g.,
app: mypythonapp) that the ReplicaSet uses to find matching pods. Must match pods created from its template. - Replicas: Desired pod count; the ReplicaSet strives to make actual = desired.
- Pod template hash: A label like
pod-template-hash: 7d8f9automatically added to pods to identify which ReplicaSet created them.
Hands-on walkthrough
Let’s get practical. Ensure you have a running cluster (minikube or kind works) and kubectl installed. We’ll deploy a simple Python HTTP server image (e.g., python:3.10-slim) and inspect the ReplicaSet.
Step 1: Create a Deployment
Create deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: pyweb
spec:
replicas: 3
selector:
matchLabels:
app: pyweb
template:
metadata:
labels:
app: pyweb
spec:
containers:
- name: pyweb
image: python:3.10-slim
command: ["python", "-m", "http.server", "8080"]
ports:
- containerPort: 8080
Apply it:
kubectl apply -f deployment.yaml
Step 2: Inspect the ReplicaSet
List ReplicaSets:
kubectl get rs
Sample output:
NAME DESIRED CURRENT READY AGE
pyweb-7d8f9c6d4 3 3 3 1m
The pyweb-7d8f9c6d4 is the ReplicaSet name. Note the hash 7d8f9c6d4—it comes from the pod template.
Get details on the ReplicaSet:
kubectl describe rs pyweb-7d8f9c6d4
Look at the Selector field (e.g., app=pyweb,pod-template-hash=7d8f9c6d4) and the Controlled By field (it should reference the Deployment pyweb).
Step 3: See the pod template hash
List pods and show labels:
kubectl get pods --show-labels
Output:
NAME READY STATUS RESTARTS AGE LABELS
pyweb-7d8f9c6d4-abc12 1/1 Running 0 1m app=pyweb,pod-template-hash=7d8f9c6d4
pyweb-7d8f9c6d4-abc34 1/1 Running 0 1m app=pyweb,pod-template-hash=7d8f9c6d4
...
Every pod carries the pod-template-hash label, identifying its ReplicaSet.
Step 4: Simulate a pod failure
Delete one pod and watch the ReplicaSet recreate it:
kubectl delete pod pyweb-7d8f9c6d4-abc12
kubectl get pods -w
You’ll see a new pod start with a different suffix—same ReplicaSet, but a fresh replacement. That’s the ReplicaSet’s reconciliation in action.
Step 5: Scale and observe old RS
Scale to 5 replicas:
kubectl scale deployment pyweb --replicas=5
kubectl get rs
Now upgrade the image (change command or add env) to trigger a rollout:
kubectl set image deployment/pyweb pyweb=python:3.11-slim
Watch the ReplicaSets during rollout:
kubectl get rs
You’ll see two ReplicaSets: one with old hash (scale down to 0) and one with new hash (scale up). The old RS remains—this is what enables rollback.
Python client example
You can manage ReplicaSets programmatically using the official kubernetes Python client. Install it (pip install kubernetes), then run a script to list ReplicaSets and their pod counts:
from kubernetes import client, config
# Load kubeconfig (assumes you're authenticated)
config.load_kube_config()
v1_apps = client.AppsV1Api()
# List ReplicaSets in the default namespace
rs_list = v1_apps.list_namespaced_replica_set(namespace="default")
for rs in rs_list.items:
print(f"ReplicaSet: {rs.metadata.name}")
print(f" Desired: {rs.spec.replicas}")
print(f" Current: {rs.status.replicas}")
print(f" Ready: {rs.status.ready_replicas}")
print(f" Labels: {rs.metadata.labels}")
print("---")
Sample output:
ReplicaSet: pyweb-7d8f9c6d4
Desired: 5
Current: 5
Ready: 5
Labels: {'app': 'pyweb', 'pod-template-hash': '7d8f9c6d4'}
---
This is power—you can build a simple CLI to audit replica health across all namespaces.
Compare options / when to choose what
You have several ways to manage pod replicas in Kubernetes. Here’s a comparison:
| Approach | Use case | Pros | Cons |
|---|---|---|---|
| Deployment + ReplicaSet | Most apps, production workloads | Rolling updates, rollback, self-healing | Adds overhead of managing RS lifecycle |
| ReplicaSet directly | Rare—only if you need raw replica control without updates | Simple, minimal | No rolling updates, no rollback, no declarative updates |
| StatefulSet | Stateful apps (databases) | Stable network identifiers, ordered scaling | Requires volumeClaimTemplates etc. |
| DaemonSet | Node-level services (monitoring, log collection) | Runs on every node | Not for replicated stateless apps |
| Job/CronJob | Batch processing | Runs to completion | Not for long-running services |
Core rule: For Python web services and APIs, always use a Deployment—you get ReplicaSet management for free. If you ever see a ReplicaSet in your cluster without a parent Deployment, it’s likely an orphan from a manual kubectl apply of a RS manifest—clean it up.
Troubleshooting & edge cases
-
Pods stuck in
Pending: The ReplicaSet may be trying to create pods, but the scheduler can’t place them (insufficient resources). Checkkubectl describe rs <name>—look for events likeFailedScheduling. Solution: add nodes or reduce replicas. -
Pod count doesn’t match desired: If
kubectl get rsshowsDESIRED=3,CURRENT=2, the ReplicaSet is having trouble creating pods (e.g., image pull failure, resource limits). Inspect events:kubectl describe rs <name>. Also checkkubectl get events --sort-by=.metadata.creationTimestamp. -
Old ReplicaSets piling up: By default, Kubernetes keeps 10 ReplicaSets for rollback. If you see many, set
revisionHistoryLimitin your Deployment spec to a lower number (e.g., 3) to keep the cluster tidy. -
CrashLoopBackOffafter update: This usually means the new pod template is broken (bad command, missing env). The Deployment will keep trying, but the ReplicaSet shows highPODSbut noREADY. Fix the image/template and re-apply. -
ReplicaSet stuck scaling down: If you scale to 0, the ReplicaSet should delete all pods. If not, maybe pods have finalizers or are unresponsive—
kubectl delete pod --force --grace-period=0as a last resort.
Pro tip: Always use
kubectl rollout status deployment/pywebto wait for a rollout to finish. It gives you clear messages like “Waiting for replica set to be available.”
What you learned & what's next
You’ve mastered the ReplicaSet layer: you now know that Deployments are wrappers around ReplicaSets, and ReplicaSets are the actual pod-reconciliation engines. You can inspect ReplicaSets with kubectl get rs, describe them, and even use the Python client to programmatically monitor them. You understand how rollout history and scale changes are reflected in RS status, and you can troubleshoot common replica-related issues like stuck scaling or missing pods.
Next in your learning path, you’ll dive into Services and networking—how your Python app becomes reachable to other pods and external clients. You’ll see how the selector labels that ReplicaSets use are also the key to connecting a Service to your pods. Get ready to route traffic like a pro.
Practice recap
Mini exercise: Create a Deployment of a Python Flask app with 2 replicas. Scale it to 4, then update the image version. Watch the ReplicaSets appear and disappear. Then roll back with kubectl rollout undo and observe the old ReplicaSet becoming active again. After that, write a short Python script using the kubernetes client to list all ReplicaSets and their current vs. desired counts. This will solidify your understanding of the ReplicaSet lifecycle.
Common mistakes
- Assuming the Deployment manages pods directly—always remember the ReplicaSet is the controller that creates and deletes pods.
- Deleting a ReplicaSet manually when you want to scale down—use
kubectl scale deploymentinstead, so the Deployment controls the RS. - Scaling a ReplicaSet directly when it's owned by a Deployment—the Deployment will override your changes on the next reconciliation.
- Forgetting to set
revisionHistoryLimit—old ReplicaSets accumulate and waste etcd space and API list time. - Using the same label selector for multiple ReplicaSets—they'll fight over the same pods.
Variations
- You can use
kubectl rollout undo deployment/pywebto roll back to a previous ReplicaSet, leveraging the stored history. - For more advanced replica management, use a HorizontalPodAutoscaler (HPA) that adjusts the ReplicaSet's desired replicas based on CPU/memory.
- The Python client can also create or delete ReplicaSets directly, but for Deployments that's usually unnecessary—stick to Deployment objects.
Real-world use cases
- A production Python web service where a pod crashes—the ReplicaSet instantly spins up a replacement to keep uptime high.
- Rolling out a new version of a Python API by updating the Deployment image—the Deployment creates a new ReplicaSet and scales it up while scaling the old one down.
- A batch processing system using a Python worker that needs to scale to 10 replicas during peak hours and back to 2 at night—the Deployment adjusts the ReplicaSet's replicas.
Key takeaways
- ReplicaSets are the intermediary controllers that ensure the desired number of pod replicas runs, while Deployments manage ReplicaSets.
- Each Deployment update creates a new ReplicaSet, identified by a pod-template-hash label, enabling rolling updates and rollbacks.
- The ReplicaSet uses a selector to find and own pods; labels like
appandpod-template-hashare crucial. - Scaling or updating a Deployment should always be done via the Deployment object, not directly on the ReplicaSet.
- Troubleshooting replica issues involves checking
kubectl describe rs, events, and pod status—link symptoms to the ReplicaSet's reconciliation loop. - Use the Python client to programmatically audit ReplicaSets and their desired/ready counts across namespaces.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.