CKAD模擬試験A
한국어 원문으로 표시합니다.
모의고사입니다
실제 CKAD 는 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로 입력 모드에 들어가야 합니다. - 문항마다 배점이 다르고, 정답에 이르는 경로는 여러 가지가 허용됩니다.
도메인 배분
| 도메인 | 실제 배점 | 이 세트의 과제 |
|---|---|---|
| Application Design and Build | 20% | 1~3번 |
| Application Deployment | 20% | 4~7번 |
| Application Observability and Maintenance | 15% | 8~10번 |
| Application Environment, Configuration and Security | 25% | 11~14번 |
| Services and Networking | 20% | 15~17번 |
시간 배분에 대해
3번(CronJob)은 컨트롤러가 실제로 한 번 돌 때까지 최대 1분이 걸립니다. 먼저 만들어
두고 다른 과제를 푼 뒤에 확인하십시오. 4번과 5번은 롤아웃이 끝날 때까지 기다려야
하므로 kubectl rollout status 를 쓰는 편이 빠릅니다.
이 환경에서 다른 점
파드 안의 클러스터는 kwokctl 이 띄운 1인용 클러스터입니다. 컨트롤 플레인은 진짜라서
매니페스트·RBAC·스케줄링·쿼터·CRD·PV 바인딩·드레인은 전부 실제로 동작합니다.
다만 워크로드 컨테이너는 실행되지 않으므로 kubectl exec·logs·port-forward 는
쓸 수 없습니다. 과제도 그것을 요구하지 않습니다.
시작 전에 kubectl get nodes 로 노드 3개가 Ready 인지 확인하십시오. 아직이라면
클러스터가 뜨는 중입니다(2분쯤 걸립니다).
채점
각 과제의 확인 버튼을 누르면 채점기가 살아 있는 클러스터에서 값을 다시 계산해 대조합니다. 파일에 무엇을 적었는지가 아니라 클러스터가 어떤 상태인지를 봅니다. 정답에 이르는 경로는 여러 가지가 허용됩니다.
초기화 컨테이너와 사이드카
네임스페이스 app 에 파드 logger 를 만드십시오.
- emptyDir 볼륨
work를 두고 모든 컨테이너가/work에 마운트합니다. - 초기화 컨테이너
seed는busybox:1.36으로/work/seed.txt를 만듭니다. - 컨테이너
app은nginx:1.27입니다. - 컨테이너
tailer는busybox:1.36으로/work/seed.txt를 tail 합니다.
초기화 컨테이너는 spec.initContainers 에 따로 적습니다. 볼륨은 파드에 한 번 선언하고 세 컨테이너가 각자 volumeMounts 로 가져다 씁니다.
완료 횟수를 지정한 Job
네임스페이스 app 에 Job migrate 를 만드십시오. 이미지는 busybox:1.36 이고 echo done 을 실행합니다.
완료 횟수 4, 병렬도 2, backoffLimit 2, activeDeadlineSeconds 120, restartPolicy 는 Never 입니다. 잡은 Complete 로 끝나야 합니다.
Job 의 completions 는 만들고 나면 바꿀 수 없습니다. 값을 잘못 적었다면 지우고 다시 만드십시오. restartPolicy 는 파드 템플릿 쪽에 적습니다.
CronJob
네임스페이스 app 에 CronJob report 를 만드십시오. 이미지는 busybox:1.36 으로 date 를 실행하고 restartPolicy 는 OnFailure 입니다.
스케줄은 */1 * * * *, concurrencyPolicy 는 Forbid, startingDeadlineSeconds 는 30, 성공 이력은 2개, 실패 이력은 1개만 남깁니다. 실제로 한 번 이상 실행되어야 합니다.
주기가 1분이므로 만든 직후에는 아직 실행 기록이 없습니다. kubectl get cronjob report -n app 의 LAST SCHEDULE 열에 시각이 찍힐 때까지 기다렸다가 채점하십시오.
롤링 업데이트로 이미지 올리기
네임스페이스 deploy 에 Deployment api 를 nginx:1.27 로, 복제본 3개로 만드십시오. 전략은 RollingUpdate 이고 maxSurge 2·maxUnavailable 0 입니다.
그다음 이미지를 nginx:1.28 로 올리십시오. 지우고 다시 만들지 말고 갱신해야 합니다.
kubectl set image deploy/api <컨테이너>=nginx:1.28 로 갱신하면 이전 ReplicaSet 이 이력으로 남습니다. kubectl rollout status 로 끝날 때까지 기다리십시오.
롤백
네임스페이스 deploy 에 Deployment web 을 nginx:1.27 로, 복제본 2개로 만드십시오.
그다음 이미지를 nginx:1.99-broken 으로 올렸다가, 잘못된 배포임을 확인하고 첫 번째 리비전으로 되돌리십시오. 되돌린 뒤에도 문제 리비전은 이력에 남아 있어야 합니다.
kubectl rollout history deploy/web 으로 리비전 번호를 확인한 뒤 kubectl rollout undo deploy/web --to-revision=1 을 씁니다. 지우고 다시 만들면 이력이 사라집니다.
라벨로 나눈 카나리
네임스페이스 deploy 에 Deployment shop-stable(복제본 4, 파드 라벨 app=shop·version=stable, 이미지 nginx:1.27)과 shop-canary(복제본 1, 파드 라벨 app=shop·version=canary, 이미지 nginx:1.28)를 만드십시오. 두 파드 모두 컨테이너 포트는 8080 입니다.
Service shop 은 80 번을 받아 8080 으로 넘기며, 두 디플로이먼트의 파드를 모두 가리켜야 합니다.
서비스 셀렉터에 version 을 넣으면 한쪽만 잡힙니다. 두 디플로이먼트가 공통으로 가진 라벨 하나만 셀렉터로 쓰십시오. 엔드포인트가 5개인지 확인하면 됩니다.
kustomize 오버레이
/root/exam/kustomize/base 에 Deployment cart(복제본 1, 이미지 nginx:1.27, 파드 라벨 app=cart)와 kustomization 을 두십시오.
/root/exam/kustomize/overlay 에 오버레이를 만들어 이름 앞에 prod- 를 붙이고, 네임스페이스를 deploy-prod 로, 복제본을 3으로, 이미지 태그를 1.28 로 바꾸고, 라벨 env=prod 를 더하십시오(셀렉터에는 넣지 않습니다).
그 오버레이를 클러스터에 적용하십시오.
kubectl kustomize <디렉터리> 로 결과를 먼저 눈으로 확인한 뒤 kubectl apply -k 로 적용하십시오. 셀렉터는 만들고 나면 바꿀 수 없으므로 공통 라벨을 셀렉터에 넣으면 다음 적용이 거절됩니다.
세 가지 프로브
네임스페이스 obs 에 Deployment checkout 을 만드십시오. 이미지는 nginx:1.27, 복제본 2개, 컨테이너 포트는 8080(이름 http)입니다.
- startupProbe: httpGet
/startup:8080, failureThreshold 30, periodSeconds 5 - readinessProbe: httpGet
/ready:8080, initialDelaySeconds 5, periodSeconds 10 - livenessProbe: httpGet
/health:8080, periodSeconds 15, failureThreshold 3, timeoutSeconds 2
세 프로브는 컨테이너 스펙 안에 나란히 적습니다. 값을 하나라도 빠뜨리면 기본값이 들어가는데, 채점기는 문제에 적힌 값을 그대로 기대합니다.
파드 목록을 파일로 뽑기
네임스페이스 obs 에서 라벨 app=checkout 인 파드를 이름순으로 정렬하여, 한 줄에 <파드이름> <노드이름> <상태> 형식으로 /root/exam/09-pods.txt 에 저장하십시오. 머리글은 넣지 않습니다.
채점기는 이 파일을 채점 시점의 클러스터와 대조합니다. 나중에 파드를 다시 만들었다면 파일도 다시 만드십시오.
kubectl get pods -o jsonpath 의 range 로 필요한 필드만 뽑고 sort 로 정렬하면 됩니다. 노드 이름은 .spec.nodeName, 상태는 .status.phase 입니다.
사라진 API 버전 옮기기
아래 매니페스트는 지금 클러스터에서 서빙하지 않는 API 버전으로 쓰여 있습니다. 현재 버전으로 옮겨 네임스페이스 obs 에 적용하십시오. 필요한 필드가 늘었다면 채워야 합니다.
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata: {name: obs-pdb}
spec:
minAvailable: 1
---
apiVersion: batch/v1beta1
kind: CronJob
metadata: {name: obs-cron}
spec:
schedule: "0 * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers: [{name: obs, image: busybox:1.36, command: ["sh","-c","date"]}]
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata: {name: obs-ing}
spec:
rules:
- host: obs.example.com
http:
paths:
- path: /
backend:
serviceName: checkout
servicePort: 80
인그레스가 가리킬 Service checkout(80 번 → 컨테이너 포트 http)과 IngressClass nginx(controller k8s.io/ingress-nginx)도 함께 만들고, 인그레스에 그 클래스를 지정하십시오. PDB 의 셀렉터는 app=checkout 입니다.
kubectl explain <리소스> --recursive 로 지금 버전의 필드 이름을 확인할 수 있습니다. policy/v1 의 PDB 는 selector 가 필수이고, networking.k8s.io/v1 의 Ingress 는 pathType 과 backend.service 구조를 요구합니다.
설정과 비밀값 주입
네임스페이스 cfg 에 ConfigMap app-config 를 만드십시오. 키는 APP_MODE=production, LOG_LEVEL=info, 그리고 파일용 키 app.properties(내용은 자유)입니다.
Secret app-secret(Opaque)에 키 API_TOKEN 을 두십시오. 값은 직접 정하십시오.
Deployment svc(이미지 nginx:1.27, 복제본 1)는 세 가지 방식을 모두 씁니다.
envFrom으로 ConfigMap 전체를 환경변수로 받습니다.- 환경변수
API_TOKEN은 Secret 의 같은 이름 키를 참조합니다(평문으로 적지 않습니다). - ConfigMap 을 볼륨으로 마운트하되
subPath로app.properties하나만/etc/app/app.properties에 놓습니다.
subPath 를 쓰면 디렉터리가 아니라 파일 하나만 그 경로에 놓입니다. mountPath 에 파일 경로를 적고 subPath 에 키 이름을 적습니다. 값을 --from-literal 로 만들면 base64 인코딩은 kubectl 이 알아서 합니다.
securityContext 로 조이기
네임스페이스 cfg 에 Deployment hardened 를 만드십시오. 이미지는 nginx:1.27, 복제본 1개입니다.
파드 수준: runAsUser 10001, runAsGroup 10001, fsGroup 10001, runAsNonRoot true, seccompProfile 은 RuntimeDefault.
컨테이너 수준: allowPrivilegeEscalation false, readOnlyRootFilesystem true, capabilities 는 전부 drop 하고 NET_BIND_SERVICE 만 add.
파드 수준과 컨테이너 수준은 다른 자리에 있습니다. capabilities 와 allowPrivilegeEscalation·readOnlyRootFilesystem 은 컨테이너 쪽에만 있습니다.
애플리케이션 전용 서비스어카운트
네임스페이스 cfg 에 서비스어카운트 app-sa 를 만들되 토큰이 자동으로 마운트되지 않게 하십시오.
Role cm-reader 는 core 그룹의 configmaps 에 get·list 만 허용합니다. RoleBinding cm-reader 로 묶으십시오.
Deployment reader(이미지 nginx:1.27, 복제본 1)가 그 서비스어카운트로 돌게 하십시오.
automountServiceAccountToken 은 서비스어카운트 오브젝트의 최상위 필드입니다(spec 안이 아닙니다). 디플로이먼트에서는 파드 스펙의 serviceAccountName 으로 지정합니다.
한도 안에서 도는 워크로드
네임스페이스 cfg-quota 에 LimitRange cfg-limits 를 두십시오. 컨테이너 기준으로 max cpu 500m·memory 512Mi, min cpu 50m·memory 64Mi, default cpu 250m·memory 256Mi, defaultRequest cpu 100m·memory 128Mi 입니다.
ResourceQuota cfg-quota 의 한도는 pods 10, requests.cpu 1, requests.memory 1Gi, limits.cpu 2, limits.memory 2Gi 입니다.
Deployment sized(이미지 nginx:1.27, 복제본 2)는 requests cpu 100m·memory 128Mi, limits cpu 250m·memory 256Mi 로 두 파드가 모두 떠야 합니다.
LimitRange 의 max 를 넘는 값은 어드미션에서 거절됩니다. 쿼터가 실제로 세고 있는지는 kubectl describe quota cfg-quota -n cfg-quota 의 Used 열로 확인하십시오.
ClusterIP 와 NodePort
네임스페이스 net 에 Deployment web 을 만드십시오. 이미지는 nginx:1.27, 복제본 3개, 컨테이너 포트는 8080 이고 이름은 http 입니다.
Service web 은 ClusterIP 로 80 번을 받아 포트 이름 http 로 넘깁니다. Service web-nodeport 는 NodePort 로 같은 파드를 가리키며 80 번을 받고 노드 포트는 30080 입니다.
두 서비스 모두 targetPort 에 숫자가 아니라 이름을 적어야 합니다. nodePort 는 30000~32767 범위에서만 직접 지정할 수 있습니다.
경로 두 개를 가르는 Ingress
네임스페이스 net 에 Deployment admin(이미지 nginx:1.27, 복제본 1, 컨테이너 포트 8080 이름 http)과 Service admin(80 번 → http)을 만드십시오.
IngressClass nginx(controller k8s.io/ingress-nginx)를 만들고, Ingress net-ing 으로 호스트 app.example.com 의 / 는 web:80 으로, /admin 은 admin:80 으로 보내십시오. 두 경로 모두 pathType 은 Prefix 입니다.
경로가 겹칠 때는 더 구체적인 것이 먼저 매칭되지만, 채점은 두 규칙이 정확히 적혀 있는지만 봅니다. 백엔드 서비스가 실제로 있는지도 확인하십시오.
차단과 허용 세 정책
네임스페이스 net 에 NetworkPolicy 세 개를 만드십시오.
deny-all: 네임스페이스의 모든 파드에 대해 들어오는 트래픽과 나가는 트래픽을 모두 막습니다.allow-dns: 네임스페이스의 모든 파드가kube-system네임스페이스로 나가는 UDP 53 과 TCP 53 을 허용합니다.allow-web:app=web파드에 대해app=client파드에서 오는 TCP 8080 을 허용합니다.
egress 를 전부 막으면 DNS 도 막혀서 파드가 아무 이름도 못 풉니다. DNS 는 UDP 만 쓰는 것이 아니라 응답이 크면 TCP 로 넘어가므로 둘 다 열어야 합니다. 네임스페이스 셀렉터는 kubernetes.io/metadata.name 라벨로 고르면 됩니다.