# CKA Range Bring-Up — kind + Cilium (matches Mock Exam Pack v1) > Produces cluster `drills` on Kubernetes **v1.35.0** (= exam version) with nodes named exactly as the drill/mock packs expect: `drills-control-plane`, `drills-worker`, `drills-worker2`. > CNI: **Cilium 1.19.x** — real NetworkPolicy enforcement (kindnet has none; netpol drills would silently pass on it). > Versions verified 2026-07-29. Re-check pins if you're reading this much later: kind releases page, cilium.io stable docs. --- ## 0. Host prerequisites - Docker (or compatible), ≥ 8GB RAM free for the 3 nodes - **cgroup v2 on the host** — mandatory: k8s 1.35 node images dropped cgroup v1. Check: `stat -fc %T /sys/fs/cgroup` → must print `cgroup2fs`. Any current distro qualifies. - Tools: `kind` ≥ v0.31.0 (ships the v1.35.0 default image), `kubectl`, `helm` ```bash kind version # v0.31.x helm version # v3.x ``` ## 1. Cluster config — `kind-drills.yaml` ```yaml kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 name: drills # ⇒ node names drills-control-plane / drills-worker / drills-worker2 networking: disableDefaultCNI: true # kindnet out, Cilium in nodes: - role: control-plane image: kindest/node:v1.35.0 # pin explicitly = exam version, survives kind upgrades - role: worker image: kindest/node:v1.35.0 - role: worker image: kindest/node:v1.35.0 ``` Note: kube-proxy stays (default). Do NOT enable Cilium's kube-proxy replacement — the exam cluster runs kube-proxy, and iptables-visible Services are part of the troubleshooting surface you're training. ## 2. Bring-up — `range-up.sh` ```bash #!/usr/bin/env bash set -euo pipefail CILIUM_VER=1.19.6 kind create cluster --config kind-drills.yaml # nodes will sit NotReady until CNI lands — expected # --- Cilium (per cilium.io kind guide) --- docker pull quay.io/cilium/cilium:v${CILIUM_VER} kind load docker-image quay.io/cilium/cilium:v${CILIUM_VER} --name drills # skip registry pulls on each node helm repo add cilium https://helm.cilium.io/ 2>/dev/null; helm repo update helm install cilium cilium/cilium --version ${CILIUM_VER} \ --namespace kube-system \ --set image.pullPolicy=IfNotPresent \ --set ipam.mode=kubernetes kubectl -n kube-system rollout status ds/cilium --timeout=180s kubectl wait --for=condition=Ready node --all --timeout=180s # --- Gateway API CRDs (standard channel) --- kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml # --- metrics-server (for HPA TARGETS + kubectl top) --- kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml kubectl -n kube-system patch deploy metrics-server --type=json \ -p='[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]' # ^ mandatory on kind: kubelet serves self-signed certs; without this metrics-server never goes Ready # --- helm repo used by drill K4 / mock Q8 --- helm repo add bitnami https://charts.bitnami.com/bitnami 2>/dev/null; helm repo update echo "RANGE UP" ``` ## 3. Smoke test — run before ANY drill session ```bash kubectl get nodes -o wide # drills-control-plane / drills-worker / drills-worker2 — all Ready, VERSION v1.35.0 kubectl get pods -A | grep -v Running | grep -v Completed # empty kubectl api-resources | grep -i httproute # gateway.networking.k8s.io present kubectl top nodes # numbers (may need ~60s after install) kubectl -n kube-system exec ds/cilium -- cilium status --brief # OK # NetworkPolicy ENFORCEMENT check — the one that matters (kindnet would pass traffic anyway): kubectl create ns smoke kubectl -n smoke run a --image=nginx --labels=app=a --port=80 kubectl -n smoke run b --image=busybox -- sleep 600 kubectl -n smoke wait --for=condition=Ready pod --all --timeout=60s kubectl -n smoke apply -f - <<'EOF' apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: {name: deny-all} spec: podSelector: {} policyTypes: [Ingress] EOF AIP=$(kubectl -n smoke get pod a -o jsonpath='{.status.podIP}') kubectl -n smoke exec b -- wget -qO- -T 2 $AIP && echo "FAIL: netpol NOT enforced" || echo "OK: netpol enforced" kubectl delete ns smoke --wait=false ``` If that last line prints FAIL, the CNI install went sideways — do not run K1/Q3 until fixed (`kubectl -n kube-system logs ds/cilium | tail`). ## 4. Static pod path sanity (mock Q17 depends on it) ```bash docker exec drills-worker grep staticPodPath /var/lib/kubelet/config.yaml # expect: staticPodPath: /etc/kubernetes/manifests ``` ## 5. Teardown / rebuild ```bash kind delete cluster --name drills # full nuke, ~10s # rebuild = run range-up.sh again, ~3 min total ``` Rebuild is cheap — prefer a fresh cluster over archaeologically cleaning a broken one between mock attempts. The mock pack's RESET script is for same-day reruns; a new day gets a new cluster. ## 6. Known deltas vs the real exam (accept, don't fight) | Real exam | This range | |---|---| | `ssh nodeX` | `docker exec -it drills- bash` | | multiple kubectl contexts | one context; discipline = read each task's target ns/node | | kubeadm package upgrades (apt) | not possible — node image is baked; upgrade tasks stay on KodeKloud/killer | | several CNIs possible | Cilium everywhere (netpol semantics are standard, so drills transfer) |