Kubernetes Administration — Level by Level›03 · Pods, Deployments & workload types

Lesson 03 of 32 · Level 1 — Foundations

Pods, Deployments & workload types

Pods are the atoms of Kubernetes; Deployments, StatefulSets, DaemonSets, Jobs and CronJobs are the molecules. Learn when to use each, and how rollouts and rollbacks really work.

Beginner → Practitioner
Key wordsPodDeploymentReplicaSetrolling updaterollbackStatefulSetDaemonSetJobCronJob
Deployment replicas: 3 image: web:v2 ReplicaSet (v1) scaling down → 0 ReplicaSet (v2) scaling up → 3 rolling update pod v1 pod v1 pod v2-1 pod v2-2 pod v2-3 Controllers watch & fix: want 3, have 2? you change the Deployment; controllers create ReplicaSets, ReplicaSets create pods
Deployment → ReplicaSets → pods: how a rolling update swaps versions.

Pods: the smallest thing you can run

A Pod is one or more containers that are always scheduled together, share one IP address, and can share storage. Most pods have one container. Extra ones are helpers (sidecars), such as a log shipper or a proxy.

A container is a sandwich. A Pod is the lunchbox. Usually one sandwich per box, but sometimes you add an apple (a helper container). Everything in the same lunchbox goes to the same table (node), and they share the same label with your name on it (the IP address).

Save this as pod.yaml:

apiVersion: v1          # which API group/version this object belongs to
kind: Pod               # what kind of object
metadata:
  name: hello
  labels:
    app: hello          # labels are how other objects find this pod
spec:
  containers:
    - name: web
      image: nginx:1.27
      ports:
        - containerPort: 80
$ kubectl apply -f pod.yaml
pod/hello created
$ kubectl get pod hello -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP           NODE
hello   1/1     Running   0          8s    10.244.1.5   lab-worker
$ kubectl exec -it hello -- nginx -v
nginx version: nginx/1.27.x

Every Kubernetes object has the same four top-level fields: apiVersion, kind, metadata, spec. Learn those once and you can read any manifest.

Bare pods are not self-healing

Delete hello and it's gone for good. Nothing recreates it. In real life you almost never create bare pods. You create a controller (usually a Deployment) that creates pods for you and keeps them alive.

Pod statuses you'll meet

Status What it means First thing to check
Pending Accepted, not yet running kubectl describe pod: no node fits? PVC not bound?
ContainerCreating Scheduled; pulling image, mounting volumes Events: slow pull? volume attach?
Running At least one container running Is it Ready too? (READY 1/1)
Completed All containers exited 0 (normal for Jobs) Nothing, if it's a Job
ImagePullBackOff Can't pull the image Typo in image name/tag? Private registry credentials?
CrashLoopBackOff Starts, crashes, restarts with growing delays kubectl logs <pod> --previous
OOMKilled (in Last State) Used more memory than its limit Raise the limit or fix the leak

Deployments: keep N copies alive, and update them safely

A Deployment says: "keep this many identical pods running, and when I change the template, replace them gradually."

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web                 # manage pods with this label…
  template:                    # …created from this pod template
    metadata:
      labels:
        app: web               # must match the selector
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          ports:
            - containerPort: 80
          resources:
            requests: { cpu: 50m, memory: 64Mi }   # what the scheduler reserves
            limits: { memory: 128Mi }              # hard ceiling
          readinessProbe:                          # only send traffic when this passes
            httpGet: { path: /, port: 80 }
$ kubectl apply -f web.yaml
deployment.apps/web created
$ kubectl get deploy,rs,pods -l app=web
NAME                  READY   UP-TO-DATE   AVAILABLE
deployment.apps/web   3/3     3            3

NAME                             DESIRED   CURRENT   READY
replicaset.apps/web-6d4b9c8f7b   3         3         3

NAME                       READY   STATUS    RESTARTS
pod/web-6d4b9c8f7b-4hx9t   1/1     Running   0
pod/web-6d4b9c8f7b-9qm2c   1/1     Running   0
pod/web-6d4b9c8f7b-tz7lw   1/1     Running   0

See the chain: Deployment → ReplicaSet → Pods. The random suffix on the ReplicaSet is a hash of the pod template. Change the template and you get a new ReplicaSet.

Rolling update and rollback

$ kubectl set image deployment/web nginx=nginx:1.27-alpine
deployment.apps/web image updated
$ kubectl rollout status deployment/web
Waiting for deployment "web" rollout to finish: 1 out of 3 new replicas have been updated...
deployment "web" successfully rolled out
$ kubectl get rs -l app=web
NAME             DESIRED   CURRENT   READY
web-6d4b9c8f7b   0         0         0      ← old, kept for rollback
web-7f9c5d6b8d   3         3         3      ← new

Made a mistake? Roll back in one line:

$ kubectl set image deployment/web nginx=nginx:does-not-exist
$ kubectl get pods -l app=web
NAME                   READY   STATUS             RESTARTS
web-5c8d7b6f4-kx2vw    0/1     ImagePullBackOff   0
web-7f9c5d6b8d-4hx9t   1/1     Running            0
web-7f9c5d6b8d-9qm2c   1/1     Running            0
web-7f9c5d6b8d-tz7lw   1/1     Running            0
$ kubectl rollout undo deployment/web
deployment.apps/web rolled back

Notice the broken rollout never took down the running pods. The new pod never became Ready, so the Deployment stopped replacing old ones. That's the readiness probe and rolling strategy protecting you.

A Deployment is a team captain with a rule: "we always have 3 players on the field". To swap in players with new shirts, the captain brings one new player on, waits until they're ready to play, then takes one old player off. If a new player can't even find the pitch (bad image), the old players just keep playing.

The five workload types, and when to use each

Workload Use it for Everyday analogy
Deployment Stateless apps: web, APIs, workers Identical delivery riders: any one can be replaced by any other
StatefulSet Databases, queues, anything with an identity and its own disk Numbered football players, each with their own locker (db-0, db-1)
DaemonSet One pod per node: log agents, monitoring, CNI, kube-proxy One security guard on every floor, including new floors
Job Run to completion once: migrations, batch tasks Homework: done when it's finished
CronJob Jobs on a schedule: backups, reports An alarm clock that starts the homework every night

A Job that computes π to 200 digits:

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  backoffLimit: 4                # retry up to 4 times on failure
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: pi
          image: perl:5.34
          command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(200)"]

A CronJob that runs every minute:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"        # standard cron syntax
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: hello
              image: busybox:1.36
              command: ["sh", "-c", "date; echo Hello from Kubernetes"]
$ kubectl get cronjob,jobs,pods
$ kubectl logs job/pi
3.1415926535897932384626433832795028841971693993751058209749445923078164062862...

Try it: self-healing and rollbacks

  1. Apply the web Deployment above with 3 replicas.
  2. Delete one pod and immediately run kubectl get pods -l app=web -w. Watch the replacement appear.
  3. Scale to 6, then back to 2. Which pods get removed first?
  4. Break the image on purpose, confirm the old pods keep serving, then rollout undo.
  5. Run kubectl rollout history deployment/web and explain each revision.

Going deeper: rollout tuning and labels

  • The rolling strategy is controlled by maxSurge (extra pods allowed above replicas) and maxUnavailable (pods allowed below). Both default to 25%. For a 3-replica app that's effectively "one extra, none down". Explore with kubectl explain deployment.spec.strategy.rollingUpdate.
  • Readiness gates traffic; liveness restarts stuck containers; startup probes protect slow starters from liveness. Misconfigured liveness probes are a classic cause of self-inflicted outages.
  • spec.selector is immutable after creation. Plan labels up front (app, component, version); they're how Services, NetworkPolicies and monitoring find your pods.
  • kubectl rollout restart just sets an annotation with a timestamp on the pod template. That's enough to trigger a new ReplicaSet.

Recap

  • A Pod is the smallest unit: containers that share an IP and are scheduled together. Bare pods are not self-healing.
  • A Deployment manages ReplicaSets, which manage Pods by label. It gives you scaling, self-healing, rolling updates and rollbacks.
  • Pick the workload by the job: Deployment (stateless), StatefulSet (identity + disk), DaemonSet (per node), Job / CronJob (run to completion).
  • When something fails: describe for events, logs --previous for crashes.

This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.