Core concepts · Workloads

Pods, Deployments & Rollouts: How Your App Actually Runs

The objects you'll write every day. Pods wrap your containers, Deployments keep the right number running and replace them safely, and probes tell Kubernetes when a container is really ready. Plus configuration, and a rolling update step by step.

Module: 05 of 20 Level: Core concepts · Workloads Time: 13 min read Updated:
All modules in this track (20)

In this module, you'll learn to:

  • Explain why pods are disposable, and why you create Deployments rather than pods
  • Set resource requests and limits, and readiness and liveness probes
  • Follow a rolling update step by step, and roll back

Before you start: read How a Cluster Works first. This module assumes the idea of desired state and controllers, and that you know how to build and run a container image.

Principle

A Pod is a wrapper around your container, and it's disposable

You never run a container on Kubernetes directly. You run a Pod: the smallest thing the cluster schedules. A pod holds one container (occasionally a few that must live together, such as an app plus a log-shipping helper), and everything in it shares one IP address and can share storage.

The key fact about pods is that they're disposable. When a node fails, a pod is evicted, or you deploy a new version, the old pod is deleted and a new one is created, with a new name and a new IP address. Nothing ever repairs a pod in place. So you almost never create pods yourself. You create a Deployment, and let it create and replace pods for you.

Who owns what: the chain from Deployment to container
Deployment orders-apiyou write this: image, replica count, update strategy
creates and manages one ReplicaSet per version of the pod template
ReplicaSet orders-api-7c9dkeeps exactly 3 pods matching this version running
creates or deletes pods to match the count
Pod 1
orders-api-7c9d-a1container: orders-api:1.4, IP 10.0.12.34
Pod 2
orders-api-7c9d-b2container: orders-api:1.4, IP 10.0.40.18
Pod 3
orders-api-7c9d-c3container: orders-api:1.4, IP 10.0.71.9
Pattern

A Deployment you can actually run in production

This is a complete, realistic Deployment. Every field below the image is there for a reason, and each is explained after the listing.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: orders-api
  namespace: orders
spec:
  replicas: 3
  selector:
    matchLabels:
      app: orders-api            # which pods this Deployment owns
  strategy:
    rollingUpdate:
      maxSurge: 1                # at most 1 extra pod during an update
      maxUnavailable: 0          # never drop below 3 ready pods
  template:                      # the pod template: what each pod looks like
    metadata:
      labels:
        app: orders-api
    spec:
      containers:
        - name: orders-api
          image: <account>.dkr.ecr.<region>.amazonaws.com/orders-api@sha256:<digest>
          ports:
            - containerPort: 8080
          envFrom:
            - configMapRef:
                name: orders-api-config
          resources:
            requests: { cpu: 250m, memory: 256Mi }
            limits: { memory: 512Mi }
          readinessProbe:
            httpGet: { path: /healthz/ready, port: 8080 }
            periodSeconds: 5
          livenessProbe:
            httpGet: { path: /healthz/live, port: 8080 }
            initialDelaySeconds: 15
            periodSeconds: 10
Scroll sideways to see the full diagram →
  • selector and labels are how Kubernetes connects objects. The Deployment owns every pod labelled app: orders-api, and a Service will find the pods by the same label in the next module.
  • resources says how much CPU and memory each container needs, and the most it may use. The next section explains it.
  • The image is pinned by digest, so every pod runs exactly the same bytes. Container Delivery explains why.
Resources

Requests and limits

Every container should say how much CPU and memory it needs. Kubernetes uses two numbers:

  • Requests are a reservation. The scheduler only places a pod on a node with that much unreserved CPU and memory left. If no node has room, the pod waits in Pending.
  • Limits are a ceiling. A container that uses more CPU than its limit is slowed down (throttled). A container that uses more memory than its limit is killed and restarted, shown as OOMKilled.

CPU is measured in cores, so 250m (250 millicores) is a quarter of a CPU. Memory uses binary units: 256Mi is 256 mebibytes, 1Gi is one gibibyte.

SettingWhat happensIf it's wrong
CPU requestReserves CPU on a node for schedulingToo high wastes nodes; too low packs pods onto busy nodes
Memory requestReserves memory on a node for schedulingToo low lets nodes run out of memory and evict pods
CPU limitThrottles the container above itToo low makes the app slow even when the node is idle
Memory limitKills the container above itToo low causes restarts under normal load

A good starting point is the one in the Deployment above: CPU and memory requests based on real usage, a memory limit, and often no CPU limit, so a busy pod can borrow idle CPU. Requests also drive autoscaling and most of the cloud bill, which is why Scaling & Cost comes back to them in depth.

# Real usage per pod (needs metrics-server; see the add-ons module)
kubectl top pods

# Why is my pod Pending? The Events section names the missing resource
kubectl describe pod <pod-name>
Scroll sideways to see the full diagram →
Probes

How Kubernetes knows your app is ready, and still alive

A container process being started doesn't mean the app is ready. It may still be loading config, warming a cache, or connecting to a database. Probes let the app tell Kubernetes the truth.

ProbeQuestion it answersWhat happens if it fails
Readiness"Can this pod take traffic right now?"The pod is taken out of its Service's endpoints, so it gets no requests. It isn't restarted.
Liveness"Is this container stuck beyond repair?"The kubelet kills and restarts the container.
Startup"Has a slow app finished starting yet?"Holds off the liveness probe until startup succeeds, so slow starters aren't killed mid-boot.

The most common mistake is making the liveness probe check a dependency, such as the database. When the database has a blip, every pod fails liveness at once, and Kubernetes restarts your whole app, which makes the outage worse. Liveness should only check the process itself. Put dependency checks in readiness.

Flow

A rolling update, step by step

Change the image in the Deployment, and the Deployment controller replaces pods gradually, never dropping below the number of ready pods you asked for. With maxSurge: 1 and maxUnavailable: 0, it looks like this:

Rolling update from v1.4 to v1.5 with 3 replicas
Start3 pods on v1.4, all ready and serving traffic
you change the image to v1.5 and apply
Surgea new ReplicaSet starts 1 pod on v1.5 (4 pods for a moment)
the v1.5 pod passes its readiness probe and joins the Service
Swapthe old ReplicaSet scales down by 1 v1.4 pod
repeat, one pod at a time
Done3 pods on v1.5; the old ReplicaSet stays at 0 pods, kept for rollback

If a new pod never becomes ready, the rollout simply stops at that step: old pods keep serving and users aren't affected. That's the safety net readiness probes give you, and it's why a Deployment without a readiness probe can take a working service down with a bad release.

# Watch a rollout until it finishes, or report that it failed
kubectl rollout status deployment/orders-api -n orders

# See the ReplicaSets: one per version, the old one scaled to 0
kubectl get replicasets -n orders -l app=orders-api

# Go back to the previous version, reusing the kept ReplicaSet
kubectl rollout undo deployment/orders-api -n orders
Scroll sideways to see the full diagram →
Configuration

ConfigMaps and Secrets

Build one image and configure it per environment, rather than baking settings into the image. A ConfigMap holds plain settings (log level, feature flags, URLs) and can be injected as environment variables, as in the Deployment above, or mounted as files.

apiVersion: v1
kind: ConfigMap
metadata:
  name: orders-api-config
  namespace: orders
data:
  LOG_LEVEL: info
  PAYMENTS_URL: http://payments.payments.svc.cluster.local
Scroll sideways to see the full diagram →

A Kubernetes Secret looks similar, but its values are only base64-encoded, not encrypted, so anyone who can read Secrets in that namespace can read the values. For real credentials, the better pattern is to keep them in AWS Secrets Manager and sync them in, covered in Workload Identity & Secrets. One gotcha: pods read environment variables once, at start-up. Changing a ConfigMap doesn't change running pods until they restart (kubectl rollout restart deployment/orders-api -n orders).

Deployments suit apps whose pods are interchangeable and keep no data of their own. Databases, node agents, and one-off or scheduled tasks need other controllers (StatefulSets, DaemonSets, Jobs, and CronJobs), covered in Storage & Other Workload Types.

Implementation notes
  • Always set a readiness probe on anything that receives traffic. Without one, a pod counts as ready the moment its process starts.
  • Always set memory requests, and a memory limit. A container that goes over its memory limit is killed (OOMKilled). Without a limit, one leaking pod can starve every other pod on the node.
  • Run at least two replicas of anything users depend on. A single pod means every deploy, node replacement, or crash is an outage.
  • Use kubectl explain to learn any field. For example, kubectl explain deployment.spec.strategy prints the documentation for that field, straight from your cluster's version.
Recap

Key terms

  • Pod: the smallest unit Kubernetes runs; one container, occasionally a few, sharing one IP address.
  • Deployment: keeps a set number of identical pods running, and replaces them safely.
  • ReplicaSet: the object a Deployment creates for each version, to hold the pod count.
  • Request and limit: the CPU and memory reserved for scheduling, and the most a container may use.
  • Readiness probe: "can this pod take traffic?" Failing removes it from its Services.
  • Liveness probe: "is this container stuck?" Failing restarts it.
  • ConfigMap: plain settings injected into pods as environment variables or files.
Check yourself

Pop quiz

Five questions: three on the ideas in this module, and two scenarios where you apply them. The order changes every time you take it, and 4 out of 5 passes.

5 questions. Decide on your answer to each, then check it.

  1. Scenario

    Your liveness probe checks the database, and the database has a 30-second blip. What happens?

    1. Nothing; probes only run at start-up
    2. Pods stop receiving traffic until the database recovers
    3. Every pod fails liveness and is restarted, making the outage worse
    4. Kubernetes fails over to another database
    Show answer

    Every pod fails liveness and is restarted, making the outage worse

    Liveness failures restart containers, so a shared dependency failing restarts everything at once. Put dependency checks in readiness, and keep liveness to the process itself.

  2. Scenario

    During a rolling update with maxUnavailable: 0, the new version's pods never pass readiness. What do users see?

    1. Nothing changes: the old pods keep serving and the rollout stops
    2. An outage until someone rolls back
    3. Half the requests fail
    4. The new pods get traffic anyway after a timeout
    Show answer

    Nothing changes: the old pods keep serving and the rollout stops

    New pods only replace old ones once they're ready. If they never become ready, the rollout stalls and the old version keeps serving. That's the safety net probes give you.

  3. Concept

    A container goes over its memory limit. What happens?

    1. It's slowed down until usage drops
    2. It's killed and restarted, shown as OOMKilled
    3. The pod moves to a bigger node
    4. Nothing; memory limits are only advisory
    Show answer

    It's killed and restarted, shown as OOMKilled

    Going over a memory limit kills the container; going over a CPU limit only throttles it. That difference is why memory limits need care.

  4. Concept

    What does the scheduler use to decide whether a pod fits on a node?

    1. The pod's limits
    2. The pod's actual usage over the last hour
    3. The pod's requests
    4. Only the number of pods already on the node
    Show answer

    The pod's requests

    Requests are a reservation. The scheduler places a pod only where its requested CPU and memory are still unreserved, whatever the pod actually uses.

  5. Concept

    You change a ConfigMap value that pods read as an environment variable. When do running pods see it?

    1. Immediately
    2. Within a minute, automatically
    3. Only after they restart, for example with kubectl rollout restart
    4. Never; you must create a new ConfigMap
    Show answer

    Only after they restart, for example with kubectl rollout restart

    Environment variables are read once, when the container starts. Restart the Deployment to pick up the new value.

Questions about this learning path?

I'm happy to go into more detail on any of this: how the pieces fit together, ingress costs, RBAC design, or how I triage incidents.

anupojuprudhvi@gmail.com