CertGrid CertGrid
Installation·Certified Kubernetes Application Developer

The CKAD Practice Cluster

CKAD tasks assume things exist: a default StorageClass so a PVC binds, an ingress controller so an Ingress does something, a metrics-server so an HPA has a denominator. This is the inventory of the cluster every command in this path runs against, and it is worth reading once so you recognise which of your own failures are missing-prerequisite failures rather than mistakes.

Orientation Guide 2 of 44 Beginner

Written against the versions above. Two StorageClasses are present and `local-path` is the default, marked `(default)` in the output. `local-path` uses `WaitForFirstConsumer`, so a PVC stays `Pending` until a Pod actually mounts it - normal, and a common source of confusion in the storage guides later.

The four-node cluster the rest of this path uses, except where a guide says otherwise. Read-only here.
Server NameIP AddressOSRolesCPURAMHDD
CKA1001192.168.0.175Ubuntu 26.04 LTSControl Plane Node2 Core4 GB50 GB
CKA1001-NODE01192.168.0.176Ubuntu 26.04 LTSWorker Node2 Core4 GB50 GB
CKA1001-NODE02192.168.0.177Ubuntu 26.04 LTSWorker Node2 Core4 GB50 GB
CKA1001-NODE03192.168.0.178Ubuntu 26.04 LTSWorker Node2 Core4 GB50 GB

Before you start

  1. Four nodes, one of them tainted

    NAME             STATUS   ROLES           AGE   VERSION
    cka1001          Ready    control-plane   46h   v1.36.4
    cka1001-node01   Ready    <none>           46h   v1.36.4

    Three schedulable workers and a control plane carrying node-role.kubernetes.io/control-plane, which is why your Pods land on the workers without you asking. CKAD rarely cares which node anything runs on - but knowing there are three is useful the first time a Deployment's Pods spread out and you wonder whether that was deliberate.

    bash Example session
    kubectl get nodes -o wideNAME             STATUS   ROLES           AGE   VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE           KERNEL-VERSION             CONTAINER-RUNTIMEcka1001          Ready    control-plane   46h   v1.36.4   192.168.0.175   <none>        Ubuntu 26.04 LTS   7.0.0-30-generic (amd64)   containerd://2.2.6cka1001-node01   Ready    <none>          46h   v1.36.4   192.168.0.176   <none>        Ubuntu 26.04 LTS   7.0.0-29-generic (amd64)   containerd://2.2.6cka1001-node02   Ready    <none>          46h   v1.36.4   192.168.0.177   <none>        Ubuntu 26.04 LTS   7.0.0-30-generic (amd64)   containerd://2.2.6cka1001-node03   Ready    <none>          45h   v1.36.4   192.168.0.178   <none>        Ubuntu 26.04 LTS   7.0.0-29-generic (amd64)   containerd://2.2.6kubectl get nodes -o 'custom-columns=NODE:.metadata.name,TAINTS:.spec.taints[*].key'NODE             TAINTScka1001          node-role.kubernetes.io/control-planecka1001-node01   <none>cka1001-node02   <none>cka1001-node03   <none>

    Expected resultFour Ready nodes on the same version.

    Success conditionYou know the shape of the cluster underneath every later guide.

  2. The three things that make tasks possible

    NAME                   PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION
    csi-hostpath-sc        hostpath.csi.k8s.io     Delete          Immediate              true
    local-path (default)   rancher.io/local-path   Delete          WaitForFirstConsumer   false

    A default StorageClass. Without one, a PVC with no storageClassName stays Pending forever and the task looks broken when it is only unprovisioned.

    An IngressClass. An Ingress object with no controller behind it is accepted by the API server and does absolutely nothing - a failure with no error, covered later in the networking track.

    A metrics-server, which is why kubectl top returns numbers rather than Metrics API not available. An HPA needs it for the same reason.

    bash Example session
    kubectl get storageclassNAME                   PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGEcsi-hostpath-sc        hostpath.csi.k8s.io     Delete          Immediate              true                   38hlocal-path (default)   rancher.io/local-path   Delete          WaitForFirstConsumer   false                  44hkubectl get ingressclassNAME    CONTROLLER             PARAMETERS   AGEnginx   k8s.io/ingress-nginx   <none>       39hkubectl top nodesNAME             CPU(cores)   CPU(%)   MEMORY(bytes)   MEMORY(%)cka1001          105m         5%       2059Mi          62%cka1001-node01   70m          3%       1731Mi          52%cka1001-node02   67m          3%       1894Mi          57%cka1001-node03   47m          2%       1041Mi          31%helm version --shortv3.18.5+gb78692c

    Expected resultA default StorageClass, an nginx IngressClass, live metrics, Helm 3.

    Success conditionYou can tell a missing prerequisite from a mistake in your manifest.

  3. The part you are not responsible for

    The control plane runs as static Pods on cka1001, and the API server reports itself healthy:

    readyz check passed

    This is the last time this path looks at any of it. If readyz ever fails on your own cluster, that is a CKA problem - and worth knowing the command for anyway, because kubectl get --raw='/readyz?verbose' answers "is it me or is it the cluster" in one line.

    bash Example session
    kubectl -n kube-system get pods -o 'custom-columns=POD:.metadata.name,NODE:.spec.nodeName' --no-headers | grep -E 'apiserver|scheduler|controller|etcd'etcd-cka1001                           cka1001kube-apiserver-cka1001                 cka1001kube-controller-manager-cka1001        cka1001kube-scheduler-cka1001                 cka1001snapshot-controller-7fd654b88d-fqhhk   cka1001-node01snapshot-controller-7fd654b88d-mdk78   cka1001-node03kubectl get --raw='/readyz?verbose' | tail -12[+]poststarthook/aggregator-reload-proxy-client-cert ok[+]poststarthook/start-kube-aggregator-informers ok[+]poststarthook/apiservice-status-local-available-controller ok[+]poststarthook/apiservice-status-remote-available-controller ok[+]poststarthook/apiservice-registration-controller ok[+]poststarthook/apiservice-discovery-controller ok

    Expected resultControl-plane Pods listed, and readyz check passed.

    Success conditionYou have a one-command way to rule the cluster out.

Troubleshooting

Official sources