CKA — Kubernetes Administrator
CKA Mock Exam A
한국어 원문으로 표시합니다.
모의고사입니다
실제 CKA 는 120분에 과제 15~20개를 풀고 66% 를 넘으면 합격합니다. 이 세트도 과제 17개·120분·합격선 66% 로 맞추었습니다. 부분 점수제이므로 전부 맞힐 필요가 없습니다. 17개 중 12개를 통과하면 완료로 처리됩니다.
힌트를 보지 말고 먼저 끝까지 풀어 보십시오. 실제 시험에는 힌트가 없습니다. 막히는 과제는 건너뛰었다가 시간이 남으면 돌아오는 편이 점수에 유리합니다. 힌트와 정답은 시험을 한 번 끝낸 뒤에 복습용으로 쓰십시오.
실제 시험장에서 처음 알면 시간을 잃는 것들
- 시험은 원격 데스크톱이고, 과제마다 지정된 호스트로
ssh해서 작업합니다. 중첩 ssh 는 지원되지 않습니다. 작업이 끝나면exit로 반드시 원래 자리로 돌아오십시오. k별칭과 bash 자동완성이 이미 설정되어 있습니다. 시작하자마자 alias 부터 만들라는 조언은 지금 환경과 맞지 않습니다. 이 실습 파드도 같게 맞추어 두었습니다.yq·curl·wget·man도 이미 깔려 있습니다.- 터미널 복사는
Ctrl+Shift+C, 붙여넣기는Ctrl+Shift+V입니다. Ctrl+W는 브라우저 탭을 닫아 버립니다. 단어를 지울 때는Ctrl+Alt+W를 쓰십시오.- INSERT 키가 막혀 있어 vim 은
i로 입력 모드에 들어가야 합니다. - 문항마다 배점이 다르고, 정답에 이르는 경로는 여러 가지가 허용됩니다.
도메인 배분
| 도메인 | 실제 배점 | 이 세트의 과제 |
|---|---|---|
| Cluster Architecture, Installation and Configuration | 25% | 1~4번 |
| Workloads and Scheduling | 15% | 5~7번 |
| Services and Networking | 20% | 8~10번 |
| Storage | 10% | 11~12번 |
| Troubleshooting | 30% | 13~17번 |
13번부터 17번까지
실제 시험에서는 고장 난 자원이 미리 준비되어 있습니다. 이 환경에는 그 장치가 없으므로 각 과제의 지시문에 문제 매니페스트를 넣어 두었습니다. 먼저 그대로 적용한 다음 증상을 진단하고 고치십시오. 적용하지 않고 처음부터 옳게 만들어도 채점은 통과하지만, 그러면 진단 연습이 되지 않습니다.
이 환경에서 다른 점
파드 안의 클러스터는 kwokctl 이 띄운 1인용 클러스터입니다. 컨트롤 플레인은 진짜라서
매니페스트·RBAC·스케줄링·쿼터·CRD·PV 바인딩·드레인은 전부 실제로 동작합니다.
다만 워크로드 컨테이너는 실행되지 않으므로 kubectl exec·logs·port-forward 는
쓸 수 없습니다. 과제도 그것을 요구하지 않습니다.
시작 전에 kubectl get nodes 로 노드 3개가 Ready 인지 확인하십시오. 아직이라면
클러스터가 뜨는 중입니다(2분쯤 걸립니다).
채점
각 과제의 확인 버튼을 누르면 채점기가 살아 있는 클러스터에서 값을 다시 계산해 대조합니다. 파일에 무엇을 적었는지가 아니라 클러스터가 어떤 상태인지를 봅니다. 정답에 이르는 경로는 여러 가지가 허용됩니다.
ops 네임스페이스의 배포 권한
네임스페이스 ops 를 만들고 서비스어카운트 deployer 를 두십시오.
Role deployer 는 apps 그룹의 deployments 에 get·list·watch·create·update 를, core 그룹의 pods 에 get·list 를 허용합니다. 그 밖의 권한은 없어야 합니다. RoleBinding deployer 로 그 서비스어카운트에 묶으십시오.
core 그룹은 apiGroups 에 빈 문자열로 적습니다. 권한이 넓어지는 실수는 허용된 것만 확인해서는 드러나지 않으므로, kubectl auth can-i ... --as=system:serviceaccount:ops:deployer 로 되면 안 되는 동사도 함께 물어보십시오.
집계 ClusterRole
ClusterRole monitoring-view 를 만드십시오. 규칙을 직접 적지 말고, 라벨 rbac.example.com/aggregate-to-monitoring: "true" 가 붙은 ClusterRole 을 모으도록 aggregationRule 을 씁니다.
그 라벨이 붙은 ClusterRole monitoring-metrics 는 core 그룹의 pods 와 nodes 에 get·list·watch 를 허용합니다.
집계는 컨트롤러가 합니다. monitoring-view 의 rules 를 비워 두고 만들면 잠시 뒤 규칙이 채워집니다. 채워지지 않는다면 셀렉터의 라벨과 실제 라벨이 한 글자라도 다른 것입니다.
Backup CRD 와 커스텀 리소스
CRD backups.ops.example.com 을 만드십시오. 그룹은 ops.example.com, 버전은 v1(served·storage), 스코프는 Namespaced, kind 는 Backup, 복수형은 backups, 짧은 이름은 bk 입니다. 스키마에서 spec.schedule 은 필수 문자열이고 spec.retention 은 정수입니다.
이어서 네임스페이스 ops 에 Backup nightly 를 만들고 schedule 을 0 2 * * *, retention 을 7 로 두십시오.
CRD 가 등록된 직후에는 아직 서빙되지 않습니다. kubectl wait --for=condition=Established crd/<이름> 으로 기다린 뒤 커스텀 리소스를 만드십시오. 스키마에 타입을 적지 않으면 아무 값이나 들어갑니다.
team-a 쿼터와 기본 자원값
네임스페이스 team-a 에 ResourceQuota team-a-quota 를 두십시오. 한도는 pods 10, requests.cpu 2, requests.memory 4Gi, limits.cpu 4, limits.memory 8Gi 입니다.
같은 네임스페이스에 LimitRange team-a-limits 를 두어, 자원을 적지 않은 컨테이너에 limits cpu 500m·memory 512Mi 와 requests cpu 100m·memory 128Mi 가 채워지게 하십시오.
LimitRange 의 default 는 limits 를, defaultRequest 는 requests 를 채웁니다. 실제로 채워지는지 보려면 kubectl run ... --dry-run=server -o yaml 로 서버에게 물어보면 됩니다.
frontend 디플로이먼트와 롤링 전략
네임스페이스 web 에 Deployment frontend 를 만드십시오. 이미지는 nginx:1.27, 복제본은 4개, 파드 라벨은 app=frontend 와 tier=web 입니다.
전략은 RollingUpdate 이고 maxSurge 1·maxUnavailable 0 이며, 리비전 히스토리는 3개만 남깁니다.
maxUnavailable 0 은 '갱신 중에도 준비된 파드 수를 절대 줄이지 않는다' 는 뜻입니다. 숫자로 적을 수도 있고 백분율로 적을 수도 있는데, 채점기는 숫자 1 과 0 을 기대합니다.
배치 전용 노드와 톨러레이션
노드 lab-node-batch 를 새로 만드십시오. kwok 이 관리하도록 어노테이션 kwok.x-k8s.io/node: fake 를 붙이고, 라벨 workload=batch 와 테인트 workload=batch:NoSchedule 을 겁니다.
이어서 네임스페이스 batch 에 Deployment cruncher 를 만들어 busybox:1.36 을 복제본 3개로 돌리되, 세 파드가 모두 그 노드에만 뜨게 하십시오.
톨러레이션은 '갈 수 있다' 만 말합니다. '거기로 가라' 는 nodeSelector 나 required nodeAffinity 가 말합니다. 둘 다 있어야 배치가 정해집니다.
모든 노드에 뜨는 에이전트
네임스페이스 web 에 DaemonSet node-agent 를 만드십시오. 이미지는 busybox:1.36 이고, 테인트가 걸린 노드를 포함해 클러스터의 모든 노드에서 떠야 합니다.
DaemonSet 도 스케줄러를 거칩니다. 테인트가 걸린 노드에 뜨려면 그 테인트를 견디는 톨러레이션이 필요합니다. 어떤 테인트든 견디게 하려면 키를 적지 않고 operator 만 Exists 로 두면 됩니다.
이름 붙은 포트를 쓰는 api 서비스
네임스페이스 web 에 Deployment api 를 만드십시오. 이미지는 nginx:1.27, 복제본 2개, 컨테이너 포트는 8080 이고 그 포트의 이름은 http 입니다.
Service api 는 ClusterIP 로 80 번을 받아, 숫자가 아니라 포트 이름 http 로 넘겨야 합니다.
Service 는 셀렉터가 틀려도 멀쩡히 만들어지고 엔드포인트만 비어 있습니다. kubectl get endpointslice -n web -l kubernetes.io/service-name=api 로 주소가 붙었는지 확인하십시오.
shop.example.com 인그레스
IngressClass nginx 를 만드십시오. controller 는 k8s.io/ingress-nginx 입니다.
네임스페이스 web 에 frontend 디플로이먼트를 가리키는 Service frontend(80 번)를 만들고, Ingress shop 으로 호스트 shop.example.com 의 / 는 frontend:80 으로, /api 는 api:80 으로 보내십시오. 두 경로 모두 pathType 은 Prefix 입니다.
Ingress 는 백엔드 서비스가 없어도 만들어집니다. 서비스 이름을 잘못 적어도 오류가 나지 않으니 직접 확인하십시오. v1 에서 백엔드는 service.name 과 service.port.number 로 적습니다.
web 네임스페이스 네트워크 정책
네임스페이스 web 의 모든 파드에 대해 들어오는 트래픽을 기본으로 막는 NetworkPolicy default-deny-ingress 를 만드십시오.
이어서 NetworkPolicy api-allow 로 app=api 파드에 한해, 같은 네임스페이스의 app=frontend 파드와 네임스페이스 monitoring 에서 오는 TCP 8080 만 허용하십시오.
전면 차단은 podSelector 를 빈 객체로 두고 ingress 규칙을 하나도 적지 않는 것입니다. from 목록의 항목을 나누어 적으면 OR 이고, 한 항목 안에 podSelector 와 namespaceSelector 를 함께 적으면 AND 입니다. 이 문제는 OR 입니다.
정적 볼륨 바인딩
StorageClass local-fast 를 만드십시오. provisioner 는 kubernetes.io/no-provisioner, reclaimPolicy 는 Retain, volumeBindingMode 는 Immediate 입니다.
PV pv-fast-1 은 2Gi·ReadWriteOnce·local-fast·hostPath /mnt/fast1 입니다. 네임스페이스 storage 의 PVC data 는 1Gi·ReadWriteOnce·local-fast 로 만들어 그 PV 에 묶이게 하십시오.
PVC 가 Pending 에서 안 벗어나면 클래스 이름·접근 모드·용량 셋 중 하나가 PV 와 안 맞는 것입니다. 요청 용량은 PV 용량보다 작아도 되지만 접근 모드는 정확히 포함되어야 합니다.
PVC 와 임시 볼륨을 함께 쓰는 워크로드
네임스페이스 storage 에 Deployment writer 를 만드십시오. 이미지는 nginx:1.27, 복제본 1개입니다.
PVC data 를 볼륨 이름 data 로 /data 에 마운트하고, emptyDir 볼륨 cache 를 /cache 에 마운트하십시오.
볼륨은 파드 스펙의 volumes 에 선언하고 컨테이너의 volumeMounts 에서 이름으로 가져다 씁니다. 둘 중 하나만 있으면 파드가 만들어지지 않거나 마운트가 빠집니다.
엔드포인트가 비어 있는 서비스
먼저 아래 매니페스트를 그대로 적용하십시오.
apiVersion: apps/v1
kind: Deployment
metadata: {name: shop, namespace: broken}
spec:
replicas: 3
selector: {matchLabels: {app: shop}}
template:
metadata: {labels: {app: shop}}
spec:
containers:
- name: shop
image: nginx:1.27
ports: [{containerPort: 8080}]
---
apiVersion: v1
kind: Service
metadata: {name: shop, namespace: broken}
spec:
selector: {app: shop-svc}
ports: [{port: 80, targetPort: 80, protocol: TCP}]
파드는 3개 다 뜨는데 Service shop 에 엔드포인트가 하나도 붙지 않습니다. 디플로이먼트는 건드리지 말고 Service 만 고쳐, 파드 3개가 8080 번으로 붙게 하십시오.
고칠 곳은 두 군데입니다. 엔드포인트가 아예 비는 이유와, 포트가 엉뚱한 곳으로 가는 이유는 서로 다른 필드에 있습니다. kubectl get svc shop -n broken -o yaml 과 파드의 라벨을 나란히 놓고 보십시오.
Pending 에서 못 벗어나는 워크로드
먼저 아래 매니페스트를 그대로 적용하십시오.
apiVersion: apps/v1
kind: Deployment
metadata: {name: report, namespace: broken}
spec:
replicas: 2
selector: {matchLabels: {app: report}}
template:
metadata: {labels: {app: report}}
spec:
containers:
- name: report
image: nginx:1.27
resources:
requests: {cpu: "16", memory: 64Mi}
파드가 Pending 에서 벗어나지 못합니다. 복제본 수와 이미지는 그대로 두고 원인을 없애 파드 2개가 모두 Running 이 되게 하십시오.
kubectl describe pod 의 Events 나 파드의 PodScheduled 조건에 스케줄러가 이유를 적어 둡니다. 노드 하나가 줄 수 있는 양은 kubectl describe node 의 Allocatable 에 있습니다.
너무 넓은 권한 줄이기
먼저 아래 매니페스트를 그대로 적용하십시오.
apiVersion: v1
kind: ServiceAccount
metadata: {name: ci, namespace: broken}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata: {name: ci-admin}
roleRef: {apiGroup: rbac.authorization.k8s.io, kind: ClusterRole, name: cluster-admin}
subjects:
- {kind: ServiceAccount, name: ci, namespace: broken}
감사에서 이 바인딩이 지적되었습니다. 서비스어카운트 ci 는 지우지 말고, 권한만 줄이십시오. ci 는 네임스페이스 broken 안에서 pods 를 get·list·watch 하고 deployments 를 get·list·watch·create·update 할 수 있어야 하며, 그 밖에는 클러스터 어디에서도 아무것도 할 수 없어야 합니다.
cluster-admin 바인딩을 지우는 것만으로는 끝이 아닙니다. 지우고 나면 ci 는 아무 권한도 없으므로 필요한 만큼을 네임스페이스 범위로 다시 주어야 합니다. 확인은 kubectl auth can-i --as=system:serviceaccount:broken:ci 로 합니다.
쿼터에 걸려 못 뜨는 워크로드
먼저 아래 매니페스트를 그대로 적용하십시오.
apiVersion: v1
kind: ResourceQuota
metadata: {name: tight, namespace: quota-broken}
spec:
hard:
requests.cpu: 500m
limits.memory: 512Mi
---
apiVersion: apps/v1
kind: Deployment
metadata: {name: worker, namespace: quota-broken}
spec:
replicas: 3
selector: {matchLabels: {app: worker}}
template:
metadata: {labels: {app: worker}}
spec:
containers:
- name: worker
image: nginx:1.27
파드가 하나도 뜨지 않습니다. 쿼터는 손대지 말고 워크로드 쪽만 고쳐 복제본 3개가 모두 Running 이 되게 하십시오.
ReplicaSet 의 상태 조건에 어드미션이 거절한 이유가 그대로 적혀 있습니다 (kubectl describe rs -n quota-broken). 쿼터에 어떤 자원이 적혀 있으면 그 네임스페이스의 모든 파드가 그 자원을 반드시 선언해야 합니다.
끝나지 않는 드레인
먼저 아래 매니페스트를 그대로 적용하십시오.
apiVersion: apps/v1
kind: Deployment
metadata: {name: edge, namespace: broken}
spec:
replicas: 2
selector: {matchLabels: {app: edge}}
template:
metadata: {labels: {app: edge}}
spec:
nodeSelector: {kubernetes.io/hostname: lab-node-0}
containers: [{name: edge, image: nginx:1.27}]
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: {name: edge-pdb, namespace: broken}
spec:
minAvailable: 2
selector: {matchLabels: {app: edge}}
정비를 위해 노드 lab-node-0 을 비워야 합니다. 그런데 드레인이 끝나지 않습니다. PodDisruptionBudget 은 지우지 말고, 복제본 2개를 유지한 채 드레인을 끝내십시오. 끝난 뒤 lab-node-0 에는 edge 파드가 하나도 없어야 하고 두 파드는 다른 노드에서 Running 이어야 합니다.
막는 것이 둘입니다. 하나는 파드를 비울 수 없게 하고, 다른 하나는 비워도 갈 곳이 없게 만듭니다. kubectl get pdb -n broken 의 ALLOWED DISRUPTIONS 열과 파드의 nodeSelector 를 함께 보십시오.