hands-on labKubernetes in your browser

Workload Controllers: Deployment, StatefulSet, DaemonSet for AI

Three controller types, three workload shapes, three different production failure modes.

time
35 min
checked steps
5
level
Intermediate
setup
None

$29.99a month with Pro

Pro opens this lab and every other one. Cancel any time.

The job

A bare Pod is a single container's life, when it ends, you start over. Production AI workloads need controllers that reconcile desired state: keep N replicas running, give pods stable identities for distributed training, run exactly one pod per node for monitoring agents. This lab walks all three controller types via the workload shape they're designed for.

5 steps, each checked when you finish it

A check runs your work at the end of every step. Hints and the full solution are there if you get stuck.

  1. 1

    The controller pattern: why bare pods aren't production

    A bare Pod is a single container's life.

    read this step below ↓

  2. 2

    Deployment: the inference shape

    A Deployment is the right controller for stateless, horizontally-scalable workloads.

  3. 3

    StatefulSet: the distributed training shape

    A StatefulSet is a controller for workloads where each replica has an identity, a stable name, a stable network address, and (typically) its own persistent volume.

  4. 4

    DaemonSet: the per-node infrastructure shape

    A DaemonSet runs exactly one pod per matching node.

  5. 5

    Triage Day: three controllers, three controller-specific failures

    The lab platform has just deployed three workload controllers, one of each kind, that are all broken in different ways.

Step 1 as it appears in the lab

The lab’s own text. The hint and the solution stay inside the lab.

Step 1: The controller pattern: why bare pods aren't production

The controller pattern — why bare pods aren't production

A bare Pod is a single container's life. The kubelet runs it; when its container exits, the pod stays in Succeeded (exit 0) or Failed (non-zero) — but it does not restart unless you explicitly write restartPolicy: Always, and even then the pod doesn't come back if it gets evicted, deleted, or its node disappears. There's nothing reconciling "I want one of these running" — the bare Pod is a one-shot.

Production AI workloads need a controller that reconciles desired state:

  • "Keep 3 replicas of this inference server Running."
  • "Run exactly one DCGM exporter on every GPU node."
  • "Maintain 4 trainer pods with stable hostnames trainer-0 through trainer-3, started in order."

Three controllers cover ~95% of these patterns, and the AI workload shape determines which one:

ControllerShapeUsed for in AI
DeploymentN stateless replicas, random pod names, scale up/down freely, rolling updatesInference servers, API gateways, model proxies, anything that scales horizontally
StatefulSetN replicas with stable hostnames, ordered creation/deletion, per-pod PVCsDistributed training (PyTorch DDP, Horovod), parameter servers, anything where peers must address each other by name
DaemonSetOne pod per matching node, automatic scheduling on new nodesDCGM exporter, device plugin, GFD, node-local model caches, log/trace agents

A fourth controller, Job, runs N pods to completion (good for one-off training runs), and CronJob schedules Jobs — covered briefly in the FAQ but not the focus of this lab.

Read what's already running

The lab platform's own infrastructure includes a couple of controllers in kube-system. Run the step's command (or click Run) — it will:

  1. Apply a bare lonely-pod and watch it terminate (no controller, no restart).
  2. List every Deployment, StatefulSet, and DaemonSet in the cluster.
# What controllers exist in this cluster right now
kubectl get deploy,sts,ds -A

You'll see kube-system Deployments for the cluster's own infrastructure (CoreDNS, etc.). You won't see any user controllers — you'll be authoring each one in the next three steps.

How a Deployment works under the hood

A Deployment doesn't manage Pods directly — it manages a ReplicaSet, which manages Pods:

Deployment (you author this)
    │
    ↓ owns
ReplicaSet (auto-created by Deployment controller)
    │
    ↓ owns
Pods (auto-created by ReplicaSet controller, named <rs>-<random>)

When you kubectl apply a new Deployment image, a new ReplicaSet is created with the new template; the old ReplicaSet is scaled to 0. That's how rolling updates work — old RS → new RS, with overlap controlled by strategy.rollingUpdate. Reading the chain kubectl describe deploy → kubectl get rs → kubectl get pods is one of the most useful diagnostics on the platform.

How a StatefulSet differs

StatefulSet manages Pods directly (no intermediate ReplicaSet) and gives each one a stable name and identity — trainer-0, trainer-1, ... trainer-(N-1). The numbers are sequential, never randomly suffixed. The pods are created in order (0 → 1 → 2 ...) and each must be Ready before the next is started. Termination on scale-down is the reverse order (highest-numbered first). Each pod can have its own PVCs auto-created via volumeClaimTemplates — data-trainer-0, data-trainer-1, etc.

This shape is exactly what distributed training frameworks need: every rank knows the others' hostnames a priori, rank 0 starts first as the coordinator, and per-rank checkpoint volumes don't collide.

How a DaemonSet differs

DaemonSet doesn't have replicas — it has a target set of nodes (matched via nodeSelector or nodeAffinity). It runs one pod per matching node and automatically schedules a new pod when a matching node joins the cluster (or removes a pod when a node leaves). The status fields show this:

NUMBER-DESIRED   NUMBER-CURRENT   NUMBER-READY   NUMBER-AVAILABLE
3                3                3              3

If a DaemonSet's nodeSelector doesn't match any node, you get 0/0 — the controller didn't fail, it just has nothing to schedule. That's a common "missing pods" diagnostic.

Why this matters in practice: picking the wrong controller is one of the most common production mistakes — and the symptoms are subtle ("my distributed trainer can't find its peers" usually means someone used Deployment and the pods got random names). The NCA-AIIO exam tests the workload-shape-to-controller mapping directly. The next four steps make each one concrete.

what you get

  • A Kubernetes cluster and terminal in your browser
  • A check at the end of every step
  • Hints and the full solution
  • Nothing to install or configure

before you start

  • Completed `nca-aiio-resource-requests-limits` and `nca-aiio-priority-preemption` (or comfortable with pod requests, QoS, PriorityClass)
  • Familiar with `kubectl get`, `describe`, and reading owner-references / ReplicaSet relationships

Exam domains covered

AI Infrastructure & OperationsWorkload Management

Part of the GPU and Kubernetes track

Lab 7 of 20. GPU scheduling on your own Kubernetes cluster, the GPU Operator, CUDA kernels and profiling.

Questions about this lab

Why use a StatefulSet instead of a Deployment for distributed training?

Three reasons unique to StatefulSet. (1) Stable network identity, each pod gets a predictable DNS name like trainer-0.trainer-svc.default.svc.cluster.local (paired with a headless Service). Distributed training frameworks (PyTorch DDP, NCCL all-reduce, Horovod) need every worker to discover every other worker by hostname; random Deployment pod names make that brittle. (2) Ordered creation and termination, trainer-0 becomes Ready before trainer-1 starts. The rank-0 worker is conventionally the coordinator; ordered start ensures it's up before the others try to reach it. (3) Per-pod PVCs via volumeClaimTemplates, each pod gets its own PVC automatically (e.g., data-trainer-0, data-trainer-1), so per-rank checkpoints don't collide. None of these guarantees come with a Deployment.

When should I NOT use a StatefulSet?

For stateless replicas, model inference servers, API gateways, function workers, Deployment is correct. StatefulSet's ordered creation is slower on scale-up (you wait for each pod to be Ready before the next starts), and pod identity is overhead you don't need. The rule of thumb: do your replicas have any requirement beyond "be one of N identical workers"? If yes (stable hostname, ordered start, per-pod storage), StatefulSet. If no, Deployment. The most common "wrong choice" is using a StatefulSet for inference because someone wanted "stable pod names", which they almost certainly don't need.

What workloads should be DaemonSets?

Anything that should run exactly one pod per matching node for the lifetime of that node. The classic AI examples: nvidia-dcgm-exporter (GPU metrics, one per GPU node), nvidia-device-plugin (advertises nvidia.com/gpu capacity, one per GPU node), gpu-feature-discovery (writes node labels, one per GPU node), node-local model caches that pre-pull weights, log/trace collectors. DaemonSets target nodes via nodeSelector or nodeAffinity and automatically schedule a new pod when matching nodes join the cluster. They're also exempt from default kube-scheduler priority, DaemonSet pods bind directly to specific nodes via the daemonset controller, not through the regular scheduling path.

Why is my DaemonSet missing pods on some nodes?

Three common reasons. (1) nodeSelector doesn't match, your spec targets nvidia.com/gpu.product=NVIDIA-A100 but those nodes have Tesla-K80. Run kubectl get nodes --show-labels | grep nvidia to see what's actually labeled. (2) Node taints with no matching toleration, control-plane nodes typically have node-role.kubernetes.io/control-plane:NoSchedule; your DaemonSet needs a toleration to run there. The nvidia.com/gpu=present:NoSchedule taint is similarly common in production GPU pools. (3) Pod resource requests exceed the node's allocatable, DaemonSet pods compete for resources the same way; if your pod requests 8Gi memory and the node has 7Gi allocatable, the pod stays Pending. Diagnose with kubectl describe ds <name> (status fields show desired/current/numberReady) and kubectl describe pod <ds-pod> (events explain the per-node failure).

What you'll learn

Kubernetes ships with three workload controllers that account for ~95% of production AI workloads: Deployment for stateless inference, StatefulSet for distributed training pods that need stable identity and ordered start, DaemonSet for per-node infrastructure (DCGM exporters, GPU monitors, node-local model caches). Each one is a different shape with a different reconciliation contract, the controller chooses the right shape, not the engineer's preference. Picking the wrong one is one of the most common production mistakes; symptoms range from "my distributed trainer can't find its peers" to "my DaemonSet is missing nodes."

This lab teaches the three controllers from the angle that matters most for an NCA-AIIO platform engineer: which AI workload shape does each one fit? You'll author a Deployment for a stateless inference service, a StatefulSet for a 3-replica distributed-training pool with stable hostnames and per-pod PVCs, and a DaemonSet for a GPU-node monitoring agent. You finish with a Triage Day where each of the three controllers is broken in a controller-specific way you'd actually see in production, and you fix each by reading the cluster's response.