CKA 개요


25년 2월 18일 변경 이후 시험 범위

  • Cluster Architecture, Installation & Configuration 25%
    • Manage role based access control (RBAC)
    • Prepare underlying infrastructure for installing a Kubernetes cluster
    • Create and manage Kubernetes clusters using kubeadm
    • Manage the lifecycle of Kubernetes clusters
    • Implement and configure a highly-available control plane
    • Use Helm and Kustomize to install cluster components
    • Understand extension interfaces (CNI, CSI, CRI, etc.)
    • Understand CRDs, install and configure operators
  • Workloads & Scheduling 15%
    • Understand application deployments and how to perform rolling update and rollbacks
    • Use ConfigMaps and Secrets to configure applications
    • Configure workload autoscaling
    • Understand the primitives used to create robust, self-healing, application deployments
    • Configure Pod admission and scheduling (limits, node affinity, etc.)
  • Services & Networking 20%
    • Understand connectivity between Pods
    • Define and enforce Network Policies
    • Use ClusterIP, NodePort, LoadBalancer service types and endpoints
    • Use the Gateway API to manage Ingress traffic
    • Know how to use Ingress controllers and Ingress resources
    • Understand and use CoreDNS
  • Storage 10%
    • Implement storage classes and dynamic volume provisioning
    • Configure volume types, access modes and reclaim policies
    • Manage persistent volumes and persistent volume claims
  • Troubleshooting 30%
    • Troubleshoot clusters and nodes
    • Troubleshoot cluster components
    • Monitor cluster and application resource usage
    • Manage and evaluate container output streams
    • Troubleshoot services and networking

CKA Practice Questions


Lightning Lab

  1. kubeadm upgrade
  2. kubectl custom column
  3. kubeconfig 수정
  4. Deployment k set image deployment test nginx=nginx:1.17
  5. PVC 수정
  6. ETCD 백업
  7. Pod 에 Secret volumeMounts

Mock Exam 1

  1. Env var, sidecar pattern, multi container 파드 생성
  2. bob 으로 node01 ssh 후 dpkg
  3. crd grep 해서 txt 저장
  4. 6379 포트로 파드 서비스 생성
  5. Deployment 생성
  6. 파드 트러블슈팅
  7. 30082 노드포트로 서비스 생성
  8. PV 생성
  9. HPA 생성
  10. VPA 생성
  11. GW 생성
  12. helm 차트 업그레이드

Mock Exam 2

  1. SC 생성
  2. Sidecar pattern, multi container deployment 생성
  3. Ingress 생성, class 꼭 지정
  4. Deployment k set image deployment test nginx=nginx:1.17
  5. CSR, Role, Role Binding 생성
  6. 서비스 생성 후 k run test --image=busybox --rm -it --restart=Never -- nslookup
  7. Static Pod 생성
  8. HPA 생성
  9. GW TLS 설정
  10. helm get all helm uninstall
  11. Network Policy 생성

Mock Exam 3

  1. net.ipv4.ip_forward = 1 sysctl
  2. ServiceAccount, ClusterRole, ClusterRoleBinding 생성
  3. StorageClass 생성
  4. ConfigMap 생성, Pod 에 spec.containers.envFrom[0].configMapRef.name 로 환경변수 설정
  5. PriorityClass 생성, Pod 에 spec.priorityClassName 에 연결
  6. NetworkPolicy 생성, Ingress TCP 80 만 전체허용
  7. Node Taint, Pod 에 spec.tolerations 설정
  8. PVC 수정
  9. kubeconfig 수정 후 테스트
  10. kube-controller-manager 트러블슈팅
  11. HPA 생성, Pods requests_per_second 기준으로 스케일링 설정
  12. HTTPRoute 생성
  13. helm install 로컬에 있는 차트로 생성
  14. kubeadm-config ConfigMap 에서 podSubnet 값 추출

prepium.sh

  1. prepium.sh Q1. CNI
  2. prepium.sh Q2. CRDs
  3. prepium.sh Q3. Network Policy
  4. prepium.sh Q4. TLS ConfigMap
  5. prepium.sh Q5. Gateway API
  6. prepium.sh Q6. Resource Requests and Limits
  7. prepium.sh Q7. Sidecar
  8. prepium.sh Q8. Taints & Tolerations
  9. prepium.sh Q9. StorageClass
  10. prepium.sh Q10. Priority Class

JayDemy

  1. prepium.sh Q4. TLS ConfigMap
  2. prepium.sh Q5. Gateway API
  3. JayDemy Q3. HPA
  4. JayDemy Q4. Ingress
  5. prepium.sh Q1. CNI
  6. prepium.sh Q1. CNI
  7. JayDemy Q7. Install ArgoCD
  8. prepium.sh Q10. Priority Class
  9. JayDemy Q9. NodePort Service
  10. prepium.sh Q9. StorageClass
  11. prepium.sh Q7. Sidecar
  12. prepium.sh Q2. CRDs
  13. prepium.sh Q6. Resource Requests and Limits
  14. JayDemy Q14. Pod PVC
  15. JayDemy Q15. CRI, systemctl
  16. JayDemy Q16. Troubleshooting, crictl, journalctl
  17. prepium.sh Q3. Network Policy

DumbITGuy

  1. JayDemy Q14. Pod PVC
  2. JayDemy Q7. Install ArgoCD
  3. prepium.sh Q7. Sidecar
  4. prepium.sh Q6. Resource Requests and Limits
  5. x
  6. prepium.sh Q2. CRDs
  7. prepium.sh Q10. Priority Class
  8. prepium.sh Q1. CNI
  9. JayDemy Q15. CRI, systemctl
  10. prepium.sh Q8. Taints & Tolerations
  11. prepium.sh Q5. Gateway API
  12. JayDemy Q4. Ingress
  13. prepium.sh Q3. Network Policy
  14. prepium.sh Q9. StorageClass
  15. JayDemy Q16. Troubleshooting, crictl, journalctl
  16. DumbITGuy Q16. NodePort Service
  17. prepium.sh Q4. TLS ConfigMap

killer.sh: CKA-A

Question1st attempt2nd attempt3rd attempt
1. Contexts❌ 0/3✅ 3/3✅ 3/3
2. CRD, Helm, cert-manager✅ 5/5✅ 5/5✅ 5/5
3. Scale down StatefulSet✅ 1/1✅ 1/1✅ 1/1
4. Find Pods first to be terminated❌ 0/1✅ 1/1✅ 1/1
5. Kustomize configure HPA Autoscaler⚠️ 5/6✅ 6/6✅ 6/6
6. Storage, PV, PVC, Pod volume✅ 6/6✅ 6/6✅ 6/6
7. Node and Pod Resource Usage✅ 2/2✅ 2/2✅ 2/2
8. Update Kubernetes Version and join cluster❌ 0/4✅ 4/4✅ 4/4
9. Contact K8s API from inside Pod✅ 2/2✅ 2/2✅ 2/2
10. RBAC ServiceAccount Role RoleBinding✅ 6/6✅ 6/6✅ 6/6
11. DaemonSet on all Nodes✅ 4/4✅ 4/4✅ 4/4
12. Deployment on all Nodes✅ 11/11✅ 11/11✅ 11/11
13. Gateway API Ingress✅ 5/5✅ 5/5✅ 5/5
14. Check how long certificates are valid❌ 0/2✅ 2/2✅ 2/2
15. NetworkPolicy✅ 7/7✅ 7/7✅ 7/7
16. Update CoreDNS Configuration❌ 0/3⚠️ 1/3✅ 3/3
17. Find Container of Pod and check info❌ 0/6✅ 6/6✅ 6/6
Score✅ 54/74(73%)✅ 72/74(97%)✅ 74/74(100%)

killer.sh: CKA-B

  1. Headless Service
  2. Create a Static Pod and Service
  3. server cert info
  4. Pod Ready if Service is reachable
  5. Kubectl sorting
  6. Fix Kubelet
  7. Etcd Operations
  8. Get Controlplane Information
  9. Kill Scheduler, Manual Scheduling
  10. PV PVC Dynamic Provisioning
  11. Create Secret and mount into Pod
  12. Schedule Pod on Controlplane Nodes
  13. Multi Containers and Pod shared Volume
  14. Find out Cluster Information
  15. Cluster Event Logging
  16. Namespaces and API Resources
  17. Operator, CRDs, RBAC, Kustomize

Cluster Architecture, Installation & Configuration 25%


CRI, kernel parameter 설정

Mock Exam 1

This question needs to be solved on node node01. To access the node using SSH, use the credentials below:

username: bob
password: caleston123

As an administrator, you need to prepare node01 to install kubernetes. One of the steps is installing a container runtime. Install the cri-docker_0.3.16.3-0.debian.deb package located in /root and ensure that the cri-docker service is running and enabled to start on boot.

ssh bob@node01
sudo su
cd /root
dpkg -i cri-docker_0.3.16.3-0.debian.deb
systemctl start cri-docker
systemctl enable cri-docker
systemctl status cri-docker
systemctl is-enabled cri-docker

Mock Exam 3

You are an administrator preparing your environment to deploy a Kubernetes cluster using kubeadm. Adjust the following network parameters on the system to the following values, and make sure your changes persist reboots: net.ipv4.ip_forward = 1 net.bridge.bridge-nf-call-iptables = 1

vi /etc/sysctl.d/k8s.conf
sysctl --system
sysctl net.ipv4.ip_forward

JayDemy Q15. CRI, systemctl

Prepare a Linux system for Kubernetes. Docker is already installed, but you need to configure it for kubeadm. Task Complete these tasks to prepare the system for Kubernetes:

  • Set up cri-dockerd:
    • Install the Debian package ~/cri-dockerd_0.3.9.3-0.ubuntu-focal_amd64.deb
    • Debian packages are installed using dpkg
    • Enable and start the cri-docker service
  • Configure these system parameters:
    • Set net.bridge.bridge-nf-call-iptables = 1
    • Set net.ipv6.conf.all.forwarding = 1
    • Set net.ipv4.ip_forward = 1
    • Set net.netfilter.nf_conntrack_max = 131072
sudo dpkg -i cri-dockerd_0.3.9.3-0.ubuntu-focal_amd64.deb
sudo systemctl enable --now cri-docker.service
sudo systemctl status cri-docker.service
sudo bash -c 'cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.ipv6.conf.all.forwarding = 1
net.ipv4.ip_forward = 1
net.netfilter.nf_conntrack_max = 131072
EOF'
 
sudo sysctl --system  # 적용
 
sysctl net.bridge.bridge-nf-call-iptables  # 검증

CNI, podSubnet

Mock Exam 3

While preparing to install a CNI plugin on your Kubernetes cluster, you typically need to confirm the cluster-wide Pod network CIDR. Identify the Pod subnet configured for the cluster (the value specified under podSubnet in the kubeadm configuration). Output this CIDR in the format x.x.x.x/x to a file located at /root/pod-cidr.txt. Note: Use the cluster-wide podSubnet from the kubeadm-config ConfigMap, not the per-node CIDR from kubectl get node.

k -n kube-system get cm kubeadm-config -o yaml | awk '/podSubnet:/{print $2}' > /root/pod-cidr.txt
# OR cat /etc/kubernetes/manigests/kube-controller-manager.yaml | grep "--cluster-cidr"
cat /root/pod-cidr.txt 
172.17.0.0/16

prepium.sh Q1. CNI

Install and configure one Container Network Interface (CNI) plugin from the options below. Available options

  • Flannel v0.26.1 - manifest kube-flannel.yml
  • Calico v3.28.2 - install the Tigera operator manifest, then apply the Calico custom resources manifest tigera-operator.yaml custom-resources.yaml Requirements The CNI you install must:
  • allow pods to communicate with each other
  • support Kubernetes NetworkPolicy enforcement
  • be installed from manifests
# NetworkPolicy 적용이 가능한 Calico 설치
k apply -f operator.yaml
k -n kube-system get cm kubeadm-config -o yaml | awk '/podSubnet:/{print $2}'  # 10.244.0.0/16
curl -O https://.../custom-resources.yaml > custom-resources.yaml
vi custom-resources.yaml # spec.calicoNetwork.ipPools[0].cidr 변경
k apply -f custom-resources.yaml

kubeconfig

Lighting Lab

A kubeconfig file called admin.kubeconfig has been created in /root/CKA. There is something wrong with the configuration. Troubleshoot and fix it.

# vi /root/CKA/admin.kubeconfig
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: XXX...
    server: https://controlplane:6443  # 포트 수정
  name: kubernetes
...
k get nodes --kubeconfig /root/CKA/admin.kubeconfig  # kubeconfig 작동 확인

killer.sh: CKA-A Q1. Contexts

Configure Access to Multiple Clusters Cluster Access with kubeconfig Solve this question on: ssh cka9412 You’re asked to extract the following information from kubeconfig file /course/1/kubeconfig on cka9412:

  1. Write all kubeconfig context names into /course/1/contexts, one per line
  2. Write the name of the current context into /course/1/current-context
  3. Write the client-certificate of user account-0027 base64-decoded into /course/1/cert
ssh cka9412
k --kubeconfig /course/1/kubeconfig config get-contexts -o name > /course/1/contexts
k --kubeconfig /course/1/kubeconfig config current-context > /course/1/current-context
k --kubeconfig /course/1/kubeconfig config view --raw -o jsonpath="{.users[0].user.client-certificate-data}" | base64 -d > /course/1/cert

kubeadm Cluster Upgrade & Certificates

Lighting Lab

Upgrade the current version of kubernetes from 1.34.0 to 1.35.0 exactly using the kubeadm utility. Make sure that the upgrade is carried out one node at a time starting with the controlplane node. To minimize downtime, the deployment gold-nginx should be rescheduled on an alternate node before upgrading each node. Upgrade controlplane node first and drain node node01 before upgrading it. Pods for gold-nginx should run on the controlplane node subsequently.

# controlplane
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.35/deb/ /" > /etc/apt/sources.list.d/kubernetes.list
sudo apt update
sudo apt-cache madison kubeadm
sudo apt-mark unhold kubeadm && \
sudo apt-get update && sudo apt-get install -y kubeadm='1.35.0-1.1' && \
sudo apt-mark hold kubeadm
sudo kubeadm upgrade plan v1.35.0
sudo kubeadm upgrade apply v1.35.0
 
kubectl drain controlplane --ignore-daemonsets
sudo apt-mark unhold kubelet kubectl && \
sudo apt-get update && sudo apt-get install -y kubelet='1.35.0-1.1' kubectl='1.35.0-1.1' && \
sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload
sudo systemctl restart kubelet
kubectl uncordon controlplane
# workernode
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.35/deb/ /" > /etc/apt/sources.list.d/kubernetes.list
sudo apt update
sudo apt-cache madison kubeadm
sudo apt-mark unhold kubeadm && \
sudo apt-get update && sudo apt-get install -y kubeadm='1.35.0-1.1' && \
sudo apt-mark hold kubeadm
sudo kubeadm upgrade node
 
kubectl drain node01 --ignore-daemonsets
sudo apt-mark unhold kubelet kubectl && \
sudo apt-get update && sudo apt-get install -y kubelet='1.35.0-1.1' kubectl='1.35.0-1.1' && \
sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload
sudo systemctl restart kubelet
kubectl uncordon node01

killer.sh: CKA-A Q8. Update Kubernetes Version and join cluster

Upgrading kubeadm clusters kubeadm join Solve this question on: ssh cka3962 Your coworker notified you that node cka3962-node1 is running an older Kubernetes version and is not even part of the cluster yet.

  1. Update the node’s Kubernetes to the exact version of the controlplane
  2. Add the node to the cluster using kubeadm

ℹ️ You can connect to the worker node using ssh cka3962-node1 from cka3962

ssh cka3962
k get node
ssh cka3962-node1
sudo -i
kubectl version
kubelet --version
kubeadm version
kubeadm upgrade node
apt-get update
apt-cache show kubectl -a | grep 1.35
apt-get install kubectl=1.35.6-1.1 kubelet=1.35.6-1.1
kubelet --version
systemctl restart kubelet
systemctl status kubelet
exit
exit
sudo -i
kubeadm token create --print-join-command
kubeadm token list
ssh cka3962-node1
kubeadm join 192.168.100.31:6443 --token 0l00rk.sopvq9xkvuasjqij --discovery-token-ca-cert-hash sha256:7a99a324fef6f169ee76e53e0a3d968db49d267b1657a1e68d76db99eff432f4
systemctl status kubelet
exit
k get node

killer.sh: CKA-A Q14. Check how long certificates are valid

Certificate Management with kubeadm Solve this question on: ssh cka9412 Perform some tasks on cluster certificates:

  1. Check how long the kube-apiserver server certificate is valid using openssl or cfssl. Write the expiration date into /course/14/expiration. Run the kubeadm command to list the expiration dates and confirm both methods show the same one
  2. Write the kubeadm command that would renew the kube-apiserver certificate into /course/14/kubeadm-renew-certs.sh
ssh cka9412
sudo -i
find /etc/kubernetes/pki | grep apiserver
openssl x509 -noout -text -in /etc/kubernetes/pki/apiserver.crt | grep Validity -A2
kubeadm certs check-expiration | grep apiserver
echo "Oct 29 14:19:27 2025 GMT" > /course/14/expiration
echo "kubeadm certs renew apiserver" > /course/14/kubeadm-renew-certs.sh

killer.sh: CKA-B Q3. Kubelet client/server cert info

TLS bootstrapping Certificates best practices Solve this question on: ssh cka5248 Node cka5248-node1 has been added to the cluster using kubeadm and TLS bootstrapping. Find the Issuer and Extended Key Usage values on cka5248-node1 for:

  1. Kubelet Client Certificate, the one used for outgoing connections to the kube-apiserver
  2. Kubelet Server Certificate, the one used for incoming connections from the kube-apiserver Write the information into file /course/3/certificate-info.txt.

ℹ️ You can connect to the worker node using ssh cka5248-node1 from cka5248

ssh cka5248
ssh cka5248-node1
openssl x509 -noout -text -in /var/lib/kubelet/pki/kubelet-client-current.pem | grep Issuer
openssl x509 -noout -text -in /var/lib/kubelet/pki/kubelet-client-current.pem | grep "Extended Key Usage" -A1
openssl x509 -noout -text -in /var/lib/kubelet/pki/kubelet.crt | grep Issuer
openssl x509 -noout -text -in /var/lib/kubelet/pki/kubelet.crt | grep "Extended Key Usage" -A1
# cka5248:/course/3/certificate-info.txt
Issuer: CN = kubernetes
X509v3 Extended Key Usage: TLS Web Client Authentication
Issuer: CN = cka5248-node1-ca@1730211854
X509v3 Extended Key Usage: TLS Web Server Authentication

ETCD Backup & Restore

Lighting Lab

Take the backup of ETCD at the location /opt/etcd-backup.db on the controlplane node.

cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep etcd
ETCDCTL_API=3 etcdctl \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/apiserver-etcd-client.crt \
  --key=/etc/kubernetes/pki/apiserver-etcd-client.key \
  snapshot save /opt/etcd-backup.db
ETCDCTL_API=3 etcdctl --write-out=table snapshot status /opt/etcd-backup.db

killer.sh: CKA-B Q7. Etcd Operations

Operating etcd clusters for Kubernetes Solve this question on: ssh cka2560 You have been tasked to perform the following etcd operations:

  1. Run etcd --version and store the output at /course/7/etcd-version
  2. Make a snapshot of etcd and save it at /course/7/etcd-snapshot.db
ssh cka2560
k -n kube-system exec etcd-cka2560 -- etcd --version > /course/7/etcd-version
ETCDCTL_API=3 etcdctl snapshot save /course/7/etcd-snapshot.db \
--cacert /etc/kubernetes/pki/etcd/ca.crt \
--cert /etc/kubernetes/pki/etcd/server.crt \
--key /etc/kubernetes/pki/etcd/server.key

RBAC & CSR & SA

Mock Exam 2

Create a new user called john. Grant him access to the cluster using a csr named john-developer. Create a role developer which should grant John the permission to create, list, get, update and delete pods in the development namespace . The private key exists in the location: /root/CKA/john.key and csr at /root/CKA/john.csr. Important Note: As of kubernetes 1.19, the CertificateSigningRequest object expects a signerName. Please refer to the documentation to see an example. The documentation tab is available at the top right of the terminal.

# vi csr.yaml
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: john-developer
spec:
  request: # cat /root/CKA/john.csr | base64 | tr -d '\n'
  signerName: kubernetes.io/kube-apiserver-client
  expirationSeconds: 86400 # one day
  usages:
  - client auth
k apply -f csr.yaml
k certificate approve john-developer
k create role developer --verb=create,list,get,update,delete --resource=pods -n development
k create rolebinding john --user=john --role=developer -n development
k auth can-i create pods --as=john -n development

Mock Exam 3

Create a new service account with the name pvviewer. Grant this Service account access to list all PersistentVolumes in the cluster by creating an appropriate cluster role called pvviewer-role and ClusterRoleBinding called pvviewer-role-binding. Next, create a pod called pvviewer with the image: redis and serviceAccount: pvviewer in the default namespace.

k create serviceaccount pvviewer
k create clusterrole pvviewer-role --verb=list --resource=persistentvolumes
k create clusterrolebinding pvviewer-role-binding --clusterrole=pvviewer-role --serviceaccount=default:pvviewer
# vi pvviewer.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pvviewer
spec:
  serviceAccountName: pvviewer
  containers:
  - image: redis
    name: pvviewer

killer.sh: CKA-A Q9. Contact K8s API from inside Pod

Accessing the Kubernetes API from a Pod Configure Service Accounts for Pods Solve this question on: ssh cka9412 There is ServiceAccount secret-reader in Namespace project-swan. Create a Pod of image nginx:1-alpine named api-contact which uses this ServiceAccount. Exec into the Pod and use curl to manually query all Secrets from the Kubernetes API. Write the result into file /course/9/result.json.

ssh cka9412
k -n project-swan run api-contact --image=nginx:1-alpine --dry-run=client -o yaml > 9.yaml
# cka9412:/home/candidate/9.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: api-contact
  name: api-contact
  namespace: project-swan
spec:
  serviceAccountName: secret-reader   # add
  containers:
  - image: nginx:1-alpine
    name: api-contact
k apply -f 9.yaml
k -n project-swan exec api-contact -it -- sh
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k https://kubernetes.default/api/v1/secrets -H "Authorization: Bearer ${TOKEN}" > result.json
k -n project-swan exec api-contact -it -- cat result.json > /course/9/result.json

killer.sh: CKA-A Q10. RBAC ServiceAccount Role RoleBinding

Using RBAC Authorization Configure Service Accounts for Pods Solve this question on: ssh cka3962 Create a new ServiceAccount processor in Namespace project-hamster. Create a Role and RoleBinding, both named processor as well. These should allow the new SA to only create Secrets and ConfigMaps in that Namespace.

➜ ssh cka3962
k -n project-hamster create sa processor
k -n project-hamster create role processor --verb=create --resource=secret --resource=configmap
k -n project-hamster create rolebinding processor --role processor --serviceaccount project-hamster:processor
k -n project-hamster auth can-i create secret --as system:serviceaccount:project-hamster:processor

CRD

Mock Exam 1

On controlplane node, identify all CRDs related to VerticalPodAutoscaler and save their names into the file /root/vpa-crds.txt.

k get crd | grep autoscaling > /root/vpa-crds.txt

prepium.sh Q2. CRDs

Task

  1. Create a list of all cert-manager CRDs and save it to /root/resources.yaml
  2. Using kubectl, extract the documentation for the subject specification field on the Certificate Custom Resource and save it to /root/documentation.txt You may use any output format that kubectl supports.
k get crd | grep cert-manager.io > /root/resources.yaml
k explain certificate.spec.subject > /root/documentation.txt

killer.sh: CKA-A Q2. CRD, Helm, cert-manager

Helm Docs Custom Resources Solve this question on: ssh cka7968 Install cert-manager using Helm in Namespace cert-manager. Then configure and create a ClusterIssuer resource:

  1. Create Namespace cert-manager
  2. Install Helm chart jetstack/cert-manager (with crds.enabled=true) into the new Namespace. The Helm Release should be called cert-manager
  3. Update the ClusterIssuer resource in /course/2/cluster-issuer.yaml to include crlDistributionPoints: ["http://example.com/crl"] under spec.selfSigned
  4. Create the ClusterIssuer resource from /course/2/cluster-issuer.yaml

ℹ️ It is not required for cert-manager to issue real certificates. Installing the Helm Chart and the ClusterIssuer resource as requested is enough

ssh cka7968
k create ns cert-manager
helm -n cert-manager install cert-manager jetstack/cert-manager --set crds.enabled=true
vi /course/2/cluster-issuer.yaml
# cka7968:/course/2/cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: course-issuer
spec:
  selfSigned:
    crlDistributionPoints:    # ADD
    - http://example.com/crl  # ADD
k apply -f /course/2/cluster-issuer.yaml

Helm

Mock Exam 1

One co-worker deployed a podinfo helm chart kk-mock1 in the kk-ns namespace on the cluster. A new update is pushed to the helm chart, and the team wants you to update the helm repository to fetch the new changes. After updating the helm chart, upgrade the helm chart version to 6.11.2.

helm list -n kk-ns
NAME            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART           APP VERSION
kk-mock1        kk-ns           1               2026-07-29 06:09:26.984079875 +0000 UTC deployed        podinfo-6.11.0  6.11.0
 
helm search repo kk-mock1
NAME                    CHART VERSION   APP VERSION     DESCRIPTION                      
kk-mock1/podinfo        6.14.1          6.14.1          Podinfo Helm chart for Kubernetes
 
helm upgrade kk-mock1 kk-mock1/podinfo --version=6.11.2 -n kk-ns
 
helm list -n kk-ns
NAME            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART           APP VERSION
kk-mock1        kk-ns           2               2026-07-29 06:14:33.024776475 +0000 UTC deployed        podinfo-6.11.2  6.11.2

Mock Exam 2

On the cluster, the team has installed multiple helm charts on a different namespace. By mistake, those deployed resources include one of the vulnerable images called kodekloud/webapp-color:v1. Find out the release name and uninstall it.

helm list -A
NAME                    NAMESPACE               REVISION        UPDATED                                 STATUS          CHART                       APP VERSION
atlanta-page-apd        atlanta-page-04         1               2025-11-08 10:57:43.405721672 +0000 UTC deployed        atlanta-page-apd-0.1.0      1.16.0     
digi-locker-apd         digi-locker-02          1               2025-11-08 10:57:40.988036054 +0000 UTC deployed        digi-locker-apd-0.1.0       1.16.0     
security-alpha-apd      security-alpha-01       1               2025-11-08 10:57:40.109579755 +0000 UTC deployed        security-alpha-apd-0.1.0    1.16.0     
web-dashboard-apd       web-dashboard-03        1               2025-11-08 10:57:41.989424936 +0000 UTC deployed        web-dashboard-apd-0.1.0     1.16.0
 
helm get manifest atlanta-page-apd -n atlanta-page-04 | grep -i webapp-color:v1
          image: "kodekloud/webapp-color:v1"
 
helm uninstall atlanta-page-apd -n atlanta-page-04
release "atlanta-page-apd" uninstalled

Mock Exam 3

One application, webpage-server-01, is currently deployed on the Kubernetes cluster using Helm. A new version of the application is available in a Helm chart located at /root/new-version. Validate this new Helm chart, then install it as a new release named webpage-server-02. After confirming the new release is installed, uninstall the old release webpage-server-01.

helm lint new-version
helm install webpage-server-02 new-version
helm uninstall webpage-server-01 -n default

JayDemy Q7. Install ArgoCD

Install Argo CD in cluster: Add the official Argo CD Helm repository with the name argo. The Argo CD CRDs have already been pre-installed in the cluster. Generate a helm template of the Argo CD Helm chart version 7.7.3 for the argocd namespace and save to /argo-helm.yaml. Configure the chart to not install CRDs. Install Argo CD using Helm with release name argocd using the same version as above and configuration as used in the template 7.7.3. Instaill it in the argocd namespace and configure it to not install CRDs. You do not need to configure access to the Argo CD server UI.

helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
k create ns argocd
helm -n argocd template argocd argo/argo-cd --version 7.7.3 --set crds.install=false > /argo-helm.yaml
helm -n argocd install argocd argo/argo-cd --version 7.7.3 --set crds.install=false

Kustomize

killer.sh: CKA-A Q5. Kustomize configure HPA Autoscaler

Horizontal Pod Autoscaling Kustomize Solve this question on: ssh cka5774 Previously the application api-gateway used some external autoscaler which should now be replaced with a HorizontalPodAutoscaler (HPA). The application has been deployed to Namespaces api-gateway-staging and api-gateway-prod like this:

kubectl kustomize /course/5/api-gateway/staging | kubectl apply -f -
kubectl kustomize /course/5/api-gateway/prod | kubectl apply -f -

Using the Kustomize config at /course/5/api-gateway do the following:

  1. Remove the ConfigMap horizontal-scaling-config completely
  2. Add HPA named api-gateway for the Deployment api-gateway with min 2 and max 4 replicas. It should scale at 50% average CPU utilisation
  3. In prod the HPA should have max 6 replicas
  4. Apply your changes for staging and prod so they’re reflected in the cluster
ssh cka5774
# cka5774:/course/5/api-gateway/base/api-gateway.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-gateway
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api-gateway
  minReplicas: 2
  maxReplicas: 4
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
---
...
# cka5774:/course/5/api-gateway/prod/api-gateway.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-gateway
spec:
  maxReplicas: 6
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-gateway
  labels:
    env: prod
k apply -k /course/5/api-gateway/staging
k apply -k /course/5/api-gateway/prod
k -n api-gateway-staging delete cm horizontal-scaling-config
k -n api-gateway-prod delete cm horizontal-scaling-config

killer.sh: CKA-B Q17. Operator, CRDs, RBAC, Kustomize

Using RBAC Authorization Kustomize Solve this question on: ssh cka6016 There is Kustomize config available at /course/17/operator. It installs an operator which works with different CRDs. It has been deployed like this: kubectl kustomize /course/17/operator/prod | kubectl apply -f - Perform the following changes in the Kustomize base config:

  1. The operator needs to list certain CRDs. Check the logs to find out which ones and adjust the permissions for Role operator-role
  2. Add a new Student resource called student4 with any name and description Deploy your Kustomize config changes to prod.
ssh cka6016
# cka6016:/course/17/operator/base/rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: operator-role
  namespace: default
rules:
- apiGroups:
  - education.killer.sh
  resources:
  - students
  - classes
  verbs:
  - list
...
# cka6016:/course/17/operator/base/students.yaml
...
apiVersion: education.killer.sh/v1
kind: Student
metadata:
  name: student4
spec:
  name: Some Name
  description: Some Description
k apply -k /course/17/operator/prod
k -n operator-prod logs operator-7f4f58d4d9-v6ftw
k -n operator-prod get student

Workloads & Scheduling 15%


Static Pod

Mock Exam 2

Create a static pod on node01 called nginx-critical with the image nginx. Make sure that it is recreated/restarted automatically in case of a failure. For example, use /etc/kubernetes/manifests as the static Pod path.

# ssh node01
# vi /etc/kubernetes/manifests/static.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-critical
spec:
  containers:
  - image: nginx
    name: nginx-critical

killer.sh: CKA-B Q2. Create a Static Pod and Service

Create static Pods Service Solve this question on: ssh cka2560 Create a Static Pod named my-static-pod in Namespace default on the controlplane node. It should be of image nginx:1-alpine and have resource requests for 10m CPU and 20Mi memory. Create a NodePort Service named static-pod-service which exposes that static Pod on port 80.

ℹ️ For verification check if the new Service has one Endpoint. It should also be possible to access the Pod via the internal IP address of cka2560, for example using curl 192.168.100.31:NODE_PORT

ssh cka2560
# k run my-static-pod --image=nginx:1-alpine -o yaml --dry-run=client > my-static-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: my-static-pod
  name: my-static-pod
spec:
  containers:
  - image: nginx:1-alpine
    name: my-static-pod
    resources:
      requests:
        cpu: 10m
        memory: 20Mi
k expose pod my-static-pod-cka2560 --name static-pod-service --type NodePort --port 80
curl 192.168.100.31:32699

ConfigMap, Secrets

Lighting Lab

Create a pod called secret-1401 in the admin1401 namespace using the busybox image. The container within the pod should be called secret-admin and should sleep for 4800 seconds.
The container should mount a read-only secret volume called secret-volume at the path /etc/secret-volume. The secret being mounted has already been created for you and is called dotfile-secret.

k config set-context --current -n admin1401
k run secret-1401 --image busybox --dry-run=client -o yaml > pod.yaml
# vi pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: secret-1401
spec:
  containers:
  - image: busybox
    name: secret-admin
    command:
    - 'sh'
    - '-c'
    - 'sleep 4800'
    volumeMounts:
    - name: secret-volume
      readOnly: true
      mountPath: "/etc/secret-volume"
  volumes:
    - name: secret-volume
      secret:
        secretName: dotfile-secret
k apply -f pod.yaml

Mock Exam 3

Create a ConfigMap named app-config in the namespace cm-namespace with the following key-value pairs:

ENV=production
LOG_LEVEL=info

Then, modify the existing Deployment named cm-webapp in the same namespace to use the app-config ConfigMap by setting the environment variables ENV and LOG_LEVEL in the container from the ConfigMap.

k create cm app-config -n cm-namespace --from-literal=ENV=production --from-literal=LOG_LEVEL=info
# k edit deployments.apps cm-webapp -n cm-namespace
...
spec:
  ...
  template:
    ...
    spec:
      containers:
        ...
        # ConfigMap 설정 추가
        envFrom:
          - configMapRef:
              name: app-config
    ...
k -n cm-namespace exec cm-webapp-748d87d5dd-7fc22 -- env

prepium.sh Q4. TLS ConfigMap

The secure-web Deployment in namespace secure-space uses a ConfigMap tls-config that currently supports both TLS 1.2 and TLS 1.3. Task: Modify the configuration so that only TLS 1.3 is supported. Note: ConfigMaps are immutable - you must delete and recreate it, then restart the Deployment.

k -n secure-space get cm tls-config -o yaml > tls-config.yaml
vi tls-config.yaml  # TLSv1.2 제거
k -n secure-space delete cm tls-config
k apply -f tls-config.yaml
k -n secure-space rollout restart deployment secure-web  # ConfigMap 적용
k -n secure-space get svc  # ClusterIP 확인
echo '${cluster-ip} ${domain-name}' >> /etc/hosts  # DNS 등록
curl -vk --tls-max 1.2 https://${domain-name}  # 실패해야 정상

killer.sh: CKA-B Q11. Create Secret and mount into Pod

Secrets Distribute Credentials Securely Solve this question on: ssh cka2560 Create Namespace secret and implement the following in it:

  • Create Pod secret-pod with image busybox:1. It should be kept running by executing sleep 1d or something similar
  • Create the existing Secret /course/11/secret1.yaml and mount it read-only into the Pod at /tmp/secret1
  • Create a new Secret called secret2 which should contain user=user1 and pass=1234. These entries should be available inside the Pod’s container as environment variables APP_USER and APP_PASS
ssh cka2560
k create ns secret
# cka2560:/home/candidate/11_secret1.yaml
apiVersion: v1
data:
  halt: IyEgL2Jpbi9zaAo...
kind: Secret
metadata:
  creationTimestamp: null
  name: secret1
  namespace: secret           # UPDATE
k create -f 11_secret1.yaml
k -n secret create secret generic secret2 --from-literal=user=user1 --from-literal=pass=1234
# k -n secret run secret-pod --image=busybox:1 --dry-run=client -o yaml -- sh -c "sleep 1d" > 11.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: secret-pod
  name: secret-pod
  namespace: secret                       # important if not automatically added
spec:
  containers:
  - args:
    - sh
    - -c
    - sleep 1d
    image: busybox:1
    name: secret-pod
    resources: {}
    env:                                  # add
    - name: APP_USER                      # add
      valueFrom:                          # add
        secretKeyRef:                     # add
          name: secret2                   # add
          key: user                       # add
    - name: APP_PASS                      # add
      valueFrom:                          # add
        secretKeyRef:                     # add
          name: secret2                   # add
          key: pass                       # add
    volumeMounts:                         # add
    - name: secret1                       # add
      mountPath: /tmp/secret1             # add
      readOnly: true                      # add
  dnsPolicy: ClusterFirst
  restartPolicy: Always
  volumes:                                # add
  - name: secret1                         # add
    secret:                               # add
      secretName: secret1                 # add

Deployment & DS & STS & Job & Probes

Lighting Lab

Create a new deployment called nginx-deploy, with image nginx:1.16 and 1 replica.
Next, upgrade the deployment to version 1.17 using rolling update and add the annotation message
Updated nginx image to 1.17.

k create deployment nginx-deploy --image=nginx:1.16 --replicas=1
k set image deployment nginx-deploy nginx=nginx:1.17

Mock Exam 1

Create a deployment named hr-web-app using the image kodekloud/webapp-color with 2 replicas.

k create deployment hr-web-app --image=kodekloud/webapp-color --replicas=2

Mock Exam 2

Create a new deployment called nginx-deploy, with image nginx:1.16 and 1 replica. Next, upgrade the deployment to version 1.17 using rolling update. Note: Use the kubectl apply command to create or update the deployment.

k create deployment nginx-deploy --image=nginx:1.16 --replicas=1
k rollout history deployment nginx-deploy
k set image deployments nginx-deploy nginx=nginx:1.17
k rollout history deployment nginx-deploy

killer.sh: CKA-A Q3. Scale down StatefulSet

StatefulSets Scale a StatefulSet Solve this question on: ssh cka3962 There are two Pods named o3db-* in Namespace project-h800. The Project H800 management asked you to scale these down to one replica to save resources.

ssh cka3962
k -n project-h800 get pod --show-labels | grep o3db
k -n project-h800 scale sts o3db --replicas 1

killer.sh: CKA-A Q11. DaemonSet on all Nodes

DaemonSet Taints and Tolerations Solve this question on: ssh cka2556 Use Namespace project-tiger for the following. Create a DaemonSet named ds-important with image httpd:2-alpine and labels id=ds-important and uuid=18426a0b-5f59-4e10-923f-c0e078e82462. The Pods it creates should request 10 millicore cpu and 10 mebibyte memory. The Pods of that DaemonSet should run on all nodes, also controlplanes.

ssh cka2556
k -n project-tiger create deployment --image=httpd:2-alpine ds-important --dry-run=client -o yaml > 11.yaml
# cka2556:/home/candidate/11.yaml
apiVersion: apps/v1
kind: DaemonSet                                     # change from Deployment to DaemonSet
metadata:
  creationTimestamp: null
  labels:                                           # add
    id: ds-important                                # add
    uuid: 18426a0b-5f59-4e10-923f-c0e078e82462      # add
  name: ds-important
  namespace: project-tiger                          # important
spec:
  selector:
    matchLabels:
      id: ds-important                              # add
      uuid: 18426a0b-5f59-4e10-923f-c0e078e82462    # add
  template:
    metadata:
      labels:
        id: ds-important                            # add
        uuid: 18426a0b-5f59-4e10-923f-c0e078e82462  # add
    spec:
      containers:
      - image: httpd:2-alpine
        name: ds-important
        resources:
          requests:                                 # add
            cpu: 10m                                # add
            memory: 10Mi                            # add
      tolerations:                                  # add
      - effect: NoSchedule                          # add
        key: node-role.kubernetes.io/control-plane  # add
k apply -f 11.yaml

killer.sh: CKA-A Q12. Deployment on all Nodes

Deployments Assigning Pods to Nodes Solve this question on: ssh cka2556 Implement the following in Namespace project-tiger:

  • Create a Deployment named deploy-important with 3 replicas
  • The Deployment and its Pods should have label id=very-important
  • First container named container1 with image nginx:1-alpine
  • Second container named container2 with image registry.k8s.io/pause:3.10
  • There should only ever be one Pod of that Deployment running on one worker node, use topologyKey: kubernetes.io/hostname for this

ℹ️ Because there are two worker nodes and the Deployment has three replicas the result should be that the third Pod won’t be scheduled. In a way this scenario simulates the behaviour of a DaemonSet, but using a Deployment with a fixed number of replicas

ssh cka2556
k -n project-tiger create deployment --image=nginx:1-alpine deploy-important --dry-run=client -o yaml > 12.yaml
# cka2556:/home/candidate/12.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    id: very-important                  # change
  name: deploy-important
  namespace: project-tiger              # important
spec:
  replicas: 3                           # change
  selector:
    matchLabels:
      id: very-important                # change
  template:
    metadata:
      creationTimestamp: null
      labels:
        id: very-important              # change
    spec:
      containers:
      - image: nginx:1-alpine
        name: container1                # change
      - image: registry.k8s.io/pause:3.10  # add
        name: container2                # add
      affinity:                                             # add
        podAntiAffinity:                                    # add
          requiredDuringSchedulingIgnoredDuringExecution:   # add
          - labelSelector:                                  # add
              matchExpressions:                             # add
              - key: id                                     # add
                operator: In                                # add
                values:                                     # add
                - very-important                            # add
            topologyKey: kubernetes.io/hostname             # add

killer.sh: CKA-B Q4. Pod Ready if Service is reachable

Configure Probes Solve this question on: ssh cka3200 Do the following in Namespace default:

  • Create a Pod named ready-if-service-ready of image nginx:1-alpine
  • Configure a LivenessProbe which simply executes command true
  • Configure a ReadinessProbe which checks if the url http://service-am-i-ready:80 is reachable. You can use wget -T2 -O- http://service-am-i-ready:80 for this
  • Start the Pod and confirm it isn’t ready because of the ReadinessProbe. Then:
  • Create a second Pod named am-i-ready of image nginx:1-alpine with label id: cross-server-ready
  • The already existing Service service-am-i-ready should now have that second Pod as endpoint
  • Now the first Pod should be in ready state, check that
ssh cka3200
k run ready-if-service-ready --image=nginx:1-alpine --dry-run=client -o yaml > 4_pod1.yaml
# cka3200:/home/candidate/4_pod1.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: ready-if-service-ready
  name: ready-if-service-ready
spec:
  containers:
  - image: nginx:1-alpine
    name: ready-if-service-ready
    resources: {}
    livenessProbe:                                      # add from here
      exec:
        command:
        - 'true'
    readinessProbe:
      exec:
        command:
        - sh
        - -c
        - 'wget -T2 -O- http://service-am-i-ready:80'   # to here
k apply -f 4_pod1.yaml
k run am-i-ready --image nginx:1-alpine --labels id=cross-server-ready

Sidecar Container

Mock Exam 1

Create a Pod mc-pod in the mc-namespace namespace with three containers. The first container should be named mc-pod-1, run the nginx:1-alpine image, and set an environment variable NODE_NAME to the node name. The second container should be named mc-pod-2, run the busybox:1 image, and continuously log the output of the date command to the file /var/log/shared/date.log every second. The third container should have the name mc-pod-3, run the image busybox:1, and print the contents of the date.log file generated by the second container to stdout. Use a shared, non-persistent volume.

# vi pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: mc-pod
  namespace: mc-namespace
spec:
  containers:
  - name: mc-pod-1
    image: nginx:1-alpine
    env:
    - name: NODE_NAME
      valueFrom:
        fieldRef:
          fieldPath: spec.nodeName
  - name: mc-pod-2
    image: busybox:1
    command:
    - "sh"
    - "-c"
    - "while true; do date >> /var/log/shared/date.log; sleep 1; done"
    volumeMounts:
    - name: data
      mountPath: /var/log/shared
  - name: mc-pod-3
    image: busybox:1
    command:
    - "sh"
    - "-c"
    - "tail -f /var/log/shared/date.log"
    volumeMounts:
    - name: data
      mountPath: /var/log/shared
  volumes:
  - name: data
    emptyDir: {}
k apply -f pod.yaml
k -n mc-namespace logs mc-pod -c mc-pod-3 -f

Mock Exam 2

Create a deployment named logging-deployment in the namespace logging-ns with 1 replica, with the following specifications: The main container should be named app-container, use the image busybox, and should run the following command to simulate writing logs:

sh -c "while true; do echo 'Log entry' >> /var/log/app/app.log; sleep 5; done"

Add a sidecar container named log-agent that also uses the busybox image and runs the command:

tail -f /var/log/app/app.log

log-agent logs should display the entries logged by the main app-container

# vi deploy.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: logging-deployment
  namespace: logging-ns
spec:
  replicas: 1
  selector:
    matchLabels:
      app: logging-deployment
  template:
    metadata:
      labels:
        app: logging-deployment
    spec:
      containers:
      - image: busybox
        name: app-container
        command:
        - "sh"
        - "-c"
        - "while true; do echo 'Log entry' >> /var/log/app/app.log; sleep 5; done"
        volumeMounts:
        - name: data
          mountPath: /var/log/app
      initContainers:
      - name: log-agent
        image: busybox
        command:
        - "sh"
        - "-c"
        - "touch /var/log/app/app.log; tail -f /var/log/app/app.log"
        volumeMounts:
        - name: data
          mountPath: /var/log/app
      volumes:
      - name: data
        emptyDir: {}
k apply -f deploy.yaml
k -n logging-ns logs logging-deployment-78cdb9bdbf-f5wgf -c log-agent -f

prepium.sh Q7. Sidecar

Update the existing wordpress Deployment in the wordpress-ns namespace, adding a sidecar container named sidecar using the busybox:stable image to the existing pod. The new sidecar container has to run the following command:

/bin/sh -c tail -f /var/log/wordpress.log

Use a volume mounted at /var/log to make the log file wordpress.log available to the co-located container.

# k edit deployment -n wordpress-ns wordpress
...
    volumeMounts:
    - name: logs
      mountPath: /var/log
  - name: sidecar
    image: busybox:stable
    command:
    - "/bin/sh"
    - "-c"
    - "tail -f /var/log/wordpress.log"
    volumeMounts:
    - name: logs
      mountPath: /var/log
  volumes:
  - name: logs
    emptyDir: {}
...

killer.sh: CKA-B Q13. Multi Containers and Pod shared Volume

Shared Volume Between Containers Pod Info via Env Solve this question on: ssh cka3200 Create a Pod with multiple containers named multi-container-playground in Namespace default:

  • It should have a volume attached and mounted into each container. The volume shouldn’t be persisted or shared with other Pods
  • Container c1 with image nginx:1-alpine should have the name of the node where its Pod is running, available as environment variable MY_NODE_NAME
  • Container c2 with image busybox:1 should write the output of the date command every second in the shared volume into file date.log. You can use while true; do date >> /your/vol/path/date.log; sleep 1; done for this.
  • Container c3 with image busybox:1 should constantly write the content of file date.log from the shared volume to stdout. You can use tail -f /your/vol/path/date.log for this.

ℹ️ Check the logs of container c3 to confirm correct setup

ssh cka3200
# k run multi-container-playground --image=nginx:1-alpine --dry-run=client -o yaml > 13.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: multi-container-playground
  name: multi-container-playground
spec:
  containers:
  - image: nginx:1-alpine
    name: c1                                                                      # change
    env:                                                                          # add
    - name: MY_NODE_NAME                                                          # add
      valueFrom:                                                                  # add
        fieldRef:                                                                 # add
          fieldPath: spec.nodeName                                                # add
    volumeMounts:                                                                 # add
    - name: vol                                                                   # add
      mountPath: /vol                                                             # add
  - image: busybox:1                                                              # add
    name: c2                                                                      # add
    command: ["sh", "-c", "while true; do date >> /vol/date.log; sleep 1; done"]  # add
    volumeMounts:                                                                 # add
    - name: vol                                                                   # add
      mountPath: /vol                                                             # add
  - image: busybox:1                                                              # add
    name: c3                                                                      # add
    command: ["sh", "-c", "tail -f /vol/date.log"]                                # add
    volumeMounts:                                                                 # add
    - name: vol                                                                   # add
      mountPath: /vol                                                             # add
  volumes:                                                                        # add
    - name: vol                                                                   # add
      emptyDir: {}                                                                # add
k apply -f 13.yaml
k exec multi-container-playground -c c1 -- env
k logs multi-container-playground -c c3

Taint & Toleration, Node Affinity, Node Selector

Mock Exam 3

Taint the worker node node01 to be Unschedulable. Once done, create a pod called dev-redis, image redis:alpine, to ensure workloads are not scheduled to this worker node. Finally, create a new pod called prod-redis and image: redis:alpine with toleration to be scheduled on node01. key: env_type, value: production, operator: Equal and effect: NoSchedule

k taint node node01 env_type=production:NoSchedule
k run dev-redis --image=redis:alpine
# vi prod-redis.yaml
apiVersion: v1
kind: Pod
metadata:
  name: prod-redis
spec:
  containers:
  - image: redis:alpine
    name: prod-redis
  tolerations:
  - key: "env_type"
    operator: "Equal"
    value: "production"
    effect: "NoSchedule"

prepium.sh Q8. Taints & Tolerations

A worker node has been tainted with dedicated=gpu:NoSchedule and labeled gpu=true. A Deployment called web-app already exists in namespace cka-taints. It should not run on the tainted node. Task: Create a pod named gpu-pod in namespace cka-taints that:

  • Uses image nginx:1.25
  • Has a toleration for the taint dedicated=gpu:NoSchedule
  • Has a nodeSelector that targets nodes with label gpu=true
  • Is in a Running state
# vi pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: gpu-pod
  namespace: cka-taints
  labels:
    run: gpu-pod
spec:
  nodeSelector:
    gpu: "true"
  tolerations:
  - key: "dedicated"
    operator: "Equal"
    value: "gpu"
    effect: "NoSchedule"
  containers:
  - image: nginx:1.25
    name: gpu-pod
k apply -f pod.yaml

killer.sh: CKA-B Q9. Kill Scheduler, Manual Scheduling

Assigning Pods to Nodes Kubernetes Scheduler Solve this question on: ssh cka5248 Temporarily stop the kube-scheduler in a way that lets you start it again afterwards. Create a single Pod named manual-schedule of image httpd:2-alpine, confirm it’s created but not scheduled on any node. Now you’re the scheduler and have all its power, manually schedule that Pod on node cka5248. Make sure it’s running. Start the kube-scheduler again and confirm it’s running correctly by creating a second Pod named manual-schedule2 of image httpd:2-alpine and check if it’s running on cka5248-node1.

ssh cka5248
cd /etc/kubernetes/manifests/
mv kube-scheduler.yaml ..
k run manual-schedule --image=httpd:2-alpine
# k edit manual-schedule
apiVersion: v1
kind: Pod
metadata:
  name: manual-schedule
spec:
  nodeName: cka5248       # ADD the controlplane node name
  containers:
  - image: httpd:2-alpine
    name: manual-schedule
cd /etc/kubernetes/manifests/
mv ../kube-scheduler.yaml .
k run manual-schedule2 --image=httpd:2-alpine
k get pod -o wide | grep schedule

killer.sh: CKA-B Q12. Schedule Pod on Controlplane Nodes

Assigning Pods to Nodes Taints and Tolerations Solve this question on: ssh cka5248 Create a Pod of image httpd:2-alpine in Namespace default. The Pod should be named pod1 and the container should be named pod1-container. This Pod should only be scheduled on controlplane nodes. Do not add new labels to any nodes.

ssh cka5248
# k run pod1 --image=httpd:2-alpine --dry-run=client -o yaml > 12.yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: pod1
  name: pod1
spec:
  containers:
  - image: httpd:2-alpine
    name: pod1-container                       # change
  tolerations:                                 # add
  - effect: NoSchedule                         # add
    key: node-role.kubernetes.io/control-plane # add
  nodeSelector:                                # add
    node-role.kubernetes.io/control-plane: ""  # add
k get pod pod1 -o wide

Resource Requests and Limits

prepium.sh Q6. Resource Requests and Limits

The web-app Deployment in namespace resources-ns has 3 replicas but none of the pods are running. A ResourceQuota has been applied to the namespace that limits total CPU and memory. The Deployment does not have resource requests or limits configured, so the pods are being blocked by the quota. Task:

  1. Investigate why the pods are not being created
  2. Inspect the ResourceQuota to find the total CPU and memory budget
  3. Edit the Deployment so that each pod gets an equal share of the quota (requests must equal limits)
  4. Confirm all 3 pods are Running
k -n resources-ns describe quota
# k -n resources-ns edit deployment web-app
...
template:
  ...
  spec:
    containers:
	  ...
      resources:
        requests:
          cpu: "10m"
          memory: "10Mi"
        limits:
          cpu: "10m"
          memory: "10Mi"
	  ...

killer.sh: CKA-A Q4. Find Pods first to be terminated

Pod Quality of Service Classes Node-pressure Eviction Solve this question on: ssh cka2556 Check all available Pods in the Namespace project-c13 and find the names of those that would probably be terminated first if the nodes run out of resources (cpu or memory). Write the Pod names into /course/4/pods-terminated-first.txt.

ssh cka2556
k -n project-c13 get pod -o jsonpath="{range .items[*]} {.metadata.name}{.spec.containers[*].resources}{'\n'}"
k get pods -n project-c13 -o jsonpath="{range .items[*]}{.metadata.name} {.status.qosClass}{'\n'}"
# /course/4/pods-terminated-first.txt
c13-3cc-runner-heavy-8687d66dbb-gnxjh
c13-3cc-runner-heavy-8687d66dbb-przdh
c13-3cc-runner-heavy-8687d66dbb-wqwfz

PriorityClass

Mock Exam 3

Create a PriorityClass named low-priority with a value of 50000. A pod named lp-pod exists in the namespace low-priority. Modify the pod to use the priority class you created. Recreate the pod if necessary.

k create priorityclass low-priority --value=50000
# k -n low-priority edit pod lp-pod
...
spec:
  ...
  priorityClassName: low-priority
  ...

prepium.sh Q10. Priority Class

A Deployment named busybox-logger exists in the priority namespace. An existing user-defined PriorityClass user-existing has value 10000. Task:

  1. Create a new PriorityClass named high-priority with a value one less than the highest existing user-defined value (i.e. 9999)
  2. Patch the busybox-logger Deployment to use high-priority
k create priorityclass high-priority --value=9999
# k -n priority edit deployment busybox-logger
...
template:
  ...
  spec:
	...
    priorityClassName: high-priority
	...

HPA, VPA

Mock Exam 1

Create a Horizontal Pod Autoscaler (HPA) with name webapp-hpa for the deployment named kkapp-deploy in the default namespace with the webapp-hpa.yaml file located under the root folder.
Ensure that the HPA scales the deployment based on CPU utilization, maintaining an average CPU usage of 50% across all pods.
Configure the HPA to cautiously scale down pods by setting a stabilization window of 300 seconds to prevent rapid fluctuations in pod count. Note: The kkapp-deploy deployment is created for backend; you can check in the terminal.

# vi webapp-hpa.yaml 
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: webapp-hpa
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: kkapp-deploy
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300

Mock Exam 1

Deploy a Vertical Pod Autoscaler (VPA) with name analytics-vpa for the deployment named analytics-deployment in the default namespace.
The VPA should automatically adjust the CPU and memory requests of the pods to optimize resource utilization. Ensure that the VPA operates in Recreate mode, allowing it to evict and recreate pods with updated resource requests as needed.

# vi vpa.yaml 
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: analytics-vpa
spec:
  targetRef:
    apiVersion: "apps/v1"
    kind: Deployment
    name: analytics-deployment
  updatePolicy:
    updateMode: "Recreate"

Mock Exam 2

Create a Horizontal Pod Autoscaler with name backend-hpa for the deployment named backend-deployment in the backend namespace with the webapp-hpa.yaml file located under the root folder. Ensure that the HPA scales the deployment based on memory utilization, maintaining an average memory usage of 65% across all pods. Configure the HPA with a minimum of 3 replicas and a maximum of 15.

# vi webapp-hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: backend-hpa
  namespace: backend
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: backend-deployment
  minReplicas: 3
  maxReplicas: 15
  metrics:
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 65

Mock Exam 3

Create a Horizontal Pod Autoscaler (HPA) api-hpa for the deployment named api-deployment located in the api namespace.
The HPA should scale the deployment based on a custom metric named requests_per_second, targeting an average value of 1000 requests per second across all pods. Set the minimum number of replicas to 1 and the maximum to 20. Note: Deployment named api-deployment is available in api namespace. Ignore errors due to the metric requests_per_second not being tracked in metrics-server

# vi api-hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-hpa
  namespace: api
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api-deployment
  minReplicas: 1
  maxReplicas: 20
  metrics:
  - type: Pods
    pods:
      metric:
        name: requests_per_second
      target:
        type: AverageValue
        averageValue: 1k

JayDemy Q3. HPA

Create a new HorizontalPodAutoscaler (HPA) named apache-server in the autoscale namespace. This HPA must target the existing Deployment called apache-server in the autoscale namespace.

  • Set the HPA to target for 50% CPU usage per Pod.
  • Configure HPA to have at min 1 Pod and no more than 4 Pods.
  • Also, we have to set the downscale stabilization window to 30 seconds.
k autoscale deployment apache-server --cpu-percent=50 --min=1 --max=4 -n autoscale --dry-run=client -o yaml > hpa.yaml
# vi hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: apache-server
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: apache-server
  minReplicas: 1
  maxReplicas: 4
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  behavior:
    scaleDown:
      downscaleStabilizationWindow: 30
k apply -f hpa.yaml

Services & Networking 20%


CoreDNS

killer.sh: CKA-A Q16. Update CoreDNS Configuration

Customizing DNS Service DNS for Services and Pods Solve this question on: ssh cka5774 The CoreDNS configuration in the cluster needs to be updated:

  1. Make a backup of the existing configuration YAML and store it at /course/16/coredns_backup.yaml. You should be able to fast recover from the backup
  2. Update the CoreDNS configuration in the cluster so that SERVICE.NAMESPACE.custom-domain resolves the same as SERVICE.NAMESPACE.cluster.local (in addition to it) Test your configuration for example from a Pod with busybox:1 image. These commands should result in an IP address:
nslookup kubernetes.default.svc.cluster.local
nslookup kubernetes.default.svc.custom-domain
ssh cka5774
k -n kube-system get cm coredns -o yaml > /course/16/coredns_backup.yaml
k -n kube-system edit cm coredns
# k -n kube-system edit cm coredns
apiVersion: v1
data:
  Corefile: |
    .:53 {
        errors
        health {
           lameduck 5s
        }
        ready
        kubernetes custom-domain cluster.local in-addr.arpa ip6.arpa {  # ADD
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
        prometheus :9153
        forward . /etc/resolv.conf {
           max_concurrent 1000
        }
        cache 30 {
           disable success cluster.local
           disable denial cluster.local
        }
        loop
        reload
        loadbalance
    }
kind: ConfigMap
metadata:
  creationTimestamp: "2024-12-26T20:35:11Z"
  name: coredns
  namespace: kube-system
  resourceVersion: "262"
  uid: c76d208f-1bc8-4c0f-a8e8-a8bfa440870e
k -n kube-system rollout restart deploy coredns
k run test --image=busybox --rm -it --restart=Never -- nslookup kubernetes.default.svc.custom-domain
k run test --image=busybox --rm -it --restart=Never -- nslookup kubernetes.default.svc.cluster.local

killer.sh: CKA-B Q1. DNS / FQDN / Headless Service

DNS for Services and Pods Service Solve this question on: ssh cka6016 The Deployment controller in Namespace lima-control communicates with various cluster-internal endpoints by using their DNS FQDN values. Update the ConfigMap used by the Deployment with the correct FQDN values for:

  1. DNS_1: Service kubernetes in Namespace default
  2. DNS_2: Headless Service department in Namespace lima-workload
  3. DNS_3: Pod section100 in Namespace lima-workload. It should work even if the Pod IP changes
  4. DNS_4: A Pod with IP 1.2.3.4 in Namespace kube-system Ensure the Deployment works with the updated values.

ℹ️ You can use nslookup or dig inside a Pod of the controller Deployment

ssh cka6016
# kubectl -n lima-control edit cm control-config
apiVersion: v1
data:
  DNS_1: kubernetes.default.svc.cluster.local                  # UPDATE
  DNS_2: department.lima-workload.svc.cluster.local            # UPDATE
  DNS_3: section100.section.lima-workload.svc.cluster.local    # UPDATE
  DNS_4: 1-2-3-4.kube-system.pod.cluster.local                 # UPDATE
kind: ConfigMap
metadata:
  name: control-config
  namespace: lima-control
...

Service

Mock Exam 1

Create a service named messaging-service to expose the messaging pod within the cluster on port 6379. The messaging pod is running in the default namespace. Use imperative commands.

k expose pod messaging --name=messaging-service --port=6379

Mock Exam 1

Expose the hr-web-app created in the previous task as a service named hr-web-app-service, accessible on port 30082 on the nodes of the cluster. The web application listens on port 8080.

# k expose deployment hr-web-app --name=hr-web-app-service --port=8080 --type=NodePort --dry-run=client -o yaml > svc.yaml
# vi svc.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: hr-web-app
  name: hr-web-app-service
spec:
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
    nodePort: 30082
  selector:
    app: hr-web-app
  type: NodePort

Mock Exam 2

Create an nginx pod named nginx-resolver using the nginx image and expose it internally using a ClusterIP service called nginx-resolver-service. From within the cluster, verify:

  1. DNS resolution of the service name
  2. Network reachability of the pod using its IP address Use the busybox:1.28 image to perform the lookups. Save the service DNS lookup output to /root/CKA/nginx.svc and the pod IP lookup output to /root/CKA/nginx.pod.
k run nginx-resolver --image=nginx
k expose pod nginx-resolver --name=nginx-resolver-svc --port=80
k run test --image=busybox:1.28 --rm -it --restart=Never -- nslookup nginx-resolver-service > /root/CKA/nginx.svc
k run test --image=busybox:1.28 --rm -it --restart=Never -- nslookup 172-17-1-15.default.pod > /root/CKA/nginx.pod

JayDemy Q9. NodePort Service

Reconfigure the existing Deployment front-end in namespace sp-culator to expose port 80/tcp of the existing container nginx. Create a new Service named front-end-svc exposing the container port 80/tcp. Configure the new Service to also expose the individual pods via & NodePort

k expose deployment front-end --type=NodePort --port=80 --protocol=TCP --name=front-end-svc

DumbITGuy Q16. NodePort Service

There is a Deployment named nodeport-deployment in the relative namespace. Tasks:

  • Configure the Deployment so it can be exposed on port 80, name=http, protocol TCP
  • Create a new Service named nodeport-service exposing the container port 80, protocol TCP, NodePort 30080
  • Configure the new Service to also expose the individual pods using NodePort
# k -n relative edit deploy nodeport-deployment
...
template:
  ...
  spec:
    containers:
    - image: nginx
      ...
      ports:
      - name: http
        containerPort: 80
        protocol: TCP
      ...
# vi nodeport-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nodeport-service
  namespace: relative
spec:
  type: NodePort
  selector:
    app: nodeport-deployment
  ports:
   - port: 80
     targetPort: 80
     protocol: TCP
     nodePort: 30080

Ingress

Mock Exam 2

A Deployment named webapp-deploy is running in the ingress-ns namespace and is exposed via a Service named webapp-svc. Create an Ingress resource called webapp-ingress in the same namespace that will route traffic to the service. The Ingress must:

  • Use pathType: Prefix
  • Route requests sent to path / to the backend service
  • Forward traffic to port 80 of the service
  • Be configured for the host kodekloud-ingress.app Test app availablility using the following command:
curl -s http://kodekloud-ingress.app/
k create ingress webapp-ingress -n ingress-ns --class=nginx --rule=kodekloud-ingress.app/*=webapp-svc:80
curl -s http://kodekloud-ingress.app/

JayDemy Q4. Ingress

Create a new Ingress resource echo in echo-sound namespace exposing Service echoserver-service on http://example.org/echo using Service port 8080.

k -n echo-sound create ingress echo --class=nginx --rule=example.org/*=echoserver-service:8080

Gateway API

Mock Exam 1

Create a Kubernetes Gateway resource with the following specifications:

  1. Name: web-gateway
  2. Namespace: nginx-gateway
  3. Gateway Class Name: nginx
  4. Listeners:
    • Protocol: HTTP
    • Port: 80
    • Name: http
# vi gw.yaml 
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: web-gateway
  namespace: nginx-gateway
spec:
  gatewayClassName: nginx
  listeners:
  - name: http
    protocol: HTTP
    port: 80

Mock Exam 2

Modify the existing web-gateway on cka5673 namespace to handle HTTPS traffic on port 443 for kodekloud.com, using a TLS certificate stored in a secret named kodekloud-tls.

# k -n cka5673 edit gateway web-gateway
...
spec:
  ...
  listeners:
  - name: https
    port: 443
    protocol: HTTPS
    hostname: kodekloud.com
    tls:
      certificateRefs:
      - name: kodekloud-tls
  ...

Mock Exam 3

Configure the web-route to split traffic between web-service and web-service-v2.The configuration should ensure that 80% of the traffic is routed to web-service and 20% is routed to web-service-v2. Note: web-gateway, web-service, and web-service-v2 have already been created and are available on the cluster.

# vi web-route.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web-route
spec:
  parentRefs:
  - name: web-gateway
  rules:
  - backendRefs:
    - name: web-service
      port: 80
      weight: 80
    - name: web-service-v2
      port: 80
      weight: 20

prepium.sh Q5. Gateway API

Migrate the existing Ingress web in namespace web-app to the new Gateway API. A GatewayClass named nginx is already installed. Use API version v1beta1 on this environment. Task 1: Create a Gateway named web-gateway with:

  • hostname: gateway.web.k8s.local
  • TLS termination using secret web-tls
  • GatewayClass: nginx Task2: Create an HTTPRoute name web-route with:
  • hostname: gateway.web.k8s.local
  • path prefix / -> service web-service:80
  • parentRef: the web-gateway
# vi web-gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: web-gateway
  namespace: web-app
spec:
  gatewayClassName: nginx
  listeners:
  - name: https
    protocol: HTTPS
    port: 443
    hostname: "gateway.web.k8s.local"
    tls:
      mode: Terminate
      certificateRefs:
      - name: web-tls
# vi httproute.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web-route
  namespace: web-app
spec:
  parentRefs:
  - name: web-gateway
  hostnames:
  - "gateway.web.k8s.local"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: web-service
      port: 80

killer.sh: CKA-A Q13. Gateway API Ingress

Gateway API Gateway API Documentation Solve this question on: ssh cka7968 The team from Project r500 wants to replace their Ingress (networking.k8s.io) with a Gateway API (gateway.networking.k8s.io) solution. The old Ingress is available at /course/13/ingress.yaml. Perform the following in Namespace project-r500 and for the already existing Gateway:

  1. Create a new HTTPRoute named traffic-director which replicates the routes from the old Ingress
  2. Extend the new HTTPRoute with path /auto which forwards to mobile backend if the User-Agent is exactly mobile and to desktop backend otherwise The existing Gateway is reachable at http://r500.gateway:30080 which means your implementation should work for these commands:
curl r500.gateway:30080/desktop
curl r500.gateway:30080/mobile
curl r500.gateway:30080/auto -H "User-Agent: mobile" 
curl r500.gateway:30080/auto
ssh cka7968
# cka7968:/home/candidate/13.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: traffic-director
  namespace: project-r500
spec:
  parentRefs:
    - name: main
  hostnames:
    - "r500.gateway"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /desktop
      backendRefs:
        - name: web-desktop
          port: 80
    - matches:
        - path:
            type: PathPrefix
            value: /mobile
      backendRefs:
        - name: web-mobile
          port: 80
# NEW FROM HERE ON
    - matches:
        - path:
            type: PathPrefix
            value: /auto
          headers:
          - type: Exact
            name: user-agent
            value: mobile
      backendRefs:
        - name: web-mobile
          port: 80
    - matches:
        - path:
            type: PathPrefix
            value: /auto
      backendRefs:
        - name: web-desktop
          port: 80
k apply -f 13.yaml
curl r500.gateway:30080/desktop
curl r500.gateway:30080/mobile
curl r500.gateway:30080/auto -H "User-Agent: mobile" 
curl r500.gateway:30080/auto
Web Desktop App
Web Mobile App
Web Mobile App
Web Desktop App

NetworkPolicy

Mock Exam 2

You are requested to create a NetworkPolicy to allow traffic from frontend apps located in the frontend namespace, to backend apps located in the backend namespace, but not from the databases in the databases namespace. There are three policies available in the /root folder. Apply the most restrictive policy from the provided YAML files to achieve the desired result. Do not delete any existing policies.

# cat net-pol-3.yaml 
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: net-policy-3
  namespace: backend
spec:
  podSelector: {}
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: frontend
    ports:
    - protocol: TCP
      port: 80
k apply -f net-pol-3.yaml

Mock Exam 3

A pod called np-test-1 and a service called np-test-service have been deployed in the default namespace. A default-deny NetworkPolicy is currently blocking all ingress traffic to pods in this namespace, which is why the service is unreachable. Create a new NetworkPolicy named ingress-to-nptest in the default namespace that allows ingress traffic from all sources to the np-test-1 pod on port 80. Important: Don’t delete any current objects deployed.

# vi np.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: ingress-to-nptest
  namespace: default
spec:
  podSelector:
    matchLabels:
      run: np-test-1
  policyTypes:
  - Ingress
  ingress:
  - ports:
    - protocol: TCP
      port: 80

prepium.sh Q3. Network Policy

There are two deployments, Frontend and Backend. Frontend is in the frontend namespace. Backend is in the backend namespace. Task Look at the Network Policy YAML file in /root/exam_resources. Decide which of the policies provides the functionality to allow interaction between the frontend and the backend deployments in the least permissive way and deploy that YAML.

# netpol2.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: netpol2
  namespace: backend-ns
spec:
  podSelector:
    matchLabels:
      role: backend
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: frontend-ns
      podSelector:
        matchLabels:
          role: frontend
k apply -f netpol2.yaml

killer.sh: CKA-A Q15. NetworkPolicy

Network Policies Declare Network Policy Solve this question on: ssh cka7968 There was a security incident where an intruder was able to access the whole cluster from a single hacked backend Pod. To prevent this create a NetworkPolicy called np-backend in Namespace project-snake. It should allow the backend-* Pods only to:

  • Connect to db1-* Pods on port 1111
  • Connect to db2-* Pods on port 2222 Use the app Pod labels in your policy.

ℹ️ All Pods in the Namespace run plain Nginx images. This allows simple connectivity tests like: k -n project-snake exec POD_NAME -- curl POD_IP:PORT ℹ️ For example, connections from backend-* Pods to vault-* Pods on port 3333 should no longer work

ssh cka7968
k -n project-snake get pod -L app
k -n project-snake get pod -o wide
# cka7968:/home/candidate/15_np.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: np-backend
  namespace: project-snake
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
    - Egress                    # policy is only about Egress
  egress:
    - to:                           # first condition "to"
      - podSelector:
          matchLabels:
            app: db1
      ports:                        # second condition "port"
      - protocol: TCP
        port: 1111
    - to:                           # first condition "to"
      - podSelector:
          matchLabels:
            app: db2
      ports:                        # second condition "port"
      - protocol: TCP
        port: 2222
k apply -f 15_np.yaml
k -n project-snake exec backend-0 -- curl -s 10.44.0.25:1111
k -n project-snake exec backend-0 -- curl -s 10.44.0.23:2222
k -n project-snake exec backend-0 -- curl -s 10.44.0.22:3333
^C

Storage 10%


PersistentVolume, PersistentVolumeClaim, StorageClass

Lighting Lab

A new deployment called alpha-mysql has been deployed in the alpha namespace. However, the pods are not running. Troubleshoot and fix the issue. The deployment should make use of the persistent volume alpha-pv to be mounted at /var/lib/mysql and should use the environment variable MYSQL_ALLOW_EMPTY_PASSWORD=1 to make use of an empty root password. Important: Do not alter the persistent volume.

k config set-context --current -n alpha
k get pvc alpha-claim -o yaml > pvc.yaml
# vi pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-alpha-pvc
  namespace: alpha
spec:
  accessModes:
  - ReadWriteOnce         # 수정 ReadWriteMany -> ReadWriteOnce
  resources:
    requests:
      storage: 1Gi        # 수정 2Gi -> 1Gi
  storageClassName: slow  # 수정 slow-storage -> slow
k delete pvc alpha-claim
k apply -f pvc.yaml
k edit deployments.apps alpha-mysql  # persistentVolumeClaim.claimName 수정

Mock Exam 1

Create a Persistent Volume with the given specification:

  • Volume name: pv-analytics
  • Storage: 100Mi
  • Access mode: ReadWriteMany
  • Host path: /pv/data-analytics
# vi pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-analytics
spec:
  capacity:
    storage: 100Mi
  accessModes:
    - ReadWriteMany
  hostPath:
    path: "/pv/data-analytics"

Mock Exam 2

Create a StorageClass named local-sc with the following specifications and set it as the default storage class:

  • The provisioner should be kubernetes.io/no-provisioner
  • The volume binding mode should be WaitForFirstConsumer
  • Volume expansion should be enabled
# vi sc.yaml 
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-sc
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
provisioner: kubernetes.io/no-provisioner
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer

Mock Exam 3

Create a StorageClass named rancher-sc with the following specifications: The provisioner should be rancher.io/local-path.
The volume binding mode should be WaitForFirstConsumer.
Volume expansion should be enabled.

# vi sc.yaml 
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: rancher-sc
provisioner: rancher.io/local-path
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer

prepium.sh Q9. StorageClass

Create a new StorageClass named local-storage with the provisioner rancher.io/local-path. Set volumeBindingMode to WaitForFirstConsumer. Do not make it the default SC. Patch the StorageClass to make it the default StorageClass. Ensure local-storage is the only default class. Do not modify any existing Deployment or PersistentVolumeClaims.

# vi local-storage.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: rancher.io/local-path
volumeBindingMode: WaitForFirstConsumer
k apply -f local-storage.yaml
k patch storageclass standard -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
k patch storageclass local-storage -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

JayDemy Q14. Pod PVC

A user accidentally deleted the MariaDB Deployment in the mariadb namespace, which was configured with persistent storage. Your responsibility is to re-establish the Deployment while ensuring data is preserved by reusing the available PersistentVolume. Taks: A PersistentVolume already exists and is retained for reuse. only one PV exist. Create a PVC named mariadb in the mariadb namespace with the spec:

  • Access mode ReadWriteOnce and Storage 250Mi
  • Edit the MariaDB Deployment file located at ~/mariadb-deploy.yaml to use PVC created in the previous step.
  • Apply the updated Deployment file to the cluster.
  • Ensure the MariaDB Deployment is Running and Stable.
# vi pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mariadb
  namespace: mariadb
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 250Mi
  volumeName: mariadb-pv
# vi ~/mariadb-deploy.yaml
...
template:
  spec:
    volumes:
    - name: mariadb-storage
      persistentVolumeClaim:
        claimName: mariadb
  ...

killer.sh: CKA-A Q6. Storage, PV, PVC, Pod volume

Persistent Volumes Configure a Pod to use storage Solve this question on: ssh cka7968 Create a new PersistentVolume named safari-pv. It should have a capacity of 2Gi, accessMode ReadWriteOnce, hostPath /Volumes/Data and no storageClassName defined. Next create a new PersistentVolumeClaim in Namespace project-t230 named safari-pvc. It should request 2Gi storage, accessMode ReadWriteOnce and should not define a storageClassName. The PVC should be bound to the PV correctly. Finally create a new Deployment safari in Namespace project-t230 which mounts that volume at /tmp/safari-data. The Pods of that Deployment should be of image httpd:2-alpine.

ssh cka7968
# cka7968:/home/candidate/6_pv.yaml
kind: PersistentVolume
apiVersion: v1
metadata:
 name: safari-pv
spec:
 capacity:
  storage: 2Gi
 accessModes:
  - ReadWriteOnce
 hostPath:
  path: "/Volumes/Data"
# cka7968:/home/candidate/6_pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: safari-pvc
  namespace: project-t230
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
     storage: 2Gi
k -n project-t230 create deploy safari --image=httpd:2-alpine --dry-run=client -o yaml > 6_dep.yaml
# cka7968:/home/candidate/6_dep.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: safari
  name: safari
  namespace: project-t230
spec:
  replicas: 1
  selector:
    matchLabels:
      app: safari
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: safari
    spec:
      volumes:                                      # add
      - name: data                                  # add
        persistentVolumeClaim:                      # add
          claimName: safari-pvc                     # add
      containers:
      - image: httpd:2-alpine
        name: container
        volumeMounts:                               # add
        - name: data                                # add
          mountPath: /tmp/safari-data               # add

killer.sh: CKA-B Q10. PV PVC Dynamic Provisioning

Storage Classes Dynamic Volume Provisioning Solve this question on: ssh cka6016 There is a backup Job which needs to be adjusted to use a PVC to store backups. Create a StorageClass named local-backup which uses provisioner: rancher.io/local-path and volumeBindingMode: WaitForFirstConsumer. To prevent possible data loss the StorageClass should keep a PV retained even if a bound PVC is deleted. Adjust the Job at /course/10/backup.yaml to use a PVC which requests 50Mi storage and uses the new StorageClass. The PV should be created automatically by the provisioner and not manually. Deploy your changes, verify the Job completed once and the PVC was bound to a newly created PV.

ℹ️ To re-run a Job, delete it and create it again ℹ️ The abbreviation PV stands for PersistentVolume and PVC for PersistentVolumeClaim

ssh cka6016
# cka6016:/home/candidate/sc.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-backup
provisioner: rancher.io/local-path
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
k apply -f sc.yaml
# cka6016:/course/10/backup.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: backup-pvc
  namespace: project-bern            # use same Namespace
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 50Mi                  # request the required size
  storageClassName: local-backup     # use the new StorageClass
---
apiVersion: batch/v1
kind: Job
metadata:
  name: backup
  namespace: project-bern
spec:
  backoffLimit: 0
  template:
    spec:
      volumes:
        - name: backup
          persistentVolumeClaim:     # CHANGE
            claimName: backup-pvc    # CHANGE
      containers:
        - name: bash
          image: bash:5
          command:
            - bash
            - -c
            - |
              set -x
              touch /backup/backup-$(date +%Y-%m-%d-%H-%M-%S).tar.gz
              sleep 15
          volumeMounts:
            - name: backup
              mountPath: /backup
      restartPolicy: Never
k apply -f backup.yaml

Troubleshooting 30%


kubectl

Lighting Lab

Print the names of all deployments in the admin2406 namespace in the following format: DEPLOYMENT CONTAINER_IMAGE READY_REPLICAS NAMESPACE <deployment name> <container image used> <ready replica count> <Namespace>. The data should be sorted by the increasing order of the deployment name. Example: DEPLOYMENT CONTAINER_IMAGE READY_REPLICAS NAMESPACE deploy0 nginx:alpine 1 admin2406 Write the result to the file /opt/admin2406_data.

k -n admin2406 get deployments -o custom-columns='DEPLOYMENT:.metadata.name,CONTAINER_IMAGE:.spec.template.spec.containers[*].image,READY_REPLICAS:.status.readyReplicas,NAMESPACE:.metadata.namespace' > /opt/admin2406_data

killer.sh: CKA-A Q7. Node and Pod Resource Usage

Tools for Monitoring Resources kubectl Quick Reference Solve this question on: ssh cka5774 The metrics-server has been installed in the cluster. Write two bash scripts which use kubectl:

  1. Script /course/7/node.sh should show resource usage of nodes
  2. Script /course/7/pod.sh should show resource usage of Pods and their containers
ssh cka5774
echo "kubectl top node" > /course/7/node.sh
echo "kubectl top pod --containers=true" > /course/7/pod.sh

killer.sh: CKA-B Q5. Kubectl sorting

kubectl Quick Reference Solve this question on: ssh cka8448 Create two bash script files which use kubectl sorting to:

  1. Write a command into /course/5/find_pods.sh which lists all Pods in all Namespaces sorted by their AGE (metadata.creationTimestamp)
  2. Write a command into /course/5/find_pods_uid.sh which lists all Pods in all Namespaces sorted by field metadata.uid
ssh cka8448
echo "kubectl get pod -A --sort-by=.metadata.creationTimestamp" > /course/5/find_pods.sh
echo "kubectl get pod -A --sort-by=.metadata.uid" > /course/5/find_pods_uid.sh

killer.sh: CKA-B Q15. Cluster Event Logging

Debug with crictl kubectl Quick Reference Solve this question on: ssh cka6016

  1. Write a kubectl command into /course/15/cluster_events.sh which shows the latest events in the whole cluster, ordered by time (metadata.creationTimestamp)
  2. Delete the kube-proxy Pod and write the events this caused into /course/15/pod_kill.log on cka6016
  3. Manually kill the containerd container of the kube-proxy Pod and write the events into /course/15/container_kill.log
ssh cka6016
echo "kubectl get events -A --sort-by=.metadata.creationTimestamp" > /course/15/cluster_events.sh
k -n kube-system delete pod kube-proxy-lf2fs
kubectl get events -A --sort-by=.metadata.creationTimestamp > /course/15/pod_kill.log
crictl ps | grep kube-proxy
crictl rm --force 2fd052f1fcf78
kubectl get events -A --sort-by=.metadata.creationTimestamp > /course/15/container_kill.log

killer.sh: CKA-B Q16. Namespaces and API Resources

kubectl Quick Reference Namespaces Solve this question on: ssh cka3200 Write the names of all namespaced Kubernetes resources (like Pod, Secret, ConfigMap…) into /course/16/resources.txt. Find the project-* Namespace with the highest number of Roles defined in it and write its name and amount of Roles into /course/16/crowded-namespace.txt.

ssh cka3200
k api-resources --namespaced -o name > /course/16/resources.txt
k -n project-miami get role --no-headers | wc -l
echo "project-miami 300" > /course/16/crowded-namespace.txt

kubelet & crictl & journalctl & Control Plane

Mock Exam 3

vi /etc/kubernetes/manifests/kube-controller-manager.yaml  # 문제가 있는 부분 수정

JayDemy Q16. Troubleshooting, crictl, journalctl

A kubeadm provisioned cluster was migrated to a new machine. Requires configuration changes to run successfully. Task: We need to fix a single-node cluster that got broken during machine migration. Identify the broken cluster components and investigate what caused to break those components. The decommissioned cluster used an external etcd server. Next, fix the configuration of all broken cluster components. Ensure to restart all necessary services and components for changes to take effect. Finally, ensure the cluster, single node and all pods are Ready.

k get po                                         # kube-apiserver 가 응답하지 않음
crictl ps -a | grep apiserver                    # kubectl 대신 crictl 사용
crictl logs ${apiserver=container-id}            # apiserver 로그 확인
journalctl -u kubelet -f                         # kubelet 로그에서 문제가 있는 컴포넌트 확인
vi /etc/kubernetes/manifests/kube-apiserver.yaml  # 문제가 있는 부분 수정. 보통 etcd 주소가 잘못된 경우. 127.0.0.1:2379

killer.sh: CKA-A Q17. Find Container of Pod and check info

Debug with crictl Solve this question on: ssh cka2556 In Namespace project-tiger create a Pod named tigers-reunite of image httpd:2-alpine with labels pod=container and container=pod. Find out on which node the Pod is scheduled. Ssh into that node and find the containerd container belonging to that Pod. Using command crictl:

  1. Write the ID of the container and the info.runtimeType into /course/17/pod-container.txt on cka2556
  2. Write the logs of the container into /course/17/pod-container.log on cka2556

ℹ️ You can connect to a worker node using ssh cka2556-node1 or ssh cka2556-node2 from cka2556

ssh cka2556
k -n project-tiger run tigers-reunite --image=httpd:2-alpine --labels "pod=container,container=pod"
ssh cka2556-node1
sudo -i
crictl ps | grep tigers-reunite
crictl inspect ba62e5d465ff0 | grep runtimeType
crictl logs ba62e5d465ff0
exit
echo "ba62e5d465ff0 io.containerd.runc.v2" > /course/17/pod-container.txt
echo "ssh cka2556-node1 crictl logs ba62e5d465ff0" > /course/17/pod-container.log

killer.sh: CKA-B Q6. Fix Kubelet

Troubleshooting kubeadm Solve this question on: ssh cka1024 There seems to be an issue with the kubelet on controlplane node cka1024. It’s not running. Fix the kubelet and confirm that the node is available in Ready state. Create a Pod called success in default Namespace of image nginx:1-alpine.

ℹ️ The node has no taints and can schedule Pods without additional tolerations

ssh cka1024
systemctl cat kubelet
journalctl -u kubulet
cat /var/lib/kublet/config.yaml
whereis kubelet
vim /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf
systemctl daemon-reload
systemctl restart kubelet
systemctl status kubelet
k run success --image nginx:1-alpine

killer.sh: CKA-B Q8. Get Controlplane Information

Kubernetes Components Create static Pods Solve this question on: ssh cka8448 Check how the controlplane components kubelet, kube-apiserver, kube-scheduler, kube-controller-manager and etcd are started/installed on the controlplane node. Also find out the name of the DNS application and how it’s started/installed in the cluster. Write your findings into file /course/8/controlplane-components.txt. The file should be structured like:

kubelet: [TYPE]
kube-apiserver: [TYPE]
kube-scheduler: [TYPE]
kube-controller-manager: [TYPE]
etcd: [TYPE]
dns: [TYPE] [NAME]

Choices of [TYPE] are: not-installed, process, static-pod, pod

ssh cka8448
# /course/8/controlplane-components.txt
kubelet: process
kube-apiserver: static-pod
kube-scheduler: static-pod
kube-controller-manager: static-pod
etcd: static-pod
dns: pod coredns

killer.sh: CKA-B Q14. Find out Cluster Information

Cluster Networking Create static Pods Solve this question on: ssh cka8448 You’re asked to find out the following information about the cluster:

  1. How many controlplane nodes are available?
  2. How many worker nodes (non controlplane nodes) are available?
  3. What is the Service CIDR?
  4. Which Networking (or CNI Plugin) is configured and where is its config file?
  5. Which suffix will static pods have that run on cka8448? Write your answers into file /course/14/cluster-info, structured like this:
# /course/14/cluster-info
1: [ANSWER]
2: [ANSWER]
3: [ANSWER]
4: [ANSWER]
5: [ANSWER]
ssh cka8448
# /course/14/cluster-info
1: 1
2: 0
3: 10.96.0.0/12 # cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep "--service-cluster-ip-range"
				# OR /etc/kubernetes/manifests/kube-controller-manager.yaml
4: Weave, /etc/cni/net.d/10-weave.conflist
5: -cka8448

References