The KCNA Practice Cluster
Four nodes, Kubernetes 1.36.4, containerd 2.2.6 - and a kubectl one minor behind the server, which is supported and worth understanding rather than fixing.
Orientation Guide 2 of 46 Beginner
- Kubernetes1.36.4
- kubectlv1.35.2 (one minor behind, deliberately)
- containerdv2.2.6
- OSUbuntu 26.04 LTS
- TimeAbout 11 min
- Reviewed22 August 2026
Written against the versions above. Domain weights come from the published KCNA curriculum. Check the current one before you book - the Linux Foundation revises it.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| CKA1001 | 192.168.0.175 | Ubuntu 26.04 LTS | Control Plane Node | 2 Core | 4 GB | 50 GB |
| CKA1001-NODE01 | 192.168.0.176 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
| CKA1001-NODE02 | 192.168.0.177 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
| CKA1001-NODE03 | 192.168.0.178 | Ubuntu 26.04 LTS | Worker Node | 2 Core | 4 GB | 50 GB |
Before you start
- A cluster of your own. Any of the CKA installation guides builds one; a single node is enough for most of this path.
-
Four nodes, one version
Every node reports
v1.36.4, Ubuntu 26.04 LTS, andcontainerd://2.2.6. Uniformity is deliberate: a mixed-version cluster is a legitimate production state during an upgrade, and a bad place to learn, because you cannot tell whether odd behaviour is the concept or the skew.The
ROLEScolumn readsbecause this query asks for a label kubeadm does not set - the role is expressed asnode-role.kubernetes.io/control-planeinstead. Worth knowing before you go looking for a missing role: plainkubectl get nodesshows it correctly.bash Example session kubectl --context cka1001 get nodes -o custom-columns=NAME:.metadata.name,ROLES:.metadata.labels.kubernetes\.io/role,VERSION:.status.nodeInfo.kubeletVersion,OS:.status.nodeInfo.osImage,RUNTIME:.status.nodeInfo.containerRuntimeVersionNAME ROLES VERSION OS RUNTIMEcka1001 <none> v1.36.4 Ubuntu 26.04 LTS containerd://2.2.6cka1001-node01 <none> v1.36.4 Ubuntu 26.04 LTS containerd://2.2.6cka1001-node02 <none> v1.36.4 Ubuntu 26.04 LTS containerd://2.2.6cka1001-node03 <none> v1.36.4 Ubuntu 26.04 LTS containerd://2.2.6Expected resultFour nodes on the same Kubernetes version, OS and container runtime.
Success conditionAll nodes report the same version.
-
The client is a minor behind the server
client v1.35.2 / server v1.36.4This is supported, not broken. Kubernetes guarantees kubectl works within one minor version either side of the API server, so 1.35 against 1.36 is inside the window.
What it costs you is narrow and specific: a flag or subcommand added in 1.36 will not exist in your client, and the error looks like the feature is missing from the cluster. When a documented flag is rejected, check the client version before anything else.
The skew rules matter more generally, because they constrain how you upgrade: the kubelet may be up to three minors behind the API server, but never ahead. Control plane first, always.
bash Example session kubectl --context cka1001 version -o json | python -c "import json,sys; d=json.load(sys.stdin); print('client', d['clientVersion']['gitVersion'], '/ server', d['serverVersion']['gitVersion'])"client v1.35.2 / server v1.36.4Expected resultA client version within one minor of the server.
Success conditionYou know your own skew rather than assuming they match.
-
What is already on it
86 Pods and 32 Deployments across these namespaces - Calico, ingress-nginx, CSI drivers, the Prometheus stack, Argo CD, and the leftovers of earlier guides.
That matters for two reasons. First, this is what a *used* cluster looks like, and reading one is a skill in itself. Second, it explains why later guides create their own namespaces: on a cluster with this much on it, dropping test objects into
defaultbecomes impossible to clean up.If you are following along on a fresh cluster, expect far fewer Pods and do not try to match these numbers.
bash Example session kubectl --context cka1001 get ns --no-headers | awk '{print $1}' | tr '\n' ' 'argocd calico-system csid default guestbook gw hlm hpa-demo ing ingress-nginx kube-node-lease kube-public kube-system kz local-path-storage metallb-system mlb monitoring nginx-gateway npt npt-other psa-open psa-strict tigera-operator trikubectl --context cka1001 get deploy -A --no-headers | wc -l32kubectl --context cka1001 get pods -A --no-headers | wc -l86Expected resultA namespace list, and Deployment and Pod counts well above a fresh install.
Success conditionYou can see what is running before you add to it.
-
Is the control plane actually healthy
/readyz?verboseis the honest health check, and it is worth knowing becausekubectl get nodesshowing Ready tells you about kubelets, not about the API server.Each line is a separate check:
etcd okmeans storage is reachable,informer-sync okmeans the caches are warm, and the poststart hooks confirm initialisation finished. A failing check appears as[-]with a reason, which is far more useful than a timing-out kubectl.Run this first whenever the cluster feels wrong. It distinguishes "the API server is unhealthy" from "my request is being denied", which is the fork every investigation starts with.
bash Example session kubectl --context cka1001 get --raw='/readyz?verbose' | head -8[+]ping ok[+]log ok[+]etcd ok[+]etcd-readiness ok[+]informer-sync ok[+]poststarthook/start-apiserver-admission-initializer ok[+]poststarthook/generic-apiserver-start-informers ok[+]poststarthook/priority-and-fairness-config-consumer okExpected resultA list of checks, each
[+] ... ok.Success conditionEvery readiness check passes.
Troubleshooting
get --raw=/readyzreturns Forbidden.Why: The endpoint needs permission on the non-resource URL.
Fix:Run it as cluster-admin.
kubectl get --raw=/healthzis more widely permitted and gives a single ok/not-ok.A documented kubectl flag is rejected as unknown.
Why: Client version skew - the flag is newer than your kubectl.
Fix:
kubectl versionfirst. Upgrade the client independently of the cluster; it is a single binary and has no cluster impact.Nodes show
Readybut workloads will not schedule.Why: Ready is about the kubelet, not about capacity or taints.
Fix:
kubectl describe nodeand read Taints and Allocated resources. A control-plane node carries a NoSchedule taint by default.