"""Reference solutions, keyed by task id. Rendered with the same %{param} / {WS} / {W1} substitution as the task text. Shown only after a task has been graded (drills) or the session submitted (exams). These are one correct way, written the way you'd type them under time pressure: imperative first, YAML only where imperative can't express it.""" S = {} # ================================================================= ARCH ==== S[1] = """ k -n rbac-app create sa deploy-bot k -n rbac-app create role deploy-manager --verb=get,list,watch,create,update,patch,delete --resource=deployments.apps k -n rbac-app create rolebinding deploy-bot-binding --role=deploy-manager --serviceaccount=rbac-app:deploy-bot k auth can-i create deployments -n rbac-app --as=system:serviceaccount:rbac-app:deploy-bot # yes k auth can-i delete pods -n rbac-app --as=system:serviceaccount:rbac-app:deploy-bot # no """ S[2] = """ {SHELL_CP} 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/cka/etcd-snapshot.db etcdutl snapshot status /opt/cka/etcd-snapshot.db -w table # If etcdctl is only inside the pod: run the same command via # kubectl -n kube-system exec etcd-{CP} -- etcdctl ... snapshot save /var/lib/etcd/snap.db # then on the node: mv /var/lib/etcd/snap.db /opt/cka/etcd-snapshot.db (the pod's /var/lib/etcd is the node's /var/lib/etcd) """ S[3] = """ mkdir -p {WS}/q03/overlays/prod && cd {WS}/q03/overlays/prod cat > namespace.yaml < kustomization.yaml < /etc/kubernetes/manifests/static-web.yaml < (copy the line into {WS}/q06/apiserver-expiry.txt) # Part B openssl genrsa -out jane.key 2048 openssl req -new -key jane.key -subj "/CN=jane" -out jane.csr cat < jane.crt k -n default create rolebinding jane-view --clusterrole=view --user=jane """ S[21] = """ k create ns %{ns} helm install %{rel} {WS}/q21/chart -n %{ns} --set replicaCount=%{n} helm upgrade %{rel} {WS}/q21/chart -n %{ns} --reuse-values --set service.type=NodePort helm -n %{ns} history %{rel} # revision 2 """ S[33] = """ cat < {WS}/q34/path.txt cat < apiserver= k get node %{node} -o jsonpath='{.status.nodeInfo.kubeletVersion}{"\\n"}{.status.nodeInfo.containerRuntimeVersion}{"\\n"}' k -n kube-system get pod -l component=etcd -o jsonpath='{.items[0].spec.containers[0].image}' # tag after the colon """ # --------------------------------------------------------------- hard ARCH S[101] = """ k get clusterrolebinding -o wide | grep %{sa} # find the cluster-admin binding -> delete it k delete clusterrolebinding %{sa}-legacy-admin k -n %{ns} create role %{sa}-role --verb=get,list,watch,patch --resource=deployments.apps,replicasets.apps k -n %{ns} patch role %{sa}-role --type=json -p='[{"op":"add","path":"/rules/-","value":{"apiGroups":[""],"resources":["pods/exec"],"verbs":["create"]}}]' k -n %{ns} create rolebinding %{sa}-rb --role=%{sa}-role --serviceaccount=%{ns}:%{sa} k create clusterrole %{sa}-nodes --verb=list --resource=nodes k create clusterrolebinding %{sa}-crb --clusterrole=%{sa}-nodes --serviceaccount=%{ns}:%{sa} k auth can-i --list -n %{ns} --as=system:serviceaccount:%{ns}:%{sa} """ S[102] = """ {SHELL_CP} etcdutl snapshot restore /opt/cka/backup.db --data-dir /var/lib/etcd-restore # point the etcd static pod at the new dir: the hostPath volume, NOT just --data-dir vi /etc/kubernetes/manifests/etcd.yaml # volumes: - hostPath: path: /var/lib/etcd -> /var/lib/etcd-restore # kubelet restarts etcd; apiserver reconnects in ~30-60s watch crictl ps # etcd + kube-apiserver come back kubectl -n default get cm %{cm} # restored """ S[103] = """ k get nodes -l tier=edge # -> the node {SHELL_W3} grep staticPodPath /var/lib/kubelet/config.yaml # NOT /etc/kubernetes/manifests here cat > /%{pod}.yaml < %{user}.crt k -n %{ns} create rolebinding %{user}-edit --clusterrole=edit --user=%{user} # self-contained kubeconfig (--embed-certs) KC={WS}/q104/%{user}.kubeconfig k config view --minify --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 -d > ca.crt SERVER=$(k config view --minify -o jsonpath='{.clusters[0].cluster.server}') kubectl --kubeconfig $KC config set-cluster lab --server=$SERVER --certificate-authority=ca.crt --embed-certs kubectl --kubeconfig $KC config set-credentials %{user} --client-certificate=%{user}.crt --client-key=%{user}.key --embed-certs kubectl --kubeconfig $KC config set-context %{user}@lab --cluster=lab --user=%{user} --namespace=%{ns} kubectl --kubeconfig $KC config use-context %{user}@lab kubectl --kubeconfig $KC get pods """ S[105] = """ mkdir -p {WS}/q105/overlays/prod && cd {WS}/q105/overlays/prod cat > replicas.yaml < envfrom.yaml < kustomization.yaml < web-config- in the envFrom too """ S[106] = """ cat < {WS}/q106/rejected.txt printf 'apiVersion: deploy.example.com/v1\\nkind: %{kind}\\nmetadata: {name: defaulted}\\nspec: {replicas: 2, image: nginx:1.27}\\n' | k apply -f - """ S[122] = """ {SHELL_CP} kubeadm certs renew all # static pods do NOT reload certs by themselves: bounce them mv /etc/kubernetes/manifests/*.yaml /tmp/ && sleep 20 && mv /tmp/*.yaml /etc/kubernetes/manifests/ # admin.conf was regenerated by `renew all`; use it: cp /etc/kubernetes/admin.conf ~/.kube/config kubeadm certs check-expiration | grep '^apiserver ' # -> {WS}/q122/expiry.txt """ # ================================================================= WORK ==== S[7] = """ k -n apps set image deploy web-frontend nginx=nginx:1.27 k -n apps scale deploy web-frontend --replicas=4 k -n apps patch deploy web-frontend -p '{"spec":{"strategy":{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}' k -n apps rollout status deploy web-frontend """ S[8] = """ cat <> /var/log/app.log; sleep 2; done"] volumeMounts: [{name: logs, mountPath: /var/log}] - name: shipper image: busybox:1.36 command: [sh, -c, "tail -f /var/log/app.log"] volumeMounts: [{name: logs, mountPath: /var/log}] EOF """ S[9] = """ k -n apps create deploy cache --image=redis:7-alpine --replicas=2 $do > cache.yaml # edit: add under spec.template.spec # affinity: # nodeAffinity: # requiredDuringSchedulingIgnoredDuringExecution: # nodeSelectorTerms: # - matchExpressions: [{key: disktype, operator: In, values: [ssd]}] # and under the container: resources: {requests: {cpu: 100m, memory: 64Mi}, limits: {cpu: 200m, memory: 128Mi}} k apply -f cache.yaml """ S[10] = """ k -n apps create cm app-config --from-literal=APP_MODE=production --from-literal=LOG_LEVEL=debug k -n apps create secret generic db-creds --from-literal=username=admin --from-literal='password=S3cr3t!' cat < d.yaml # add under spec.template.spec: # nodeSelector: {kubernetes.io/hostname: {W1}} # tolerations: [{key: %{key}, operator: Equal, value: %{val}, effect: NoSchedule}] k apply -f d.yaml """ S[26] = """ cat < cj.yaml # set: spec.concurrencyPolicy: Forbid, successfulJobsHistoryLimit: 3, failedJobsHistoryLimit: 1, # spec.jobTemplate.spec.activeDeadlineSeconds: 30, ...template.spec.restartPolicy: Never k apply -f cj.yaml k -n %{ns} create job %{cj}-manual --from=cronjob/%{cj} """ S[28] = """ cat < /work/index.html"] volumeMounts: [{name: content, mountPath: /work}] containers: - name: web image: nginx:1.25 volumeMounts: [{name: content, mountPath: /usr/share/nginx/html}] EOF """ S[29] = """ k -n %{ns} create cm %{cm}-v2 --from-literal=mode=new k -n %{ns} patch cm %{cm}-v2 -p '{"immutable":true}' # optional, keeps the pattern k -n %{ns} set volume deploy %{dep} --add=false 2>/dev/null; \\ k -n %{ns} patch deploy %{dep} --type=json -p='[{"op":"replace","path":"/spec/template/spec/volumes/0/configMap/name","value":"%{cm}-v2"}]' k -n %{ns} rollout status deploy %{dep} """ S[31] = """ k -n %{ns} create job %{job} --image=busybox:1.36 $do -- sh -c "echo done; sleep 2" > job.yaml # spec: completions: %{c}, parallelism: %{p}, backoffLimit: 2 (restartPolicy Never is the default from create job) k apply -f job.yaml && k -n %{ns} wait --for=condition=complete job/%{job} --timeout=120s """ S[32] = """ k -n %{ns} create quota %{ns}-quota --hard=pods=%{pods},requests.cpu=1,limits.memory=2Gi cat < d.yaml # spec.template.spec: # priorityClassName: %{dep}-prio # tolerations: [{key: %{key}, operator: Equal, value: %{val}, effect: NoSchedule}] # affinity: # podAntiAffinity: # requiredDuringSchedulingIgnoredDuringExecution: # - labelSelector: {matchLabels: {app: %{dep}}} # topologyKey: kubernetes.io/hostname # container resources: {requests: {cpu: 100m, memory: 64Mi}} k apply -f d.yaml """ S[108] = """ k -n %{ns} rollout history deploy %{dep} # find the revision whose image is nginx:1.25 k -n %{ns} rollout history deploy %{dep} --revision=1 # confirm k -n %{ns} rollout undo deploy %{dep} --to-revision=1 k -n %{ns} scale deploy %{dep} --replicas=3 && k -n %{ns} rollout status deploy %{dep} k -n %{ns} rollout pause deploy %{dep} k -n %{ns} set image deploy %{dep} nginx=nginx:1.27 k -n %{ns} set env deploy %{dep} RELEASE=next k -n %{ns} get rs # no new RS while paused """ S[109] = """ cat <> /var/log/agent.log; sleep 1; done"] volumeMounts: [{name: logs, mountPath: /var/log}] containers: - name: app image: nginx:1.27 volumeMounts: [{name: logs, mountPath: /var/log}] readinessProbe: {httpGet: {path: /, port: 80}, periodSeconds: 5} livenessProbe: {tcpSocket: {port: 80}} EOF k -n %{ns} get pod %{pod} -o jsonpath='{.spec.initContainers[*].name} {.spec.containers[*].name}' | tr ' ' '\\n' | sort > {WS}/q109/containers.txt k -n %{ns} get pod %{pod} -o jsonpath='{.status.podIP}' > {WS}/q109/ip.txt """ S[110] = """ k get pods -A -o json | jq -r '.items[] | select(.metadata.namespace!="kube-system") | select(.spec.containers[0].readinessProbe==null) | "\\(.metadata.namespace)/\\(.metadata.name)"' | sort > {WS}/q110/no-readiness.txt k get pods -A --field-selector=status.phase=Running -o jsonpath='{range .items[*]}{.spec.nodeName}{"\\n"}{end}' | sort | uniq -c | awk '{print $2, $1}' > {WS}/q110/per-node.txt k -n kube-system get pods -o jsonpath='{.items[*].spec.containers[*].image}' | tr ' ' '\\n' | sort -u > {WS}/q110/images.txt # no jq? use: k get pods -A -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,RP:.spec.containers[0].readinessProbe --no-headers | awk '$3=="" && $1!="kube-system"{print $1"/"$2}' | sort """ S[121] = """ k config use-context k8s-c2-AC k config view --minify -o jsonpath='{..namespace}' # -> %{ns}, write it to {WS}/q121/ns.txt k create deploy %{dep} --image=busybox:1.36 --replicas=2 -- sleep 3600 k label deploy %{dep} owner=ac && k patch deploy %{dep} -p '{"spec":{"template":{"metadata":{"labels":{"owner":"ac"}}}}}' k expose deploy %{dep} --port=9000 --target-port=9000 --name=%{dep} """ # ================================================================== NET ==== S[11] = """ cat < svc.yaml # set spec.ports[0].nodePort: 30080 k apply -f svc.yaml && k -n svc get endpointslices """ S[13] = """ k -n ingress-app create ingress app-ingress --class=nginx \\ --rule="shop.example.com/api*=api:8080" --rule="shop.example.com/*=web:80" # `*` suffix -> pathType Prefix. Verify: k -n ingress-app get ingress app-ingress -o yaml """ S[14] = """ k -n dns-check run dns-test --image=busybox:1.36 -- sleep 3600 k -n dns-check exec dns-test -- nslookup hello.dns-check.svc.cluster.local | tee {WS}/q14/nslookup.txt echo hello.dns-check.svc.cluster.local > {WS}/q14/svc-fqdn.txt """ S[22] = """ k apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml cat < d.yaml # container: ports: [{name: %{pname}, containerPort: 80}] # readinessProbe: {httpGet: {path: /, port: %{pname}}} k apply -f d.yaml k -n %{ns} expose deploy %{dep} --port=8080 --target-port=%{pname} """ S[42] = """ cat < h.yaml # fix selector: app: %{sts} k apply -f h.yaml k -n %{ns} create svc externalname legacy --external-name=%{ext} k -n %{ns} exec %{sts}-0 -- nslookup %{sts}-0.%{sts} > {WS}/q43/pod0.txt """ # --------------------------------------------------------------- hard NET S[111] = """ k -n %{ns} delete netpol temp-allow-all k label ns %{from} team=%{from} cat < {WS}/q112/fp.txt """ S[113] = """ cat < scale it k -n kube-system scale deploy coredns --replicas=2 k -n kube-system edit cm coredns # forward . 10.255.255.254 -> forward . /etc/resolv.conf k -n kube-system rollout restart deploy coredns k run t --image=busybox:1.36 --rm -it --restart=Never -- nslookup kubernetes.default """ # ================================================================= STOR ==== S[15] = """ cat < remove it k patch pv %{pv} --type=json -p='[{"op":"remove","path":"/spec/claimRef"}]' k get pv %{pv} # Available cat < /data/marker.txt"], volumeMounts: [{name: d, mountPath: /data}]}] EOF k -n %{ns} wait --for=jsonpath='{.status.phase}'=Succeeded pod/writer cat < add nodeName k -n %{ns} wait --for=condition=complete job/migrate --timeout=120s k -n %{ns} get pod legacy -o yaml > legacy.yaml # change claimName old-data -> new-data, strip status/uid/resourceVersion k -n %{ns} delete pod legacy --now && k apply -f legacy.yaml k get pv $(k -n %{ns} get pvc old-data -o jsonpath='{.spec.volumeName}') -o jsonpath='{.spec.persistentVolumeReclaimPolicy}' # Retain k -n %{ns} delete pvc old-data """ # =============================================================== TSHOOT ==== S[17] = """ k get node {W2} # NotReady {SHELL_W2} systemctl status kubelet # inactive (dead), disabled systemctl enable --now kubelet journalctl -u kubelet -f # until "Successfully registered node" """ S[18] = """ k -n troubleshoot describe deploy payments | grep -i image # nginx:1.99-nope -> ErrImagePull k -n troubleshoot set image deploy payments payments=nginx:1.27 k -n troubleshoot get svc payments -o yaml # selector app: payment (typo), targetPort 8080 (container is 80) k -n troubleshoot patch svc payments -p '{"spec":{"selector":{"app":"payments"},"ports":[{"port":80,"targetPort":80}]}}' k -n troubleshoot get endpointslices """ S[19] = """ k -n troubleshoot logs crasher --previous | tee {WS}/q19/crasher.log # FATAL: ... DB_HOST is not set k -n troubleshoot get pod crasher -o yaml > crasher.yaml # add under the container: env: [{name: DB_HOST, value: db.internal}] (strip status/uid/resourceVersion) k -n troubleshoot delete pod crasher --now && k apply -f crasher.yaml """ S[20] = """ k -n kube-system get pods # kube-scheduler-... CrashLoopBackOff / Error k -n kube-system logs kube-scheduler-{CP} # exec: "kube-schedulerr": not found {SHELL_CP} vi /etc/kubernetes/manifests/kube-scheduler.yaml # command: - kube-schedulerr -> - kube-scheduler # kubelet restarts it; k -n kube-system get pods -w """ S[37] = """ k -n kube-system get pods -l k8s-app=kube-dns; k -n kube-system logs -l k8s-app=kube-dns --tail=20 k -n kube-system get cm coredns -o yaml # forward . 10.255.255.1 -> unreachable upstream k -n kube-system edit cm coredns # forward . /etc/resolv.conf k -n kube-system rollout restart deploy coredns k run t --image=busybox:1.36 --rm -it --restart=Never -- nslookup kubernetes.default """ S[38] = """ k -n kube-system get pods # kube-controller-manager Error/CrashLoop k -n kube-system logs kube-controller-manager-{CP} # flag error: lease duration ... invalid {SHELL_CP} vi /etc/kubernetes/manifests/kube-controller-manager.yaml # remove the bogus --leader-elect-lease-duration=1s line k create deploy probe --image=busybox:1.36 -- sleep 60 && k get rs """ S[39] = """ {SHELL_W1} systemctl status kubelet; journalctl -u kubelet -n 30 --no-pager # "open /etc/kubernetes/pki/ca-bundle.crt: no such file" grep -n clientCAFile /var/lib/kubelet/config.yaml # -> /etc/kubernetes/pki/ca.crt systemctl restart kubelet && systemctl enable kubelet """ # -------------------------------------------------------------- hard TSHOOT S[117] = """ k get node {W2} # NotReady,SchedulingDisabled {SHELL_W2} systemctl status kubelet # disabled + dead journalctl -u kubelet -n 30 --no-pager # after enable: "ca-wrong.crt: no such file" sed -i 's|ca-wrong.crt|ca.crt|' /var/lib/kubelet/config.yaml systemctl enable --now kubelet exit k uncordon {W2} # the third thing k run t --image=busybox:1.36 --restart=Never --overrides='{"spec":{"nodeName":"{W2}"}}' -- sleep 60 """ S[118] = """ k -n kube-system get pods # scheduler CrashLoop, controller-manager ImagePullBackOff {SHELL_CP} vi /etc/kubernetes/manifests/kube-scheduler.yaml # kube-schedulerr -> kube-scheduler vi /etc/kubernetes/manifests/kube-controller-manager.yaml # image tag ...-broken -> remove suffix (match the others: crictl images) k -n kube-system get pods -w k create deploy probe --image=busybox:1.36 -- sleep 60 && k get deploy probe -w """ S[119] = """ k -n %{ns} get all; k -n %{ns} describe deploy %{dep}; k -n %{ns} describe rs -l app=%{dep} # quota: pods 1 k -n %{ns} patch quota tight -p '{"spec":{"hard":{"pods":"3"}}}' k -n %{ns} set image deploy %{dep} web=nginx:1.27 # image tag typo k -n %{ns} patch secret %{dep}-env -p '{"stringData":{"DATABASE_URL":"postgres://db"}}' # missing key referenced by env k -n %{ns} rollout restart deploy %{dep} # picks up the secret key k -n %{ns} patch svc %{dep} -p '{"spec":{"selector":{"app":"%{dep}"},"ports":[{"port":80,"targetPort":80}]}}' """ S[120] = """ k -n kube-system get pod -l component=kube-controller-manager -o yaml | grep cluster-cidr k -n kube-system get pod -l component=kube-apiserver -o yaml | grep service-cluster-ip-range k -n kube-system get svc kube-dns -o jsonpath='{.spec.clusterIP}' k -n kube-system get cm kube-proxy -o yaml | grep '^ *mode:' # "" -> iptables k get node %{node} -o jsonpath='{.spec.podCIDR}' """