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

Sponsored

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 pods shows 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 all output.

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 replicas field.

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:

  1. API server receives the Deployment object and stores it in etcd.
  2. 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.
  3. ReplicaSet controller sees the new ReplicaSet and reads the desired replicas count. It then queries the API server for existing pods that match its selector.
  4. 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.
  5. ReplicaSet controller reconciles continuously—if a pod dies (node failure, OOM, crash), it creates a new one to maintain the desired count.
  6. 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 maxSurge and maxUnavailable (defaults: 25% surge, 25% unavailable). The old RS is not deleted—it’s kept for rollback (retention controlled by revisionHistoryLimit).

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: 7d8f9 automatically 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

  1. Pods stuck in Pending: The ReplicaSet may be trying to create pods, but the scheduler can’t place them (insufficient resources). Check kubectl describe rs <name>—look for events like FailedScheduling. Solution: add nodes or reduce replicas.

  2. Pod count doesn’t match desired: If kubectl get rs shows DESIRED=3, CURRENT=2, the ReplicaSet is having trouble creating pods (e.g., image pull failure, resource limits). Inspect events: kubectl describe rs <name>. Also check kubectl get events --sort-by=.metadata.creationTimestamp.

  3. Old ReplicaSets piling up: By default, Kubernetes keeps 10 ReplicaSets for rollback. If you see many, set revisionHistoryLimit in your Deployment spec to a lower number (e.g., 3) to keep the cluster tidy.

  4. CrashLoopBackOff after update: This usually means the new pod template is broken (bad command, missing env). The Deployment will keep trying, but the ReplicaSet shows high PODS but no READY. Fix the image/template and re-apply.

  5. 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=0 as a last resort.

Pro tip: Always use kubectl rollout status deployment/pyweb to 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 deployment instead, 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

  1. You can use kubectl rollout undo deployment/pyweb to roll back to a previous ReplicaSet, leveraging the stored history.
  2. For more advanced replica management, use a HorizontalPodAutoscaler (HPA) that adjusts the ReplicaSet's desired replicas based on CPU/memory.
  3. 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 app and pod-template-hash are 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.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.