Based on: Kubernetes v1.35. Commands checked on a kubeadm cluster (v1.34.12 → v1.35.9, 3 libvirt VMs)
Summary
CKA commands and YAML in one place. For the kubectl basics shared with CKAD (Pod, Deployment, ConfigMap, Service, probes, etc.), see the CKAD cheat sheet.
What’s different from CKAD: the curriculum includes node preparation, kubeadm, etcd, and component troubleshooting, so you’ll often ssh into a node and deal with the kubelet, containerd, and static Pod manifests directly. Be ready for a dead apiserver where kubectl doesn’t work.
| Domain | Weight |
|---|---|
| Troubleshooting | 30% |
| Cluster Architecture, Installation and Configuration | 25% |
| Services and Networking | 20% |
| Workloads and Scheduling | 15% |
| Storage | 10% |
Check which sites you can open in the Linux Foundation’s allowed resources list before the exam.
Exam environment
sshto the host given for each task,exitwhen done. Node work withsudo -iorsudo- if the task says to switch context,
kubectl config use-contextfirst - namespace with
-n <ns>on every command
k run tmp --image=nginx --dry-run=client -o yaml > pod.yaml
k explain pod.spec.affinity.nodeAffinity
k explain pod.spec --recursive | less
Paths you’ll check often on a node
| Path | Contents |
|---|---|
/etc/kubernetes/manifests/ | static Pods (etcd, kube-apiserver, kube-controller-manager, kube-scheduler) |
/etc/kubernetes/pki/ | CA and certificates. etcd-only ones under etcd/ |
/etc/kubernetes/admin.conf | admin kubeconfig |
/etc/kubernetes/kubelet.conf | kubelet kubeconfig |
/var/lib/kubelet/config.yaml | kubelet config (staticPodPath, clusterDNS, cgroupDriver) |
/usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf | kubelet systemd drop-in |
/etc/containerd/config.toml | containerd config (SystemdCgroup) |
/etc/cni/net.d/, /opt/cni/bin/ | CNI config, binaries |
/var/log/pods/, /var/log/containers/ | container log files |
Cluster Architecture, Installation and Configuration
Curriculum: RBAC, preparing infrastructure for installation, installing and managing with kubeadm, cluster lifecycle, HA control plane, installing components with Helm and Kustomize, CNI/CSI/CRI, CRDs and Operators.
Notes: cluster architecture, kubeadm install and HA, upgrade, etcd, certificates, RBAC and users, CNI, CSI, CRI, Operators
Node preparation
swapon --show # should be empty (or kubelet failSwapOn: false)
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
containerd config default | sudo tee /etc/containerd/config.toml >/dev/null
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl restart containerd
Packages (pkgs.k8s.io, one repo per minor)
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.35/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.35/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update && sudo apt-get install -y kubelet kubeadm kubectl cri-tools
sudo apt-mark hold kubelet kubeadm kubectl
sudo systemctl enable --now kubelet
kubeadm
sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=<ip>
sudo kubeadm init --control-plane-endpoint "<lb>:6443" --upload-certs --pod-network-cidr=... # HA
mkdir -p $HOME/.kube && sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config && sudo chown $(id -u):$(id -g) $HOME/.kube/config
sudo kubeadm token create --print-join-command
sudo kubeadm join <ep>:6443 --token <t> --discovery-token-ca-cert-hash sha256:<h>
sudo kubeadm join <ep>:6443 --token <t> --discovery-token-ca-cert-hash sha256:<h> --control-plane --certificate-key <k>
sudo kubeadm init phase upload-certs --upload-certs # reissue certificate-key (expires in 2h)
kubectl -n kube-system get cm kubeadm-config -o yaml # podSubnet, serviceSubnet, etcd dataDir
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
sudo kubeadm reset
- node
NotReady+cni plugin not initializedright after init → install a CNI
upgrade (control plane → workers, one at a time)
# after changing v1.34 → v1.35 in the repo line
sudo apt-mark unhold kubeadm && sudo apt-get update && sudo apt-get install -y kubeadm='1.35.9-*' && sudo apt-mark hold kubeadm
sudo kubeadm upgrade plan
sudo kubeadm upgrade apply v1.35.9 # first control plane only
sudo kubeadm upgrade node # other control planes, workers
kubectl drain <node> --ignore-daemonsets [--delete-emptydir-data] [--force]
sudo apt-mark unhold kubelet kubectl && sudo apt-get install -y kubelet='1.35.9-*' kubectl='1.35.9-*' && sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload && sudo systemctl restart kubelet
kubectl uncordon <node>
- one minor at a time. The kubelet must not be newer than the apiserver
- VERSION in
kubectl get nodesis the kubelet version
etcd
sudo grep -E "file|urls|data-dir" /etc/kubernetes/manifests/etcd.yaml
sudo ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save /opt/etcd-backup.db
sudo etcdutl --write-out=table snapshot status /opt/etcd-backup.db
sudo etcdutl --data-dir /var/lib/etcd-from-backup snapshot restore /opt/etcd-backup.db
# point the etcd-data hostPath in etcd.yaml to /var/lib/etcd-from-backup
# then restart kube-apiserver, controller-manager, scheduler (move manifests out and back), restart kubelet
- in etcd 3.6,
etcdctl snapshotonly hassave. status and restore are inetcdutl
Certificates
sudo kubeadm certs check-expiration
sudo kubeadm certs renew all # then restart control plane static Pods, copy ~/.kube/config again
sudo openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -enddate
RBAC and users
openssl genrsa -out u.key 2048
openssl req -new -key u.key -out u.csr -subj "/CN=u/O=g"
cat u.csr | base64 | tr -d "\n" # spec.request of the CSR object
kubectl certificate approve u
kubectl get csr u -o jsonpath='{.status.certificate}' | base64 -d > u.crt
kubectl config set-credentials u --client-key=u.key --client-certificate=u.crt --embed-certs=true
kubectl config set-context u --cluster=kubernetes --user=u --namespace=<ns>
kubectl create role r --verb=get,list --resource=pods -n <ns>
kubectl create clusterrole cr --verb=get,list --resource=nodes
kubectl create rolebinding rb --role=r --user=u -n <ns>
kubectl create clusterrolebinding crb --clusterrole=cr --group=g
kubectl create rolebinding rb2 --clusterrole=edit --serviceaccount=<ns>:<sa> -n <ns>
kubectl auth can-i list pods -n <ns> --as u
kubectl auth can-i --list -n <ns> --as=system:serviceaccount:<ns>:<sa>
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: u
spec:
request: <base64>
signerName: kubernetes.io/kube-apiserver-client
usages: [client auth]
Helm, Kustomize, CRDs
helm repo add <name> <url> && helm repo update
helm search repo <keyword>; helm show values <chart>
helm install <release> <chart> -n <ns> --create-namespace --set key=value
helm upgrade / rollback <release> <rev> / uninstall / list -A / history / get values
kubectl kustomize <dir|url>; kubectl apply -k <dir|url>
kubectl get crd; kubectl api-resources --api-group=<group>
CRD, Helm, and Kustomize basics: CKAD: Helm and Kustomize, CKAD: CRDs, Operators, API deprecations
Docs
- Installing kubeadm, Creating a cluster with kubeadm, Container Runtimes
- Upgrading kubeadm clusters
- Operating etcd clusters
- Certificate Management with kubeadm
- Certificate Signing Requests, RBAC
Workloads and Scheduling
Curriculum: rolling updates and rollbacks, ConfigMaps and Secrets, autoscaling, self-healing primitives, Pod admission and scheduling.
Notes: Pod scheduling, autoscaling. Rolling updates in CKAD: rolling updates and deployment strategies, ConfigMaps and Secrets in CKAD: ConfigMaps and Secrets, requests, limits, LimitRange, and ResourceQuota in CKAD: requests, limits, quotas
k label node <node> disk=ssd
k taint node <node> key=value:NoSchedule
k taint node <node> key=value:NoSchedule-
k create priorityclass high --value=1000
k autoscale deploy <name> --cpu=50% --min=1 --max=5 # target needs a cpu request
k scale deploy <name> --replicas=3
k rollout status|history|undo deploy <name>
spec:
nodeSelector: { disk: ssd }
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- { key: disk, operator: In, values: [ssd] }
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector: { matchLabels: { app: web } }
topologyKey: kubernetes.io/hostname
tolerations:
- { key: key, operator: Equal, value: value, effect: NoSchedule }
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
nodeTaintsPolicy: Honor # keeps the control plane from counting as a domain and leaving Pods Pending
labelSelector: { matchLabels: { app: web } }
priorityClassName: high
- static Pod: put the YAML in that node’s
staticPodPath(/var/lib/kubelet/config.yaml)
Docs
- Assigning Pods to Nodes, Taints and Tolerations, Pod Topology Spread Constraints
- Horizontal Pod Autoscaling
Services and Networking
Curriculum: Pod-to-Pod connectivity, NetworkPolicy, Service types and endpoints, Gateway API, Ingress controllers and Ingress, CoreDNS.
Notes: Pod network and CoreDNS, Gateway API and Ingress. Service, Ingress, and NetworkPolicy basics in CKAD: Service, Ingress, NetworkPolicy
k expose deploy web --port=80 --target-port=8080 --type=NodePort
k get svc,endpointslice
k create ingress web --class=nginx --rule="web.example.com/*=web:80"
k get ingressclass; k get gatewayclass; k get gateway,httproute -A
k -n kube-system get cm coredns -o yaml # Corefile
k -n kube-system edit cm coredns # the reload plugin picks it up (a bit over a minute)
k -n kube-system rollout restart deploy coredns
k -n kube-system get cm kube-proxy -o yaml | grep mode
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata: { name: app }
spec:
parentRefs: [{ name: web-gw }]
hostnames: [app.example.com]
rules:
- matches: [{ path: { type: PathPrefix, value: /v2 } }]
backendRefs: [{ name: app-v2, port: 80 }]
- backendRefs:
- { name: app-v1, port: 80, weight: 80 }
- { name: app-v2, port: 80, weight: 20 }
- if a Route won’t attach, check
status.parents[].conditions(NotAllowedByListeners,BackendNotFound,RefNotPermitted) - across namespaces: Route → Gateway needs
allowedRoutes, Route → Service needs a ReferenceGrant - DNS:
<svc>.<ns>.svc.cluster.local, Pods<ip-with-dashes>.<ns>.pod.cluster.local. hostNetwork Pods usednsPolicy: ClusterFirstWithHostNet
Docs
- Cluster Networking, Virtual IPs and Service Proxies
- DNS for Services and Pods, Customizing DNS Service
- Gateway API, Ingress Controllers
Storage
Curriculum: StorageClasses and dynamic provisioning, volume types, access modes, reclaim policies, PVs and PVCs.
Notes: CKAD: persistent and ephemeral volumes
k get sc,pv,pvc
k get csidriver
k describe pvc <pvc> # why it's Pending (StorageClass, provisioner)
k patch pv <pv> -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
Troubleshooting
Curriculum: clusters and nodes, cluster components, resource usage, container output, Services and networking.
Notes: nodes and cluster components, workloads, resources, networking
Nodes, components
k get nodes; k describe node <node> | grep -A8 Conditions
sudo systemctl status kubelet containerd
sudo systemctl restart kubelet; sudo systemctl enable kubelet
sudo journalctl -u kubelet --since "-5min" --no-pager | grep -iE "error|failed"
sudo crictl ps -a; sudo crictl logs <id>
sudo sh -c 'ls /var/log/pods/kube-system_kube-apiserver-*/kube-apiserver/'
| Symptom | Likely cause |
|---|---|
node NotReady, condition Unknown | kubelet stopped |
kubelet activating (auto-restart) | wrong config file path, containerd missing → journalctl command failed |
kubectl TLS handshake timeout | apiserver can’t reach etcd |
kubectl connection refused | apiserver not up, kubeconfig address/port |
control plane Pod CrashLoopBackOff | wrong command/flag in the manifest |
| control plane Pod missing from the list | manifest YAML syntax → journalctl Could not process manifest file |
Pod Pending with no Events | scheduler broken |
Deployment 0/N, no ReplicaSet | controller-manager broken |
Resources, logs
k top nodes --sort-by=cpu
k top pods -A --sort-by=memory --no-headers | head -1
k top pod <pod> --containers
k logs <pod> -c <container> [--previous] [--tail=N] [--since=5m] [--timestamps]
k logs <pod> --all-containers --prefix
k logs -l app=web
Services, networking
| Symptom | Check |
|---|---|
Connection refused + no endpoints | selector vs Pod labels |
Connection refused + endpoints exist | targetPort vs container port |
endpoint ready=false | readinessProbe |
name gives bad address, ClusterIP works | CoreDNS |
timed out | NetworkPolicy |
k get endpointslice -l kubernetes.io/service-name=<svc>
k run t --image=busybox:1.36 --rm -it --restart=Never -- wget -qO- -T3 http://<svc>.<ns>.svc.cluster.local
k run t --image=busybox:1.36 --rm -it --restart=Never -- nslookup kubernetes.default.svc.cluster.local