Lab 12 - DaemonSets

Lab 12 - DaemonSets

Goal: one Pod per node, and the two surprises that come with it. Chapter: DaemonSets

# 1 - the cluster already runs several
kubectl get ds -A
kubectl get pods -n kube-system -o wide | grep -i -E 'proxy|calico|cilium|flannel'

kube-proxy and the CNI agent are DaemonSets: they must exist on every node, including nodes that join tomorrow. There is no replicas field anywhere - the node list is the count.

# 2 - your own
cd ~
cat > logger-ds.yaml <<'YAML'
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-logger
spec:
  selector:
    matchLabels:
      app: node-logger
  template:
    metadata:
      labels:
        app: node-logger
    spec:
      containers:
        - name: logger
          image: busybox
          command: ["sh", "-c", "while true; do echo \"$(date) $(hostname)\"; sleep 30; done"]
          resources:
            requests: { cpu: "10m", memory: "16Mi" }
YAML
kubectl apply -f logger-ds.yaml
kubectl get ds node-logger
kubectl get pods -l app=node-logger -o wide

Three Pods, one per worker - and none on control01.

# 3 - why control01 was skipped
kubectl describe node control01 | grep -i -A2 taint

node-role.kubernetes.io/control-plane:NoSchedule. A taint repels Pods that do not tolerate it. Ordinary workloads stay off the control plane by design.

# 4 - tolerate the taint and watch a fourth Pod appear
kubectl patch ds node-logger --type merge -p '{"spec":{"template":{"spec":{"tolerations":[{"key":"node-role.kubernetes.io/control-plane","operator":"Exists","effect":"NoSchedule"}]}}}}'
kubectl get pods -l app=node-logger -o wide

This is how the CNI and kube-proxy get onto the control plane: they tolerate the taint. Yours should not, normally - revert it in a moment.

# 5 - read the logs of one Pod per node
kubectl logs -l app=node-logger --tail=2 --prefix

--prefix puts the Pod name in front of each line, which is what makes label-selected logs readable across nodes.

# 6 - target a subset of nodes instead
kubectl label node worker01 storage=nfs
kubectl patch ds node-logger --type merge -p '{"spec":{"template":{"spec":{"nodeSelector":{"storage":"nfs"},"tolerations":[]}}}}'
kubectl get pods -l app=node-logger -o wide

One Pod, on worker01 only. nodeSelector is how storage or GPU agents are restricted to the machines that actually have the hardware.

# 7 - the drain interaction you will meet again in Lab 16
kubectl drain worker01 --ignore-daemonsets --dry-run=server 2>&1 | head -5

Drain never evicts DaemonSet Pods - the Pod belongs to the node, and the node is still there. That is why --ignore-daemonsets is on every drain command.

Clean up

# 8 -
kubectl delete -f logger-ds.yaml
kubectl label node worker01 storage-