Convert Deployments to a DaemonSet and a StatefulSet
Namespace `platform` runs a node agent as a Deployment, so it only covers one node. The queue team drafted `/root/queue.yaml` as a Deployment, but each replica needs a stable name and its own storage. This cluster has a control-plane node and a worker node.
Live lab access
This task description is public. Live practice requires full access unless this is the selected free weekly task. AI Lab Coach is available in single-task labs, including the free weekly task. See this week's free task.
Create an account to continue →
On a phone? Read the task and create your account here, then sign in on a desktop or laptop for the terminal lab. Nothing starts until you choose Start.
The task
Namespace platform runs a node agent as a Deployment, so it only covers one node. The queue team drafted /root/queue.yaml as a Deployment, but each replica needs a stable name and its own storage. This cluster has a control-plane node and a worker node.
Requirements
1. Replace Deployment node-agent with a DaemonSet node-agent that uses the same Pod template and runs one Pod on every node, including the control-plane node. Remove the Deployment.
2. Create a headless Service queue in platform that selects app=queue on port 9000.
3. Run the queue from /root/queue.yaml as StatefulSet queue with 2 replicas, governed by Service queue, giving each replica its own 64Mi ReadWriteOnce volume named data from the default StorageClass, mounted at /data.
4. Ensure queue-0 and queue-1 are ready.
Original Prepium guide · Written for Kubernetes v1.35 and checked against the official documentation on 6 October 2026. Use a disposable practice cluster. It needs a default StorageClass and, ideally, a control-plane node plus at least one worker.
Pick the controller by the guarantee you need
All workload controllers create Pods from a template. What differs is the promise each one makes about those Pods, and exam questions are usually written so that only one promise fits. Ask what the Pods need: interchangeable copies, a fixed identity with private storage, one copy per node, or a run that finishes.
Deployment N identical, replaceable Pods random names, shared or no storage
StatefulSet N Pods with identity name-0..N-1, own PVC each, needs headless Service
DaemonSet 1 Pod per eligible node follows nodes, taints decide eligibility
Job Pods that run to completion restartPolicy Never or OnFailure
1. StatefulSet: identity comes from the name, the Service and the claim
Save this as kv.yaml. It pairs a headless Service (clusterIP: None) with a StatefulSet that names it in serviceName, and gives each replica its own volume through volumeClaimTemplates. No storageClassName is set, so the default StorageClass provisions the volumes.
apiVersion: v1
kind: Service
metadata:
name: kv-peers
namespace: prepium-tutorial
spec:
clusterIP: None
selector:
app: kv
ports:
- name: http
port: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: kv
namespace: prepium-tutorial
spec:
serviceName: kv-peers
replicas: 3
selector:
matchLabels:
app: kv
template:
metadata:
labels:
app: kv
spec:
containers:
- name: server
image: nginx:1.27-alpine
ports:
- name: http
containerPort: 80
volumeMounts:
- name: state
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: state
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Mi
kubectl create namespace prepium-tutorial
kubectl apply -f kv.yaml
kubectl -n prepium-tutorial get pods -l app=kv -w
kubectl -n prepium-tutorial get pvc
Pods appear as kv-0, kv-1, kv-2, and under the default OrderedReady policy each waits for the previous one to be Running and Ready. The claims are named after template, StatefulSet and ordinal: state-kv-0 and so on. A StatefulSet does not create its governing Service for you; that is your job.
2. Prove the identity survives a restart
kubectl -n prepium-tutorial exec kv-1 -- sh -c 'echo written-by-kv-1 > /usr/share/nginx/html/id.txt'
kubectl -n prepium-tutorial delete pod kv-1
kubectl -n prepium-tutorial wait --for=condition=Ready pod/kv-1 --timeout=120s
kubectl -n prepium-tutorial exec kv-1 -- cat /usr/share/nginx/html/id.txt
kubectl -n prepium-tutorial run dns --image=busybox:1.28 --restart=Never --rm -it -- nslookup kv-1.kv-peers
The replacement Pod has the same name, remounts state-kv-1, and still has the file. Each Pod also gets a DNS name of the form pod.service.namespace.svc.cluster.local, which is what clustered software uses to find specific peers. By default, deleting or scaling down the StatefulSet leaves the PVCs behind on purpose.
3. DaemonSet: one Pod per node, if the node's taints allow it
A DaemonSet has no replica count; the node list is the count. kubeadm taints control-plane nodes with node-role.kubernetes.io/control-plane:NoSchedule, so a DaemonSet skips them unless its Pod template tolerates that taint. To see it, write a small DaemonSet of your own (say node-echo, running busybox:1.36 with sleep 3600), apply it once without and once with this block in the Pod template:
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
kubectl describe nodes | grep -i taints
kubectl -n prepium-tutorial get ds
kubectl -n prepium-tutorial get pods -o wide
Compare the DaemonSet's DESIRED count with the number of nodes, and the NODE column with your node names. The controller adds some tolerations by itself (for not-ready, unreachable, pressure and unschedulable conditions), but not this one.
4. Job: when "done" is the goal
If the work ends, use a Job (or a CronJob for a schedule). A Deployment would restart a finished container forever, because its Pods must use restartPolicy: Always.
Common mistakes
- Leaving
replicasor a Deployment-stylestrategyin a manifest converted to a DaemonSet. Neither field exists there (DaemonSets useupdateStrategy), so kubectl's strict validation rejects the object. serviceNamepointing at a Service that does not exist or has a ClusterIP. Per-Pod DNS names never resolve.- Selector and template labels that differ. The API rejects the StatefulSet or DaemonSet at creation.
- No default StorageClass and no matching PV. Claims stay Pending and
kv-0never starts, which blocks every later ordinal.
| Example check | Evidence |
|---|---|
| Every node runs the agent | DaemonSet DESIRED and READY equal the node count |
| Headless governing Service | spec.clusterIP: None and StatefulSet serviceName match |
| Per-replica storage | One Bound PVC per ordinal with the requested size and mode |
| Stable identity | Recreated Pod keeps its name and its data |
Cleanup: kubectl delete namespace prepium-tutorial (this also removes the leftover PVCs in it). References: StatefulSets, DaemonSet, StatefulSet basics. Next, try CronJobs or taints and tolerations.
Practice scope
This additional curriculum lab is available in single-task practice with the existing 12-minute limit. The three CKAD mocks keep their original 17-task sets. Hints, an explained solution and requirement-by-requirement feedback are available in the lab.
Browse CKAD practice tasks →