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

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
  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 uninstall
  11. Network Policy 생성

Cluster Architecture, Installation & Configuration 25%


CRI

JayDemy

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  # 검증

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

kernel parameter 설정

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

CNI, podSubnet

Prepium

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 configmap 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

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
cat /root/pod-cidr.txt 
172.17.0.0/16

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 작동 확인

kubeadm Cluster Upgrade

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

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

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

CRD

Prepium

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

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

Helm

JayDemy

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 template argocd argo/argo-cd --namespace argocd --version 7.7.3 --set crds.install=false > /argo-helm.yaml
helm install argocd argo/argo-cd --namespace argocd --version 7.7.3 --set crds.install=false

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

Review

  • helm 으로 argocd 설치
  • helm install
  • helm upgrade
  • helm get values

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

ConfigMap, Secrets

Prepium

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}  # 실패해야 정상

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

Deployment

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

Sidecar Container

Prepium

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: {}
...

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

Taint & Toleration, Node Affinity, Node Selector

Prepium

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

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"

Review

  • Pod 에 nodeSelector (disktype=ssd) 추가하여 특정 Node 에 배포

Resource Requests and Limits

Prepium

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 edit deployment web-app
...
template:
  ...
  spec:
    containers:
	  ...
      resources:
        requests:
          cpu: "10m"
          memory: "10Mi"
        limits:
          cpu: "10m"
          memory: "10Mi"
	  ...

PriorityClass

Prepium

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
	...

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
  ...

HPA, VPA

JayDemy

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

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

Services & Networking 20%


CoreDNS

Review

  • /etc/hosts 에서 주소를 찾고 없으면 /etc/resolv.conf 에서 주소를 찾음

Service

JayDemy

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

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

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

Ingress

JayDemy

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

# vi ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: echo-ingress
  namespace: echo-sound
spec:
  ingressClassName: nginx
  rules:
  - host: example.org
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: echoserver-service
            port:
              number: 8080

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/

Gateway API

Prepium

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

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-gatewayweb-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

NetworkPolicy

Prepium

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

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

Storage 10%


PersistentVolume, PersistentVolumeClaim, StorageClass

Prepium

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

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:
  volumes:
  - name: mariadb-storage
    persistentVolumeClaim:
      claimName: mariadb
  ...

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

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

Review

  • Pod 로그 확인하여 특정 단어가 들어간 log grep 해서 파일로 저장
  • Taint 가 없는 노드 개수 파일로 저장
  • 노드 Ready 개수 파일로 저장
  • 사용률이 가장 높은 파드를 특정 label 로 조회해서 파일로 저장
  • explain 명령어로 특정 속성값 정보 파일에 저장하기

kubelet

Review

  • 리눅스 kubelet 로그 보기 journal -u kubulet -r - kubelet의 설정 파일 위치  /var/lib/kublet/config.yaml
  • whereis kubelet  명령어  kubelet 의 바이너리 경로를 확인가능
  • systemctl cat kubelet 쿠블렛 설정파일 위치 찾기

Node Troubleshooting

Review

# 클러스터에서 NotReady 상태의 노드 확인
# 원인 파악, 해당 노드를 Ready 상태로 만들기
# 1. containerd 가 작동하고 있어야 함
# 2. kubelet 이 작동하고 있어야 함
# 3. cni 가 작동하고 있어야 함
kubectl get nodes
ssh hk8s-worker2
systemctl status containerd
systemctl status kubelet # 보통 kubelet 이 inactive 상태임
systemctl enable --now kubelet
systemctl status kubelet
exit
kubectl get nodes
  • NotReady 상태 Ready 가 되도록 트러블슈팅
  • 보통 kubelet

Pod Troubleshooting

JayDemy

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/kubernets/manifests/kube-apiserver.yaml  # 문제가 있는 부분 수정. 보통 etcd 주소가 잘못된 경우. 127.0.0.1:2379

Mock Exam 3

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

References