Certified Kubernetes Administrator (CKA) #27 풀스케일 실기 모의고사: 17개 작업 + 해설
#1 시험 환경에서 #26 시험 팁까지 전 도메인을 모두 다뤘습니다. 이번 글은 실제 CKA 형식에 맞춘 실기 모의고사입니다. 전 도메인을 통합한 17개 작업으로 구성했고, 각 작업별로 배점이 정해져 있습니다.
권장 제한 시간은 실제 시험과 같은 2시간입니다. 합격선은 **66%**이며, 17개 작업의 배점을 합산해 채점합니다. 한 작업에 막히면 표시해 두고 넘어간 뒤 배점 높고 손에 익은 작업부터 점수를 쌓는 운영이 합격선을 넘기는 길입니다.
CKA는 클러스터가 여러 개라 context 전환을 가장 먼저 하는 습관이 오답을 막습니다. 각 작업은 지정된 컨텍스트에서 풀어야 채점되며, 노드 안으로 들어가는 작업이 많으므로 SSH,systemctl,etcdctl 감각이 함께 필요합니다. 각 작업은 먼저 스스로 끝까지 푼 뒤 정답을 펼쳐 보십시오. 정답을 먼저 읽으면 손이 익지 않습니다.
풀이 방법 #
- kubeadm으로 세운 멀티 노드 클러스터에서 푸는 것이 가장 실전에 가깝습니다. 로컬에서 어렵다면 클라우드 VM 두세 대로 control plane 한 대와 워커 한 대를 세워 두십시오. etcd 백업,복구와 노드 트러블슈팅은 단일 노드 minikube로는 감각이 살지 않습니다.
- 작업마다 지정된 컨텍스트를 먼저 전환합니다. 이 시리즈에서 반복한 대로 컨텍스트 오설정은 정답을 써도 0점입니다.
k config use-context <문제에서 지정한 컨텍스트>- SSH로 노드에 들어가는 작업이 있으므로, 시험에서 제시하는 호스트명(
node01등)으로 접속이 되는지 미리 확인합니다. 노드 위에서 root 권한이 필요하면sudo -i로 전환합니다. - 17개를 끝까지 푼 뒤 정답을 펼쳐 한 번에 채점합니다. 중간에 정답을 보면 실제 시험 감각이 흐려집니다. #1에서 잡은
alias k=kubectl과export do="--dry-run=client -o yaml"셋업을 먼저 적용하면 시간을 아낍니다.
도메인 분포 #
실제 CKA의 도메인 비중에 맞춰 17개 작업을 배치했습니다. Troubleshooting이 30%로 가장 크므로 작업 수도 가장 많습니다.
| # | 도메인 | 작업 수 | 작업 번호 |
|---|---|---|---|
| 1 | Cluster Architecture, Installation, Configuration | 5 | 1, 2, 3, 4, 5 |
| 2 | Workloads and Scheduling | 3 | 6, 7, 8 |
| 3 | Services and Networking | 3 | 9, 10, 11 |
| 4 | Storage | 2 | 12, 13 |
| 5 | Troubleshooting | 4 | 14, 15, 16, 17 |
배점은 도메인 비중과 작업 난이도를 반영해 합계 100점으로 두었습니다. 채점 기준은 글 끝에 정리했습니다.
작업 1 (8점): Cluster Architecture, Installation, Configuration #
컨텍스트 cluster1에서 실행 중인 etcd의 스냅샷을 /opt/etcd-backup.db에 저장하세요. control plane 노드에 SSH로 접속해 작업하며, etcd는 static Pod로 떠 있고 인증서는 /etc/kubernetes/pki/etcd 아래에 있습니다.
정답
control plane 노드에 접속해 root로 전환한 뒤 스냅샷을 저장합니다.
ssh cluster1-controlplane
sudo -i
ETCDCTL_API=3 etcdctl snapshot save /opt/etcd-backup.db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key저장이 끝나면 스냅샷 상태를 확인합니다.
ETCDCTL_API=3 etcdctl --write-out=table snapshot status /opt/etcd-backup.db해설: snapshot save는 클라이언트 인증서로 etcd에 접속하므로 --cacert,--cert,--key 세 가지가 모두 필요하며, 경로는 etcd static Pod 매니페스트(/etc/kubernetes/manifests/etcd.yaml)의 --cert-file,--key-file,--trusted-ca-file 값에서 확인할 수 있습니다. ETCDCTL_API=3을 빼면 v2 API로 동작해 명령이 실패하는 것이 가장 흔한 함정입니다.
작업 2 (8점): Cluster Architecture, Installation, Configuration #
작업 1에서 만든 스냅샷 /opt/etcd-backup.db를 새 데이터 디렉터리 /var/lib/etcd-restore로 복구하고, etcd static Pod가 이 디렉터리를 쓰도록 매니페스트를 수정하세요. 복구 후 클러스터가 정상화되는지 확인합니다.
정답
스냅샷을 새 디렉터리로 복원합니다.
ETCDCTL_API=3 etcdctl snapshot restore /opt/etcd-backup.db \
--data-dir=/var/lib/etcd-restoreetcd static Pod 매니페스트에서 host 경로를 새 디렉터리로 바꿉니다.
vim /etc/kubernetes/manifests/etcd.yaml volumes:
- name: etcd-data
hostPath:
path: /var/lib/etcd-restore
type: DirectoryOrCreatekubelet이 매니페스트 변경을 감지해 etcd Pod를 다시 띄울 때까지 기다린 뒤 확인합니다.
crictl ps | grep etcd
k get nodes해설: snapshot restore는 새 데이터 디렉터리를 만들 뿐 etcd를 재시작하지 않습니다. 실제 전환은 static Pod 매니페스트의 hostPath.path를 새 디렉터리로 바꿔, kubelet이 etcd Pod를 새 데이터로 재생성하게 하는 단계에서 일어납니다. --data-dir의 부모 경로 권한과 기존 etcd 컨테이너가 잠시 내려가는 점을 주의합니다.
작업 3 (8점): Cluster Architecture, Installation, Configuration #
컨텍스트 cluster1의 control plane과 워커 노드를 v1.31.0에서 v1.31.1로 업그레이드하세요. 먼저 control plane을 업그레이드한 뒤 워커 node01을 업그레이드합니다. 워커 업그레이드 중에는 워크로드를 비워야 합니다.
정답
control plane 노드에서 kubeadm을 올린 뒤 업그레이드 계획을 확인하고 적용합니다.
ssh cluster1-controlplane
sudo -i
apt-get update && apt-get install -y kubeadm=1.31.1-1.1
kubeadm upgrade plan
kubeadm upgrade apply v1.31.1control plane의 kubelet과 kubectl을 올린 뒤 drain,uncordon으로 재기동합니다.
kubectl drain cluster1-controlplane --ignore-daemonsets
apt-get install -y kubelet=1.31.1-1.1 kubectl=1.31.1-1.1
systemctl daemon-reload && systemctl restart kubelet
kubectl uncordon cluster1-controlplane워커는 control plane에서 drain한 뒤 노드 위에서 업그레이드합니다.
kubectl drain node01 --ignore-daemonsets
ssh node01
sudo -i
apt-get update && apt-get install -y kubeadm=1.31.1-1.1
kubeadm upgrade node
apt-get install -y kubelet=1.31.1-1.1
systemctl daemon-reload && systemctl restart kubelet
exit
kubectl uncordon node01해설: control plane은 kubeadm upgrade apply, 워커는 kubeadm upgrade node를 쓰는 것이 핵심 차이입니다. 업그레이드 순서는 kubeadm 설치 → upgrade → kubelet,kubectl 설치 → kubelet 재시작이며, --ignore-daemonsets 없이 drain하면 DaemonSet Pod 때문에 막힙니다. 버전 점프는 한 마이너씩만 허용됩니다.
작업 4 (8점): Cluster Architecture, Installation, Configuration #
네임스페이스 dev에서만 동작하는 ServiceAccount deployer를 만들고, 이 ServiceAccount가 dev 안에서 Deployment를 생성,조회,수정,삭제할 수 있도록 RBAC을 구성하세요. 권한이 올바른지 kubectl auth can-i로 검증합니다.
정답
ServiceAccount와 Role, RoleBinding을 만듭니다.
k -n dev create serviceaccount deployer
k -n dev create role deploy-manager \
--verb=create,get,list,update,delete \
--resource=deployments.apps
k -n dev create rolebinding deployer-binding \
--role=deploy-manager \
--serviceaccount=dev:deployer권한을 검증합니다.
k -n dev auth can-i create deployments --as=system:serviceaccount:dev:deployer
k -n dev auth can-i delete deployments --as=system:serviceaccount:dev:deployer해설: Role은 네임스페이스 한정 권한이고, RoleBinding이 그 Role을 주체(여기서는 ServiceAccount)에 연결합니다. --resource=deployments.apps처럼 API 그룹까지 명시하지 않으면 core 그룹으로 잡혀 빈 규칙이 될 수 있습니다. 검증 시 주체는 system:serviceaccount:<ns>:<name> 형식으로 적어야 하며, 두 명령 모두 yes가 나와야 합니다.
작업 5 (6점): Cluster Architecture, Installation, Configuration #
/etc/kubernetes/pki/apiserver.crt 인증서의 만료일을 확인하고, kubeadm으로 클러스터 인증서를 모두 갱신하세요. 갱신 후 만료일이 미래로 바뀌었는지 다시 확인합니다.
정답
control plane 노드에서 인증서 만료 현황을 확인합니다.
ssh cluster1-controlplane
sudo -i
kubeadm certs check-expiration특정 인증서의 만료일을 직접 보고 싶으면 openssl로 확인합니다.
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -enddate모든 인증서를 갱신한 뒤 control plane static Pod를 재기동합니다.
kubeadm certs renew all
systemctl restart kubelet해설: kubeadm certs check-expiration은 각 인증서와 kubeconfig의 만료일을 표로 보여 줍니다. certs renew all은 인증서 파일만 새로 발급하므로, apiserver 등 static Pod가 새 인증서를 읽도록 컴포넌트를 재기동해야 실제로 반영됩니다. kubeadm은 control plane 업그레이드 때 인증서를 자동 갱신하므로, 정기 업그레이드가 곧 갱신을 겸하기도 합니다.
작업 6 (6점): Workloads and Scheduling #
네임스페이스 apps에 fluentd 로그 수집기를 모든 노드에 정확히 하나씩 띄우는 DaemonSet log-agent를 만드세요. 이미지는 fluent/fluentd:v1.16을 쓰며, control plane 노드의 taint 때문에 거기에는 뜨지 않아도 됩니다.
정답
dry-run으로 뼈대를 만들 수 없는 kind이므로 매니페스트를 직접 작성합니다.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-agent
namespace: apps
spec:
selector:
matchLabels:
app: log-agent
template:
metadata:
labels:
app: log-agent
spec:
containers:
- name: fluentd
image: fluent/fluentd:v1.16k apply -f log-agent.yaml
k -n apps get ds log-agent해설: DaemonSet은 워커 노드마다 Pod를 하나씩 배치하며 replicas 필드가 없습니다. control plane 노드에는 보통 node-role.kubernetes.io/control-plane:NoSchedule taint가 있어 별도 toleration 없이는 뜨지 않으므로, “거기에는 뜨지 않아도 된다"는 요구는 toleration을 추가하지 않는 것으로 충족됩니다. Deployment와 달리 selector와 template label이 일치해야 합니다.
작업 7 (7점): Workloads and Scheduling #
워커 노드 node01에 gpu=true:NoSchedule taint를 걸고, 네임스페이스 apps에 이 taint를 견디면서 disktype=ssd 라벨이 있는 노드에만 스케줄되는 Pod ml-job(이미지 nginx)을 만드세요.
정답
노드에 taint를 걸고 라벨이 있는지 확인합니다.
k taint node node01 gpu=true:NoSchedule
k label node node01 disktype=ssdtoleration과 nodeAffinity를 가진 Pod를 만듭니다.
apiVersion: v1
kind: Pod
metadata:
name: ml-job
namespace: apps
spec:
tolerations:
- key: gpu
operator: Equal
value: "true"
effect: NoSchedule
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
containers:
- name: ml-job
image: nginxk apply -f ml-job.yaml
k -n apps get pod ml-job -o wide해설: taint는 노드가 Pod를 밀어내는 장치이고, toleration은 Pod가 그 taint를 견디겠다는 선언입니다. toleration만 있으면 다른 노드에도 뜰 수 있으므로, 특정 노드로 고정하려면 nodeAffinity나 nodeSelector를 함께 써야 합니다. toleration의 value는 따옴표로 문자열로 두어야 "true" 불리언 해석 오류를 피합니다.
작업 8 (7점): Workloads and Scheduling #
네임스페이스 data에 StatefulSet cache를 만드세요. 이미지는 redis:7, replicas는 3이며, headless Service cache로 Pod에 안정적인 DNS 이름을 부여하세요. 각 Pod는 volumeClaimTemplates로 1Gi PVC를 갖습니다.
정답
headless Service와 StatefulSet을 함께 정의합니다.
apiVersion: v1
kind: Service
metadata:
name: cache
namespace: data
spec:
clusterIP: None
selector:
app: cache
ports:
- port: 6379
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: cache
namespace: data
spec:
serviceName: cache
replicas: 3
selector:
matchLabels:
app: cache
template:
metadata:
labels:
app: cache
spec:
containers:
- name: redis
image: redis:7
ports:
- containerPort: 6379
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gik apply -f cache.yaml
k -n data get pods -l app=cache해설: StatefulSet은 serviceName으로 headless Service(clusterIP: None)와 묶여야 cache-0.cache.data.svc.cluster.local 형태의 안정적 DNS를 부여합니다. volumeClaimTemplates는 Pod마다 별도 PVC를 자동 생성하며, Pod가 재생성돼도 같은 PVC에 다시 붙습니다. Pod 이름이 0부터 순서대로 붙는 것도 Deployment와의 차이입니다.
작업 9 (6점): Services and Networking #
네임스페이스 net에 nginx 이미지의 Deployment web을 replicas 2로 만들고, 클러스터 외부에서 노드의 30080 포트로 접근 가능한 NodePort Service web-np로 노출하세요. Service의 포트는 80, 타깃 포트는 80입니다.
정답
k -n net create deploy web --image=nginx --replicas=2
k -n net expose deploy web --name=web-np --type=NodePort --port=80 --target-port=80nodePort 값을 30080으로 고정합니다.
k -n net patch svc web-np --type='json' \
-p='[{"op":"replace","path":"/spec/ports/0/nodePort","value":30080}]'해설: expose로 NodePort를 만들면 nodePort는 30000〜32767 범위에서 자동 할당되므로, 특정 값으로 고정하려면 patch하거나 dry-run YAML에 nodePort: 30080을 직접 적어 apply합니다. 검증은 curl <노드IP>:30080으로 응답을 확인합니다.
작업 10 (6점): Services and Networking #
네임스페이스 net에 Ingress web-ing를 만드세요. 호스트 web.example.com의 경로 /로 들어오는 트래픽을 작업 9의 Service web-np의 80 포트로 라우팅하며 pathType은 Prefix, ingressClassName은 nginx를 사용하세요.
정답
k -n net create ingress web-ing \
--class=nginx \
--rule="web.example.com/*=web-np:80" $do > ing.yaml생성된 매니페스트는 다음과 같습니다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ing
namespace: net
spec:
ingressClassName: nginx
rules:
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-np
port:
number: 80k apply -f ing.yaml해설: create ingress의 --rule은 host/path=service:port 형식이며, 경로 끝의 /*가 pathType: Prefix로 변환됩니다. --class=nginx를 빼면 ingressClassName이 비어 기본 IngressClass가 없는 클러스터에서 라우팅이 안 됩니다. 실제 동작에는 ingress-nginx 컨트롤러가 필요하지만 채점은 리소스 정의의 정확성을 봅니다.
작업 11 (6점): Services and Networking #
네임스페이스 net에 NetworkPolicy db-allow를 만드세요. 라벨 app=db를 가진 Pod로의 ingress 트래픽을, 같은 네임스페이스에서 라벨 role=api를 가진 Pod가 TCP 5432 포트로 접근할 때만 허용하고 그 외 ingress는 모두 차단하세요.
정답
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-allow
namespace: net
spec:
podSelector:
matchLabels:
app: db
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: api
ports:
- protocol: TCP
port: 5432k apply -f db-allow.yaml해설: podSelector는 정책이 적용될 대상 Pod(app=db)를 고르고, ingress.from은 허용할 출발지(role=api)를 고릅니다. NetworkPolicy는 한 Pod에 하나라도 적용되면 명시되지 않은 트래픽이 모두 차단되므로 별도 deny-all 없이도 그 외 ingress가 막힙니다. from과 ports를 같은 규칙에 두면 두 조건의 AND가 되며, Calico 같은 정책 지원 CNI에서만 강제됩니다.
작업 12 (8점): Storage #
네임스페이스 storage에 호스트 경로 /mnt/data를 쓰는 2Gi PersistentVolume pv-data(accessMode ReadWriteOnce, reclaimPolicy Retain)를 만들고, 이를 정확히 바인딩하는 1Gi PVC pvc-data를 만든 뒤, 이 PVC를 /usr/share/nginx/html에 마운트하는 Pod pv-user(nginx)를 만드세요.
정답
PV, PVC, Pod를 차례로 정의합니다.
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-data
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /mnt/data
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-data
namespace: storage
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
name: pv-user
namespace: storage
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumes:
- name: data
persistentVolumeClaim:
claimName: pvc-datak apply -f pv.yaml
k -n storage get pvc pvc-data해설: PVC가 PV에 바인딩되려면 accessMode가 같고 PV의 용량이 PVC 요청보다 크거나 같아야 합니다(2Gi ≥ 1Gi). PV는 클러스터 전역 리소스라 namespace가 없고, PVC와 Pod는 같은 네임스페이스에 있어야 합니다. reclaimPolicy: Retain이면 PVC를 지워도 PV의 데이터가 남으며 PV는 Released 상태가 됩니다.
작업 13 (6점): Storage #
네임스페이스 storage에 동적 프로비저닝용 StorageClass fast를 만드세요. provisioner는 rancher.io/local-path, volumeBindingMode는 WaitForFirstConsumer, allowVolumeExpansion은 true입니다. 그다음 이 StorageClass로 3Gi PVC pvc-fast를 만드세요.
정답
StorageClass와 PVC를 정의합니다.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
provisioner: rancher.io/local-path
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-fast
namespace: storage
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast
resources:
requests:
storage: 3Gik apply -f sc.yaml
k -n storage get pvc pvc-fast해설: WaitForFirstConsumer는 PVC를 쓰는 Pod가 스케줄되기 전까지 볼륨 생성을 미루므로, PVC만 만들면 Pending 상태로 머무는 것이 정상입니다. allowVolumeExpansion: true여야 나중에 PVC의 resources.requests.storage를 키워 온라인 확장을 할 수 있습니다. PVC에 storageClassName을 지정하지 않으면 기본 StorageClass가 쓰입니다.
작업 14 (7점): Troubleshooting #
컨텍스트 cluster1의 워커 노드 node01이 NotReady 상태입니다. 원인을 진단하고 노드를 Ready로 되돌리세요.
정답
먼저 노드 상태와 condition을 확인합니다.
k get nodes
k describe node node01노드에 접속해 kubelet 상태와 로그를 봅니다.
ssh node01
sudo -i
systemctl status kubelet
journalctl -u kubelet -ekubelet이 멈춰 있으면 재기동하고, 설정 오류면 로그가 가리키는 파일을 고친 뒤 재기동합니다.
systemctl enable --now kubelet
systemctl restart kubelet해설: NotReady의 가장 흔한 원인은 kubelet 서비스가 죽었거나(systemctl restart kubelet), kubeconfig,CA 경로 오류, 디스크,메모리 pressure입니다. describe node의 Conditions와 journalctl -u kubelet이 원인을 가르며, kubelet이 부팅 시 비활성이면 enable --now로 부팅 자동 시작까지 켜 둡니다. control plane 쪽 문제가 아니라 노드 자체를 봐야 한다는 점이 핵심입니다.
작업 15 (7점): Troubleshooting #
컨텍스트 cluster2의 control plane에서 kubectl이 connection refused로 응답하지 않습니다. apiserver static Pod 매니페스트가 잘못 수정되었습니다. 원인을 찾아 고치고 apiserver를 정상화하세요.
정답
control plane 노드에 접속해 apiserver 컨테이너 상태를 봅니다.
ssh cluster2-controlplane
sudo -i
crictl ps -a | grep kube-apiserver
crictl logs <apiserver-container-id>static Pod 매니페스트를 검토해 잘못된 필드(예: 오타 난 포트, 잘못된 인증서 경로, 깨진 들여쓰기)를 고칩니다.
vim /etc/kubernetes/manifests/kube-apiserver.yamlkubelet이 매니페스트 변경을 감지해 apiserver Pod를 다시 띄울 때까지 기다린 뒤 확인합니다.
crictl ps | grep kube-apiserver
k get nodes해설: apiserver가 떠야 kubectl이 동작하므로 이 상태에서는 kubectl 대신 crictl로 컨테이너 로그를 직접 읽는 것이 핵심입니다. static Pod는 매니페스트 파일을 고치면 kubelet이 자동으로 재생성하므로, 파일을 잠깐 다른 곳으로 옮겼다 되돌리면 강제 재시작 효과가 납니다. --etcd-servers 경로나 인증서 경로 오타가 흔한 원인입니다.
작업 16 (7점): Troubleshooting #
네임스페이스 apps의 Service frontend로 보내는 요청이 응답하지 않습니다. Pod는 모두 Running이지만 Service의 endpoint가 비어 있습니다. 원인을 진단해 트래픽이 Pod에 닿도록 고치세요.
정답
Service와 endpoint, Pod 라벨을 비교합니다.
k -n apps get svc frontend -o wide
k -n apps get endpoints frontend
k -n apps describe svc frontend
k -n apps get pods --show-labelsService의 selector와 Pod의 라벨이 어긋나 있으면 selector를 Pod 라벨에 맞게 고칩니다.
k -n apps patch svc frontend \
-p '{"spec":{"selector":{"app":"frontend"}}}'
k -n apps get endpoints frontend해설: endpoint가 비어 있다는 것은 Service의 selector가 어떤 Pod 라벨과도 일치하지 않는다는 신호입니다. Pod가 Running이어도 selector가 어긋나면 endpoint에 등록되지 않아 트래픽이 닿지 않습니다. selector를 고친 뒤 get endpoints에 Pod IP가 채워지는지로 검증하며, 포트 이름,번호 불일치도 함께 점검합니다.
작업 17 (7점): Troubleshooting #
네임스페이스 apps의 Pod report가 CrashLoopBackOff 상태입니다. 원인을 진단하고, 가장 최근에 종료된 컨테이너의 직전 로그를 파일 /tmp/report.log에 저장한 뒤, 원인이 메모리 부족(OOMKilled)이라면 메모리 limit을 256Mi로 올려 정상화하세요.
정답
상태와 종료 원인을 진단합니다.
k -n apps describe pod report
k -n apps logs report --previous > /tmp/report.logLast State가 OOMKilled이면 메모리 limit을 올립니다. Pod의 resources는 직접 수정할 수 없으므로 매니페스트를 받아 고친 뒤 재생성합니다.
k -n apps get pod report -o yaml > report.yaml resources:
limits:
memory: "256Mi"
requests:
memory: "128Mi"k -n apps delete pod report
k apply -f report.yaml해설: CrashLoopBackOff는 컨테이너가 반복 종료되는 상태라 현재 로그가 비어 있을 수 있으므로 --previous(-p)로 직전 컨테이너 로그를 가져옵니다. describe의 Last State Reason이 OOMKilled이면 메모리 limit 부족이 원인입니다. 실행 중인 Pod의 resources는 불변이라 매니페스트를 고쳐 삭제,재생성해야 하며, Deployment가 관리하는 Pod라면 Deployment 쪽 template을 고칩니다.
채점 기준 #
각 작업의 배점을 합산해 채점합니다. 합계는 100점이며 66점 이상이 합격권입니다.
| 도메인 | 작업,배점 | 소계 |
|---|---|---|
| Cluster Architecture, Installation, Configuration | 1(8) , 2(8) , 3(8) , 4(8) , 5(6) | 38 |
| Workloads and Scheduling | 6(6) , 7(7) , 8(7) | 20 |
| Services and Networking | 9(6) , 10(6) , 11(6) | 18 |
| Storage | 12(8) , 13(6) | 14 |
| Troubleshooting | 14(7) , 15(7) , 16(7) , 17(7) | 28 |
| 합계 | (합계) | 100 |
채점은 실제 시험과 같이 결과물 기준입니다. 명령을 어떻게 쳤는지가 아니라 만들어진 리소스와 복구된 클러스터 상태가 요구사항과 일치하는지를 봅니다. 한 작업 안에서도 라벨,포트,경로,필드 같은 항목별로 부분 점수가 나뉘므로, 막히는 항목이 있어도 채울 수 있는 부분은 끝까지 채우는 편이 점수에 유리합니다.
도메인별 약점 복습 #
채점 후 점수가 낮은 도메인을 아래 표의 해당 글로 돌아가 복습하십시오.
| 도메인 | 관련 작업 | 복습할 글 |
|---|---|---|
| Cluster Architecture, Installation, Configuration | 1, 2, 3, 4, 5 | #6 , #7 , #8 , #9 |
| Workloads and Scheduling | 6, 7, 8 | #11 , #13 , #14 |
| Services and Networking | 9, 10, 11 | #18 , #19 , #20 |
| Storage | 12, 13 | #16 , #17 |
| Troubleshooting | 14, 15, 16, 17 | #22 , #23 , #24 , #25 |
특정 작업에서 시간이 부족했다면 도메인 지식보다 손 빠르기 문제일 수 있습니다. 이 경우 #1 셋업과 #26 시간 관리를 다시 읽고, 같은 17개 작업을 시간을 재며 한 번 더 푸십시오. etcd 백업,복구와 노드 트러블슈팅은 한 번 손에 익으면 작업당 소요 시간이 눈에 띄게 줄어듭니다.
시리즈를 마치며 #
#1의 시험 환경 셋업에서 출발해 control plane,노드,kubeadm 설치,HA,업그레이드,etcd 백업복구,인증서,RBAC,워크로드,스케줄링,리소스,스토리지,Service,Ingress,NetworkPolicy,트러블슈팅 4종까지 CKA 전 도메인을 27편에 걸쳐 모두 정리했습니다. 이 모의고사에서 66점을 넘겼다면 실제 시험장에서도 충분히 합격선을 넘길 수 있는 실기 감각이 갖춰진 것입니다. 축하합니다.
CKA가 클러스터 운영자의 실기였다면, 다음 단계는 그 클러스터를 안전하게 지키는 **CKS(Certified Kubernetes Security Specialist)**이며, 여기서 익힌 etcd,인증서,RBAC,트러블슈팅 감각이 그대로 발판이 되니 합격의 기세를 이어 다음 실기로 넘어가시기를 권합니다.