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
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.yamlcustom-resources.yaml
Requirements
The CNI you install must:
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.kubeconfigapiVersion: v1clusters:- cluster: certificate-authority-data: XXX... server: https://controlplane:6443 # 포트 수정 name: kubernetes...
k get nodes --kubeconfig /root/CKA/admin.kubeconfig # kubeconfig 작동 확인
Fix kubeconfig (8)
controlplane ~ ➜ cat CKA/super.kubeconfig
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ...
server: https://controlplane:9999
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
users:
- name: kubernetes-admin
user:
client-certificate-data: ...
client-key-data: ...
controlplane ~ ➜ k get node --kubeconfig=/root/CKA/super.kubeconfig
E1126 14:09:25.390277 46273 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"https://controlplane:9999/api?timeout=32s\": dial tcp 192.168.0.191:9999: connect: connection refused"
E1126 14:09:25.390610 46273 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"https://controlplane:9999/api?timeout=32s\": dial tcp 192.168.0.191:9999: connect: connection refused"
E1126 14:09:25.392730 46273 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"https://controlplane:9999/api?timeout=32s\": dial tcp 192.168.0.191:9999: connect: connection refused"
E1126 14:09:25.393061 46273 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"https://controlplane:9999/api?timeout=32s\": dial tcp 192.168.0.191:9999: connect: connection refused"
E1126 14:09:25.394498 46273 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"https://controlplane:9999/api?timeout=32s\": dial tcp 192.168.0.191:9999: connect: connection refused"
The connection to the server controlplane:9999 was refused - did you specify the right host or port?
controlplane ~ ✖ sudo netstat -tulnp | grep kube-apiserver
tcp6 0 0 :::6443 :::* LISTEN 2847/kube-apiserver
# port 수정
controlplane ~ ➜ vi /root/CKA/super.kubeconfig
controlplane ~ ➜ k get node --kubeconfig=/root/CKA/super.kubeconfig
NAME STATUS ROLES AGE VERSION
controlplane Ready control-plane 49m v1.34.0
node01 Ready <none> 48m v1.34.0
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 ~ ➜ k create serviceaccount pvviewer
serviceaccount/pvviewer created
controlplane ~ ➜ k get sa
NAME SECRETS AGE
default 0 19m
pvviewer 0 4s
controlplane ~ ➜ k create clusterrole pvviewer-role --resource=persistentvolumes --verb=list
clusterrole.rbac.authorization.k8s.io/pvviewer-role created
controlplane ~ ➜ k describe clusterrole pvviewer-role
Name: pvviewer-role
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
persistentvolumes [] [] [list]
controlplane ~ ➜ k create clusterrolebinding pvviewer-role-binding --clusterrole=pvviewer-role --serviceaccount=default:pvviewer
clusterrolebinding.rbac.authorization.k8s.io/pvviewer-role-binding created
controlplane ~ ➜ k describe clusterrolebindings pvviewer-role-binding
Name: pvviewer-role-binding
Labels: <none>
Annotations: <none>
Role:
Kind: ClusterRole
Name: pvviewer-role
Subjects:
Kind Name Namespace
---- ---- ---------
ServiceAccount pvviewer default
controlplane ~ ➜ k run pvviewer --image=redis --dry-run=client -o yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: pvviewer
name: pvviewer
spec:
containers:
- image: redis
name: pvviewer
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
controlplane ~ ➜ vi redis.yaml
controlplane ~ ➜ k apply -f redis.yaml
pod/pvviewer created
controlplane ~ ➜ k get pod
NAME READY STATUS RESTARTS AGE
pvviewer 1/1 Running 0 6s
ServiceAccount 생성
ClusterRole/Role 생성
ClusterRoleBinding 생성 후 확인
CRD
Prepium CRD Question
Task
Create a list of all cert-manager CRDs and save it to /root/resources.yaml
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.yamlk explain certificate.spec.subject > /root/documentation.txt
controlplane ~ ➜ helm list -n kk-nsNAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSIONkk-mock1 kk-ns 1 2025-11-06 14:28:24.255194099 +0000 UTC deployed nginx-18.1.0 1.27.0controlplane ~ ➜ helm repo listNAME URLkk-mock1 https://charts.bitnami.com/bitnamicontrolplane ~ ➜ helm repo updateHang tight while we grab the latest from your chart repositories......Successfully got an update from the "kk-mock1" chart repositoryUpdate Complete. ⎈Happy Helming!⎈controlplane ~ ➜ helm repo listNAME URLkk-mock1 https://charts.bitnami.com/bitnamicontrolplane ~ ➜ helm search repo nginxNAME CHART VERSION APP VERSION DESCRIPTIONkk-mock1/nginx 22.2.4 1.29.3 NGINX Open Source is a web server that can be a...kk-mock1/nginx-ingress-controller 12.0.7 1.13.1 NGINX Ingress Controller is an Ingress controll...kk-mock1/nginx-intel 2.1.15 0.4.9 DEPRECATED NGINX Open Source for Intel is a lig...controlplane ~ ➜ helm search repo nginx --versions | grep 18.1.15kk-mock1/nginx 18.1.15 1.27.1 NGINX Open Source is a web server that can be a...controlplane ~ ➜ helm upgrade kk-mock1 kk-mock1/nginx --version=18.1.5 -n kk-nsRelease "kk-mock1" has been upgraded. Happy Helming!NAME: kk-mock1LAST DEPLOYED: Thu Nov 6 14:31:52 2025NAMESPACE: kk-nsSTATUS: deployedREVISION: 2TEST SUITE: NoneNOTES:CHART NAME: nginxCHART VERSION: 18.1.5APP VERSION: 1.27.0** Please be patient while the chart is being deployed **NGINX can be accessed through the following DNS name from within your cluster: kk-mock1-nginx.kk-ns.svc.cluster.local (port 80)To access NGINX from outside the cluster, follow the steps below:1. Get the NGINX URL by running these commands: NOTE: It may take a few minutes for the LoadBalancer IP to be available. Watch the status with: 'kubectl get svc --namespace kk-ns -w kk-mock1-nginx' export SERVICE_PORT=$(kubectl get --namespace kk-ns -o jsonpath="{.spec.ports[0].port}" services kk-mock1-nginx) export SERVICE_IP=$(kubectl get svc --namespace kk-ns kk-mock1-nginx -o jsonpath='{.status.loadBalancer.ingress[0].ip}') echo "http://${SERVICE_IP}:${SERVICE_PORT}"WARNING: There are "resources" sections in the chart not set. Using "resourcesPreset" is not recommended for production. For production installations, please set the following values according to your workload needs: - cloneStaticSiteFromGit.gitSync.resources - resources+info https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/⚠ SECURITY WARNING: Original containers have been substituted. This Helm chart was designed, tested, and validated on multiple platforms using a specific set of Bitnami and Tanzu Application Catalog containers. Substituting other containers is likely to cause degraded security and performance, broken chart features, and missing environment variables.Substituted images detected: - %!s(<nil>)/:%!s(<nil>)controlplane ~ ➜ helm list -n kk-nsNAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSIONkk-mock1 kk-ns 2 2025-11-06 14:31:52.339403678 +0000 UTC deployed nginx-18.1.5 1.27.0
controlplane ~ ➜ helm lint /root/new-version/
==> Linting /root/new-version/
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed
controlplane ~ ➜ helm install --generate-name /root/new-version/
NAME: new-version-1764167838
LAST DEPLOYED: Wed Nov 26 14:37:18 2025
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
controlplane ~ ➜ helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
new-version-1764167838 default 1 2025-11-26 14:37:18.205659093 +0000 UTC deployed webpage-server-02-0.1.1v2
webpage-server-01 default 1 2025-11-26 14:35:32.19008219 +0000 UTC deployed webpage-server-01-0.1.0v1
controlplane ~ ➜ helm uninstall webpage-server-01
release "webpage-server-01" uninstalled
controlplane ~ ➜ helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
new-version-1764167838 default 1 2025-11-26 14:37:18.205659093 +0000 UTC deployed webpage-server-02-0.1.1v2
helm 으로 argocd 설치
helm install
helm upgrade
helm get values
JayDemy Helm Question
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.
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.yamlvi tls-config.yaml # TLSv1.2 제거k -n secure-space delete cm tls-configk apply -f tls-config.yamlk -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 Secret Question
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 admin1401k run secret-1401 --image busybox --dry-run=client -o yaml > pod.yaml
controlplane ~ ➜ k create configmap app-config -n cm-namespace --from-literal=ENV=production --from-literal=LOG_LEVEL=info
configmap/app-config created
controlplane ~ ➜ k -n cm-namespace get cm
NAME DATA AGE
app-config 2 13s
kube-root-ca.crt 1 58s
controlplane ~ ➜ k -n cm-namespace get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
cm-webapp 1/1 1 1 2m26s
controlplane ~ ➜ k edit -n cm-namespace deployment cm-webapp
...
spec:
containers:
- image: nginx
imagePullPolicy: Always
name: nginx
# ConfigMap 설정 추가
envFrom:
- configMapRef:
name: app-config
...
deployment.apps/cm-webapp edited
ConfigMap 설정을 이용하는 Pod 가 있을때 ConfigMap 정보를 변경하면 Pod 도 재시작 시켜줘야한다.
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=1k set image deployment nginx-deploy nginx=nginx:1.17
Create Deployment (10)
controlplane ~ k create deployment hr-web-app --image=kodekloud/webapp-color --replicas=2
k -n mc-namespace get pod
k -n mc-namespace logs mc-pod -c mc-pod-3 -f
로그 수집 목적 사이드카 컨테이너 추가
Prepium Sidecar Container Question
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.
controlplane ~ ➜ k get nodes
NAME STATUS ROLES AGE VERSION
controlplane Ready control-plane 36m v1.34.0
node01 Ready <none> 35m v1.34.0
controlplane ~ ➜ k taint node node01 env_type=production:NoSchedule
node/node01 tainted
controlplane ~ ➜ k describe node node01 | grep -i taint
Taints: env_type=production:NoSchedule
controlplane ~ ➜ k run dev-redis --image=redis:alpine
pod/dev-redis created
controlplane ~ ➜ vi prod-redis.yaml
controlplane ~ ➜ cat prod-redis.yaml
apiVersion: v1
kind: Pod
metadata:
name: prod-redis
spec:
containers:
- name: prod-redis
image: redis:alpine
tolerations:
- key: "env_type"
operator: "Equal"
value: "production"
effect: "NoSchedule"
controlplane ~ ➜ k apply -f prod-redis.yaml
pod/prod-redis created
controlplane ~ ➜ k get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
dev-redis 1/1 Running 0 3m5s 172.17.0.5 controlplane <none> <none>
np-test-1 1/1 Running 0 11m 172.17.1.8 node01 <none> <none>
prod-redis 1/1 Running 0 7s 172.17.1.9 node01 <none> <none>
pvviewer 1/1 Running 0 23m 172.17.1.3 node01 <none> <none>
Pod 에 nodeSelector (disktype=ssd) 추가하여 특정 Node 에 배포
Prepium Taints and Tolerations Question
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
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:
Investigate why the pods are not being created
Inspect the ResourceQuota to find the total CPU and memory budget
Edit the Deployment so that each pod gets an equal share of the quota (requests must equal limits)
controlplane ~ ➜ k create priorityclass low-priority --value=50000
priorityclass.scheduling.k8s.io/low-priority created
controlplane ~ ➜ k -n low-priority get pod
NAME READY STATUS RESTARTS AGE
lp-pod 1/1 Running 0 41s
controlplane ~ ➜ k -n low-priority edit pod lp-pod
error: pods "lp-pod" is invalid
A copy of your changes has been stored to "/tmp/kubectl-edit-1609190342.yaml"
error: Edit cancelled, no valid changes were saved.
controlplane ~ ✖ k replace -f /tmp/kubectl-edit-1609190342.yaml --force
pod "lp-pod" deleted from low-priority namespace
Error from server (Forbidden): pods "lp-pod" is forbidden: the integer value of priority (0) must not be provided in pod spec; priority admission controller computed 50000 from the given PriorityClass name
# spec.priority 제거
controlplane ~ ✖ vi /tmp/kubectl-edit-1609190342.yaml
controlplane ~ ➜ k replace -f /tmp/kubectl-edit-1609190342.yaml --force
pod/lp-pod replaced
PriorityClass 만들어서 Pod 에 연결
Prepium PriorityClass Question
A Deployment named busybox-logger exists in the priority namespace.
An existing user-defined PriorityClass user-existing has value 10000.
Task:
Create a new PriorityClass named high-priority with a value one less than the highest existing user-defined value (i.e. 9999)
Patch the busybox-logger Deployment to use high-priority
controlplane ~ ➜ vi analytics-vpa.yamlcontrolplane ~ ➜ cat analytics-vpa.yamlapiVersion: autoscaling.k8s.io/v1kind: VerticalPodAutoscalermetadata: name: analytics-vpa namespace: defaultspec: targetRef: apiVersion: apps/v1 kind: Deployment name: analytics-deployment updatePolicy: updateMode: Autocontrolplane ~ ➜ k apply -f analytics-vpa.yamlverticalpodautoscaler.autoscaling.k8s.io/analytics-vpa createdcontrolplane ~ ➜ k get vpaNAME MODE CPU MEM PROVIDED AGEanalytics-vpa Auto False 38s
Create HPA (10)
controlplane ~ ➜ lsCKA csr.yaml local-sc.yaml logging-deploy.yaml rbac.yaml static.yaml webapp-hpa.yaml webapp-ingress.yamlcontrolplane ~ ➜ vi webapp-hpa.yamlcontrolplane ~ ➜ cat webapp-hpa.yamlapiVersion: autoscaling/v2kind: HorizontalPodAutoscalermetadata: name: backend-hpa namespace: backendspec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: backend-deployment minReplicas: 3 maxReplicas: 15 metrics: - type: Resource resource: name: memory target: type: Utilization averageUtilization: 65controlplane ~ ➜ k apply -f webapp-hpa.yamlhorizontalpodautoscaler.autoscaling/backend-hpa unchangedcontrolplane ~ ➜ k -n backend describe hpaName: backend-hpaNamespace: backendLabels: <none>Annotations: <none>CreationTimestamp: Sat, 08 Nov 2025 11:57:11 +0000Reference: Deployment/backend-deploymentMetrics: ( current / target ) resource memory on pods (as a percentage of request): <unknown> / 65%Min replicas: 3Max replicas: 15Deployment pods: 3 current / 0 desiredConditions: Type Status Reason Message ---- ------ ------ ------- AbleToScale True SucceededGetScale the HPA controller was able to get the target's current scale ScalingActive False FailedGetResourceMetric the HPA was unable to compute the replica count: failed to get memory utilization: unable to get metrics for resource memory: unable to fetch metrics from resource metrics API: the server could not find the requested resource (get pods.metrics.k8s.io)Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedGetResourceMetric 4s (x6 over 79s) horizontal-pod-autoscaler failed to get memory utilization: unable to get metrics for resource memory: unable to fetch metrics from resource metrics API: the server could not find the requested resource (get pods.metrics.k8s.io) Warning FailedComputeMetricsReplicas 4s (x6 over 79s) horizontal-pod-autoscaler invalid metrics (1 invalid out of 1), first error is: failed to get memory resource metric value: failed to get memory utilization: unable to get metrics for resource memory: unable to fetch metrics from resource metrics API: the server could not find the requested resource (get pods.metrics.k8s.io)
Create HPA (6)
controlplane ~ ➜ vi hpa.yaml
controlplane ~ ➜ cat 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: "1000"
controlplane ~ ➜ k apply -f hpa.yaml
horizontalpodautoscaler.autoscaling/api-hpa created
controlplane ~ ➜ k describe hpa -n api
Name: api-hpa
Namespace: api
Labels: <none>
Annotations: <none>
CreationTimestamp: Wed, 26 Nov 2025 14:29:06 +0000
Reference: Deployment/api-deployment
Metrics: ( current / target )
"requests_per_second" on pods: <unknown> / 1k
Min replicas: 1
Max replicas: 20
Deployment pods: 0 current / 0 desired
Events: <none>
존재하는 deployment 에 알맞은 hpa 생성
CPU max/min 설정, Stabilization window 구현
보통 docs 복붙 후 필드 수정으로 간단함
JayDemy HPA Question
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.
controlplane ~ k get podsNAME READY STATUS RESTARTS AGEmessaging 1/1 Running 0 4m2scontrolplane ~ k expose pod messaging --port=6379 --name=messaging-serviceservice/messaging-service exposedcontrolplane ~ k get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEkubernetes ClusterIP 172.20.0.1 <none> 443/TCP 48mmessaging-service ClusterIP 172.20.26.32 <none> 6379/TCP 6s
controlplane ~ ➜ k run nginx-resolver --image=nginxpod/nginx-resolver createdcontrolplane ~ ➜ k expose pod nginx-resolver --name=nginx-resolver-svc --port=80 --target-port=80 --type=ClusterIPservice/nginx-resolver-svc exposedcontrolplane ~ ➜ k get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEkubernetes ClusterIP 172.20.0.1 <none> 443/TCP 51mnginx-resolver-svc ClusterIP 172.20.137.21 <none> 80/TCP 4scontrolplane ~ ➜ k describe svc nginx-resolver-svcName: nginx-resolver-svcNamespace: defaultLabels: run=nginx-resolverAnnotations: <none>Selector: run=nginx-resolverType: ClusterIPIP Family Policy: SingleStackIP Families: IPv4IP: 172.20.137.21IPs: 172.20.137.21Port: <unset> 80/TCPTargetPort: 80/TCPEndpoints: 172.17.1.15:80Session Affinity: NoneInternal Traffic Policy: ClusterEvents: <none>controlplane ~ ➜ k get pod -o wideNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESnginx-deploy-5846bc77f5-2szm4 1/1 Running 0 20m 172.17.1.14 node01 <none> <none>nginx-resolver 1/1 Running 0 85s 172.17.1.15 node01 <none> <none>controlplane ~ ➜ k run test-nslookup --image=busybox:1.28 --rm -it --restart=Never -- nslookup nginx-resolver-svc > /root/CKA/nginx.svccontrolplane ~ ➜ cat /root/CKA/nginx.svcAddress 1: 172.20.0.10 kube-dns.kube-system.svc.cluster.localName: nginx-resolver-svcAddress 1: 172.20.137.21 nginx-resolver-svc.default.svc.cluster.localpod "test-nslookup" deleted from default namespacecontrolplane ~ ➜ k run test-nslookup --image=busybox:1.28 --rm -it --restart=Never -- nslookup 172-17-1-15.default.pod > /root/CKA/nginx.podcontrolplane ~ ➜ cat /root/CKA/nginx.podAddress 1: 172.20.0.10 kube-dns.kube-system.svc.cluster.localName: 172-17-1-15.default.podAddress 1: 172.17.1.15 172-17-1-15.nginx-resolver-svc.default.svc.cluster.localpod "test-nslookup" deleted from default namespace
이미 배포된 Deployment 의 ContainerPort 를 NodePort 로 expose
Pod (port 80) 생성 후 NodePort 타입의 Service 생성
JayDemy Service Question
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 Service Question
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
controlplane ~ ➜ k get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 172.20.0.1 <none> 443/TCP 71m
np-test-service ClusterIP 172.20.59.134 <none> 80/TCP 42m
web-service ClusterIP 172.20.154.119 <none> 80/TCP 4m34s
web-service-v2 ClusterIP 172.20.191.4 <none> 80/TCP 4m34s
controlplane ~ ➜ k get gateway
NAME CLASS ADDRESS PROGRAMMED AGE
web-gateway nginx True 4m38s
controlplane ~ ➜ vi hr.yaml
controlplane ~ ➜ cat hr.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web-route
spec:
parentRefs:
- name: web-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: web-service
port: 80
weight: 80
- name: web-service-v2
port: 80
weight: 20
controlplane ~ ➜ k apply -f hr.yaml
httproute.gateway.networking.k8s.io/web-route created
Ingress 설정을 GatewayAPI, HTTPRoute(TLS) 로 마이그레이션
TLS 연동 Gateway yaml
Prepium Gateway API Question
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:
controlplane ~ ➜ lsCKA kodekloud.crt local-sc.yaml net-pol-1.yaml net-pol-3.yaml static.yaml webapp-ingress.yamlcsr.yaml kodekloud.key logging-deploy.yaml net-pol-2.yaml rbac.yaml webapp-hpa.yaml web-gateway.yamlcontrolplane ~ ➜ cat net-pol-1.yamlapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: net-policy-1 namespace: backendspec: podSelector: {} ingress: - from: - namespaceSelector: matchLabels: access: allowed ports: - protocol: TCP port: 80controlplane ~ ➜ cat net-pol-2.yamlapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: net-policy-2 namespace: backendspec: podSelector: {} ingress: - from: - namespaceSelector: matchLabels: name: frontend - namespaceSelector: matchLabels: name: databases ports: - protocol: TCP port: 80controlplane ~ ➜ cat net-pol-3.yamlapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: net-policy-3 namespace: backendspec: podSelector: {} ingress: - from: - namespaceSelector: matchLabels: name: frontend ports: - protocol: TCP port: 80controlplane ~ ➜ k get ns --show-labelsNAME STATUS AGE LABELSatlanta-page-04 Active 75m kubernetes.io/metadata.name=atlanta-page-04backend Active 18m kubernetes.io/metadata.name=backend,name=backendcka5673 Active 7m30s kubernetes.io/metadata.name=cka5673default Active 81m kubernetes.io/metadata.name=defaultdevelopment Active 51m kubernetes.io/metadata.name=developmentdigi-locker-02 Active 75m kubernetes.io/metadata.name=digi-locker-02frontend Active 93s kubernetes.io/metadata.name=frontend,name=frontendingress-nginx Active 66m app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx,kubernetes.io/metadata.name=ingress-nginxingress-ns Active 66m kubernetes.io/metadata.name=ingress-nskube-node-lease Active 81m kubernetes.io/metadata.name=kube-node-leasekube-public Active 81m kubernetes.io/metadata.name=kube-publickube-system Active 81m kubernetes.io/metadata.name=kube-systemlogging-ns Active 72m kubernetes.io/metadata.name=logging-nsnginx-gateway Active 75m kubernetes.io/metadata.name=nginx-gatewaysecurity-alpha-01 Active 75m kubernetes.io/metadata.name=security-alpha-01web-dashboard-03 Active 75m kubernetes.io/metadata.name=web-dashboard-03controlplane ~ ➜ k apply -f net-pol-3.yamlnetworkpolicy.networking.k8s.io/net-policy-3 created
Create NetworkPolicy (8)
controlplane ~ ➜ k get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
np-test-1 1/1 Running 0 4m27s run=np-test-1
pvviewer 1/1 Running 0 16m <none>
controlplane ~ ➜ cat 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
controlplane ~ ➜ k apply -f np.yaml
networkpolicy.networking.k8s.io/ingress-to-nptest created
NetworkPolicy 를 생성해서 특정 namespace 의 Pod 만 특정 경로로 연결
Prepium NetworkPolicy Question
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.
controlplane ~ ➜ k get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
app-pv 1Gi RWO Retain Available <unset> 40s
controlplane ~ ➜ k get pvc -n storage-ns
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
app-pvc Pending <unset> 59s
controlplane ~ ➜ k get pvc -n storage-ns -o yaml
apiVersion: v1
items:
- apiVersion: v1
kind: PersistentVolumeClaim
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"PersistentVolumeClaim","metadata":{"annotations":{},"name":"app-pvc","namespace":"storage-ns"},"spec":{"accessModes":["ReadWriteMany"],"resources":{"requests":{"storage":"1Gi"}}}}
creationTimestamp: "2025-11-26T14:04:47Z"
finalizers:
- kubernetes.io/pvc-protection
name: app-pvc
namespace: storage-ns
resourceVersion: "5401"
uid: e0092a4c-9d4d-47ad-b091-f23c1a8dfae4
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
volumeMode: Filesystem
status:
phase: Pending
kind: List
metadata:
resourceVersion: ""
# accessModes 수정
controlplane ~ ➜ k edit pvc -n storage-ns app-pvc
error: persistentvolumeclaims "app-pvc" is invalid
A copy of your changes has been stored to "/tmp/kubectl-edit-1829119349.yaml"
error: Edit cancelled, no valid changes were saved.
controlplane ~ ✖ k replace -f /tmp/kubectl-edit-1829119349.yaml --force
persistentvolumeclaim "app-pvc" deleted from storage-ns namespace
persistentvolumeclaim/app-pvc replaced
controlplane ~ ➜ k get pvc -n storage-ns
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
app-pvc Bound app-pv 1Gi RWO <unset> 7s
PVC 생성 (PV 와 SC 는 주어짐) + kubectl edit 또는 kubectl patch 로 용량 변경
PV - PVC - Pod 마운트
volume types, access modes and reclaim policies
StorageClass 는 보통 docs 복붙 후 필드 수정으로 간단함
Prepium StorageClass Question
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.yamlapiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: local-storageprovisioner: rancher.io/local-pathvolumeBindingMode: WaitForFirstConsumer
k apply -f local-storage.yamlk 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 PVC Question
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.
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 alphak get pvc alpha-claim -o yaml > pvc.yaml
# vi pvc.yamlapiVersion: v1kind: PersistentVolumeClaimmetadata: name: mysql-alpha-pvc namespace: alphaspec: accessModes: - ReadWriteOnce # 수정 ReadWriteMany -> ReadWriteOnce resources: requests: storage: 1Gi # 수정 2Gi -> 1Gi storageClassName: slow # 수정 slow-storage -> slow
k delete pvc alpha-claimk apply -f pvc.yamlk edit deployments.apps alpha-mysql # persistentVolumeClaim.claimName 수정
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 NAMESPACEdeploy0 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
Pod 로그 확인하여 특정 단어가 들어간 log grep 해서 파일로 저장
Taint 가 없는 노드 개수 파일로 저장
노드 Ready 개수 파일로 저장
사용률이 가장 높은 파드를 특정 label 로 조회해서 파일로 저장
explain 명령어로 특정 속성값 정보 파일에 저장하기
Troubleshooting
Node Troubleshooting
# 클러스터에서 NotReady 상태의 노드 확인# 원인 파악, 해당 노드를 Ready 상태로 만들기# 1. containerd 가 작동하고 있어야 함# 2. kubelet 이 작동하고 있어야 함# 3. cni 가 작동하고 있어야 함kubectl get nodesssh hk8s-worker2systemctl status containerdsystemctl status kubelet # 보통 kubelet 이 inactive 상태임systemctl enable --now kubeletsystemctl status kubeletexitkubectl get nodes
Fix controller-manager (10)
controlplane ~ ➜ k get deploy nginx-deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deploy 1/1 1 1 5m36s
controlplane ~ ➜ k scale deployment nginx-deploy --replicas=3
deployment.apps/nginx-deploy scaled
controlplane ~ ➜ k get deploy nginx-deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deploy 1/3 1 1 6m16s
# deployment-controller 에서 이벤트가 전달되지 않음
controlplane ~ ➜ k describe deploy nginx-deploy | grep -A5 Events
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 8m36s deployment-controller Scaled up replica set nginx-deploy-59874dbc6b from 0 to 1
# controller-manager 가 없음
controlplane ~ ➜ k get pod -n kube-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-587f6db6c5-bkn26 1/1 Running 0 55m
canal-jg7mj 2/2 Running 0 54m
canal-lmhj8 2/2 Running 0 55m
coredns-6678bcd974-jkff2 1/1 Running 0 55m
coredns-6678bcd974-wjcgs 1/1 Running 0 55m
etcd-controlplane 1/1 Running 0 55m
kube-apiserver-controlplane 1/1 Running 0 55m
kube-proxy-mqwb8 1/1 Running 0 55m
kube-proxy-vcc8z 1/1 Running 0 54m
kube-scheduler-controlplane 1/1 Running 0 55m
# manifest 수정
controlplane ~ ➜ vi /etc/kubernetes/manifests/kube-controller-manager.yaml
controlplane ~ ➜ k get pod -n kube-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-587f6db6c5-bkn26 1/1 Running 0 58m
canal-jg7mj 2/2 Running 0 57m
canal-lmhj8 2/2 Running 0 58m
coredns-6678bcd974-jkff2 1/1 Running 0 58m
coredns-6678bcd974-wjcgs 1/1 Running 0 58m
etcd-controlplane 1/1 Running 0 58m
kube-apiserver-controlplane 1/1 Running 0 58m
kube-controller-manager-controlplane 1/1 Running 0 21s
kube-proxy-mqwb8 1/1 Running 0 58m
kube-proxy-vcc8z 1/1 Running 0 57m
kube-scheduler-controlplane 1/1 Running 0 58m
controlplane ~ ➜ k get deploy nginx-deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deploy 3/3 3 3 12m
NotReady 상태 Ready 가 되도록 트러블슈팅
보통 kubelet
JayDemy Troubleshooting Question
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