LabHub
배우기 러닝패스 코스

CNPE — Cloud Native Platform Engineer

CNPE Mock Exam A

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

실제 CNPE 와 같은 조건에서 과제 17개를 120분 안에 풉니다. 합격선은 64% 이고 부분 점수제이므로, 17개 중 11개를 통과하면 완료로 처리됩니다.

모의고사입니다. 힌트와 정답지를 보지 말고 먼저 끝까지 풀어 보십시오. 막힌 과제는 표시해 두고 넘어갔다가 남은 시간에 돌아오는 편이 낫습니다. 채점은 언제든 눌러도 되고, 여러 번 눌러도 결과가 달라지지 않습니다.

왜 중요한가

CNPE 는 전문가 등급이고, 묻는 것이 조작법이 아니라 판단입니다. 같은 규칙을 파이프라인에서 막을지 API 서버에서 막을지, 사고가 났을 때 제약을 지울지 만족시킬지, 테넌트에게 무엇을 열어 주고 무엇을 닫아 둘지를 정하는 자리가 과제마다 하나씩 들어 있습니다. 도구는 여러 개가 나오지만 어느 것도 깊게 묻지 않습니다. 대신 어느 도구를 어디에 놓을 것인가를 묻습니다.

시험 환경 (실제 시험에서 확인된 사실)

이 모의고사 환경에서 다른 점

이 실습의 클러스터는 파드 안에서 도는 1인용 클러스터입니다. kube-apiserver 와 컨트롤러 매니저와 스케줄러는 진짜라서 스키마 검증, 승인 정책, RBAC 판정, 쿼터, 스케줄링, 집계 ClusterRole, 축출 API 는 실제로 동작하고 실제로 거부합니다. 다만 컨테이너를 실제로 돌리는 런타임이 없으므로 다음이 다릅니다.

노드는 3대이고 각각 CPU 8코어, 메모리 32Gi 이며 존은 zone-0, zone-1, zone-2 로 하나씩 다릅니다.

단계

GitOps and Continuous Delivery

  1. /root/exam/chart 에 Helm 차트를 만드십시오. 차트 이름은 paved-app 이고 템플릿은 Deployment 하나이며 그 이름은 paved-app 입니다. 컨테이너 이름은 app, 레플리카 수는 .Values.replicas, 이미지는 .Values.image.repository.Values.image.digest@ 로 이은 것입니다. values.schema.json 으로 replicas 는 2 이상 10 이하의 정수, tierbronze, silver, gold 중 하나, image.digestsha256: 뒤에 16진수 64자리인 문자열만 허용하고 세 값 모두 필수로 만드십시오. 기본값은 각각 3, silver, 그리고 임의의 유효한 다이제스트입니다.
  2. /root/exam/portal 에 kustomize 디렉터리를 만드십시오. Deployment portal 은 레플리카 2, 컨테이너 이름 app, 이미지는 다이제스트 고정이며, envFrom 으로 ConfigMap portal-config 를 통째로 읽습니다. 그 ConfigMap 은 파일로 두지 말고 configMapGenerator 로 만들되 LOG_LEVEL=infoFEATURE_FLAGS=beta 두 항목을 담고, 이름 해시 접미사를 끄지 마십시오. 렌더 결과에서 Deployment 가 참조하는 이름과 생성된 ConfigMap 의 이름이 같아야 합니다.
  3. /root/exam/gitops/appproject.yaml 에 Argo CD AppProject platform 을 쓰십시오. 네임스페이스는 argocd 입니다. sourceRepos 에는 실제 저장소 주소를 적고 * 를 쓰지 마십시오. destinations 는 서버와 네임스페이스를 모두 못박고 네임스페이스에 * 를 쓰지 마십시오. clusterResourceWhitelist 는 비워 두고, namespaceResourceBlacklist 에는 코어 그룹의 ResourceQuotaLimitRange 를 넣으십시오.
  4. /root/exam/promote 를 git 저장소로 만드십시오. envs/staging/deployment.yamlenvs/prod/deployment.yaml 두 파일이 있고 둘 다 Deployment ledger 이며 컨테이너 이름은 app, 이미지는 다이제스트로 고정합니다. 첫 커밋에서는 두 다이제스트가 서로 다릅니다. 그다음 스테이징의 다이제스트를 운영으로 올리는 승격 커밋 하나 를 더 쌓으십시오. 그 커밋에서 스테이징 파일은 바뀌지 않아야 하고, 커밋 메시지에 올린 다이제스트가 들어가야 하며, 작업 트리는 깨끗해야 합니다.

Platform APIs and Self-Service Capabilities

  1. /root/exam/api/environment-crd.yaml 에 CRD 를 쓰고 apply 하십시오. 그룹은 platform.labhub.io, 종류는 Environment, 복수형은 environments, 짧은 이름은 env, 범위는 Namespaced 입니다. 버전은 둘입니다. v1 은 served 이자 저장 버전이고, spec.owner 는 필수 문자열, spec.tierbronze, silver, gold 중 하나이며 기본값이 bronze, spec.retentionDays 는 1 이상 90 이하의 정수이며 기본값이 7 입니다. v1alpha1 은 served 이지만 저장 버전이 아니고 spec.owner 만 가지며, 폐기 표시를 하고 폐기 경고 문구를 직접 적으십시오. 스키마에 없는 필드는 서버가 거절해야 합니다.
  2. /root/exam/api/env-defaults.yaml 에 Kyverno ClusterPolicy env-defaults 를 쓰십시오. Environment 리소스에만 적용되는 변형(mutate) 규칙으로, 라벨 platform.labhub.io/ownerspec.owner 값으로, 애너테이션 platform.labhub.io/requested-tierspec.tier 값으로 채웁니다. 값을 고정해 적지 말고 요청에서 읽어 채워야 합니다.
  3. /root/exam/api/env-rbac.yaml 에 집계 권한을 쓰고 apply 하십시오. ClusterRole platform-env-author 는 자체 규칙 없이 라벨 platform.labhub.io/aggregate-to-env-author: "true" 가 붙은 ClusterRole 을 모읍니다. ClusterRole platform-env-author-base 에 그 라벨을 붙이고 Environment 에 대해 get, list, watch, create, update, patch 를 허용하되 delete 는 주지 마십시오. 네임스페이스 tenant-amber 와 ServiceAccount env-author 를 만들고 RoleBinding env-author 로 그 집계 ClusterRole 을 묶으십시오. 어느 규칙에도 * 를 쓰지 마십시오.
  4. /root/exam/api/env-status-rbac.yaml 에 spec 과 status 의 권한을 나누십시오. tenant-amber 에 ServiceAccount env-controller, Role env-controller, RoleBinding env-controller 를 만듭니다. 이 계정은 Environment 를 읽고 environments/status 를 update, patch 할 수 있지만 Environment 자체를 create, update, patch, delete 할 수는 없습니다. 반대로 7번의 env-authorenvironments/status 를 update 할 수 없어야 합니다.

Observability and Operations

  1. /root/exam/ops/platform-rules.yaml 에 Prometheus 규칙을 쓰십시오. 그룹 이름은 platform-api 이고, 기록 규칙 platform:request_error:ratio5m 은 나눗셈과 5분 구간을 쓰며, 알림 규칙 PlatformApiErrorBudgetBurn 에는 for, labels.severity, annotations.summary, annotations.runbook_url 이 있어야 합니다. 그리고 /root/exam/ops/platform-rules-test.yaml 에 규칙 유닛 테스트를 쓰십시오. rule_files 에는 상대 이름 platform-rules.yaml 만 적고, alert_rule_test 를 두 개 이상 두되 하나는 알림이 울리는 시각을, 다른 하나는 울리지 않는 시각(exp_alerts: [])을 확인해야 합니다. promtool check rulespromtool test rules 가 모두 통과해야 합니다.
  2. 네임스페이스 platform-system 을 만들고 /root/exam/ops/servicemonitor.yaml 에 ServiceMonitor platform-api 를 써서 적용하십시오. 대상 선택자는 app: platform-api, namespaceSelectorplatform-system 입니다. 엔드포인트는 하나이고 포트 이름은 metrics, 수집 주기는 30s, 수집 제한 시간은 주기보다 짧아야 합니다. metricRelabelings 에는 __name__ 을 보고 특정 지표를 drop 하는 규칙과, 고카디널리티 라벨을 labeldrop 으로 떨어뜨리는 규칙이 각각 하나 이상 있어야 합니다.
  3. 네임스페이스 tenant-ochre 를 만들어 라벨 platform.labhub.io/tenant: ochre 를 붙이고, ResourceQuota ochre-quotarequests.cpu 를 2, requests.memory 를 4Gi 로 제한하십시오. 그 안에 Deployment search 를 레플리카 4로 만드십시오. 컨테이너 이름은 app, 이미지는 다이제스트 고정, 요청은 CPU 700m 과 메모리 512Mi 입니다. 이 시점에 파드는 다 뜨지 못합니다. 그다음 /root/exam/ops/triage.txtquota_cpu_hard, quota_memory_hard, desired_replicas, max_cpu_per_pod 네 줄을 키=값 형식으로 적으십시오. CPU 는 밀리코어 정수로, 메모리는 Mi 정수로, 마지막 값은 이 쿼터 안에서 레플리카가 전부 뜨려면 파드 하나의 CPU 요청이 얼마 이하여야 하는지를 밀리코어 정수로 적습니다. 단위 문자는 붙이지 마십시오.
  4. 쿼터를 올리지 말고 search 의 파드 4개가 모두 Running 이 되게 하십시오. 레플리카 수와 메모리 요청은 그대로 두고, CPU 요청만 11번에서 계산한 값으로 내립니다. 옛 요청값을 쓰는 파드가 남아 있으면 안 됩니다.

Platform Architecture and Infrastructure

  1. 노드 정확히 두 대 에 라벨 platform.labhub.io/pool=platform 과 테인트 platform.labhub.io/pool=platform:NoSchedule 을 함께 거십시오. 그리고 platform-system 에 Deployment portal 을 레플리카 3으로 만드십시오. 컨테이너 이름은 app, 이미지는 다이제스트 고정, 요청은 CPU 100m 과 메모리 128Mi 입니다. 이 워크로드는 그 풀만 고르고, 그 테인트를 견디며, topology.kubernetes.io/zone 을 기준으로 maxSkew: 1whenUnsatisfiable: DoNotSchedule 로 퍼져야 합니다. 파드 3개가 모두 풀 노드 위에서 Running 이어야 합니다.
  2. PriorityClass platform-critical(값 100000 이상)과 tenant-batch(값 1000 이하, preemptionPolicy: Never)를 만드십시오. 둘 다 globalDefault 는 false 입니다. tenant-amber 에 ResourceQuota amber-priority 를 걸어 platform-critical 우선순위를 쓰는 파드를 그 네임스페이스에서 아예 만들 수 없게 하십시오. 그리고 13번의 portalplatform-critical 을 쓰게 하십시오.
  3. platform-system 에 PodDisruptionBudget portal 을 만드십시오. 선택자는 portal 파드를 고르고 minAvailable 은 2 이며 maxUnavailable 은 쓰지 않습니다. 유지보수 때 한 번에 한 대만 비울 수 있어야 합니다.

Security and Policy Enforcement

  1. 네임스페이스 tenant-teal 을 만들고 파드 보안 승인의 enforce, audit, warn 을 모두 restricted 로 걸되 세 모드의 버전 라벨은 latest 가 아니라 v1.31 로 고정하십시오. 그 안에 Deployment checkout 을 레플리카 2로 만드십시오. 컨테이너 이름은 app, 이미지는 다이제스트 고정이며 파드는 restricted 를 통과해야 합니다. 파드 2개가 실제로 Running 이어야 합니다.
  2. 규칙 하나를 두 곳에서 집행하십시오. 규칙은 "Deployment 에는 라벨 platform.labhub.io/owner 가 있어야 한다" 입니다. /root/exam/security/owner-policy.yaml 에 Kyverno ClusterPolicy require-ownerEnforce 로 쓰십시오. 파이프라인이 kyverno apply 로 돌릴 게이트입니다. /root/exam/security/owner-vap.yaml 에는 ValidatingAdmissionPolicy require-owner 와 같은 이름의 바인딩을 쓰고 apply 하십시오. validationActionsDeny 이고, 적용 범위는 라벨 platform.labhub.io/gate=owner 가 붙은 네임스페이스뿐입니다. 네임스페이스 delivery 를 만들어 그 라벨을 붙이십시오. 라벨 없는 Deployment 는 delivery 에서 거부되고 default 에서는 통과해야 합니다.

참고

차트가 잘못된 값을 스스로 거절하게

helm 은 차트 안에 values.schema.json 이 있으면 template 과 install 앞에서 값을 검증하고, 어긋나면 렌더 자체를 거절합니다. JSON 스키마의 minimum, maximum, enum, pattern, required 를 쓰십시오. 스키마가 걸리는지는 helm template ... --set replicas=1 처럼 일부러 어긋난 값을 넣어 확인합니다.

설정이 바뀌면 배포도 바뀌게

configMapGenerator 는 내용의 해시를 이름 뒤에 붙이고, 같은 kustomization 안에서 그 이름을 참조하는 곳을 자동으로 고쳐 씁니다. Deployment 에는 접미사 없는 원래 이름을 적어 두면 됩니다. disableNameSuffixHash 를 켜면 이 연결이 끊어지고, 설정만 바꿨을 때 파드가 다시 뜨지 않습니다.

프로젝트가 무엇을 허용하는지 못박기

AppProject 는 Application 이 무엇을 어디에 배포할 수 있는지 정하는 담장입니다. sourceRepos 와 destinations 에 별표를 남겨 두면 담장이 없는 것과 같습니다. clusterResourceWhitelist 를 비워 두면 클러스터 범위 자원을 아예 못 만들고, namespaceResourceBlacklist 는 네임스페이스 안이라도 손대지 못할 종류를 적는 자리입니다.

승격을 커밋 하나로 남기기

GitOps 에서 배포는 커밋입니다. 그래서 무엇이 언제 올라갔는지는 로그에 남아야 하고, 되돌리기는 revert 여야 합니다. 스테이징에서 검증한 다이제스트를 그대로 운영에 옮기되, 스테이징 파일은 건드리지 마십시오. 다 하고 나면 git status 가 비어 있어야 합니다. 커밋하지 않은 변경은 배포된 것이 아닙니다.

스키마가 계약이 되게

구조 스키마(structural schema)에 default 를 적으면 서버가 값을 채워 주고, 적지 않은 필드는 서버가 거절합니다. 두 성질이 함께 와야 스키마가 계약이 됩니다. 폐기는 versions 항목의 deprecated 와 deprecationWarning 으로 표시하며, 그 문구는 실제로 kubectl 화면에 Warning 으로 뜹니다. 확인은 kubectl apply --dry-run=server -o json 으로 하십시오.

요청에 빠진 것을 플랫폼이 채우기

셀프서비스는 사용자가 적어야 할 것이 적을수록 좋습니다. 사용자가 이미 적은 값에서 끌어낼 수 있는 것은 플랫폼이 채워야 합니다. Kyverno 의 mutate 규칙에서 {{ request.object... }} 로 요청 본문을 읽을 수 있습니다. 값을 고정해 적으면 채점기가 다른 요청으로 물었을 때 드러납니다. 확인은 kyverno apply <정책> --resource <파일> -o <디렉터리> 로 하십시오.

권한을 조각으로 나눠 모으기

집계 ClusterRole 은 자기 규칙을 적지 않습니다. 라벨 선택자만 적어 두면 컨트롤러가 그 라벨이 붙은 다른 ClusterRole 의 규칙을 모아서 채워 넣습니다. 그래서 나중에 권한을 늘릴 때 이 롤을 고치지 않고 조각을 하나 더 붙이면 됩니다. 집계가 실제로 됐는지는 kubectl get clusterrole <이름> -o jsonpath='{.rules}' 로 확인하십시오. 비어 있으면 라벨이 어긋난 것입니다.

적는 사람과 채우는 사람을 나누기

status 는 컨트롤러가 채우는 자리이고 spec 은 사용자가 적는 자리입니다. RBAC 에서 이 둘은 별개의 자원 이름이라 environments 와 environments/status 를 따로 적습니다. 확인할 때는 자원 이름 뒤에 슬래시를 붙이지 말고 --subresource=status 를 쓰십시오. 슬래시로 적으면 kubectl 이 엉뚱하게 해석해 반대 결과를 냅니다.

알림 규칙을 시험으로 못박기

promtool test rules 는 가짜 시계열을 넣고 규칙을 실제로 평가합니다. input_series 의 값은 '0+3x30' 처럼 시작값, 증가폭, 반복 횟수로 적습니다. 울리는 시각만 확인하면 언제나 울리는 규칙도 통과하므로, 울리지 않아야 할 시각을 exp_alerts: [] 로 함께 적으십시오. rule_files 는 테스트 파일이 있는 디렉터리를 기준으로 찾습니다.

무엇을 재지 않을지 정하기

관측 비용은 수집한 시계열의 수로 정해지고, 그 수는 라벨의 조합으로 폭발합니다. 그래서 수집 설정에는 무엇을 잴지만큼 무엇을 버릴지가 함께 들어가야 합니다. drop 은 sourceLabels 로 고른 값이 regex 에 맞으면 그 지표를 통째로 버리고, labeldrop 은 regex 에 맞는 라벨 이름을 지웁니다. labeldrop 에 sourceLabels 를 적으면 안 됩니다.

쿼터 소진을 재현하고 숫자로 적기

쿼터가 막으면 Deployment 는 오류를 내지 않습니다. ReplicaSet 이 대신 ReplicaFailure 조건을 달고, 파드는 애초에 만들어지지 않습니다. kubectl describe rs 나 kubectl get rs -o yaml 로 그 조건을 보십시오. 분류는 느낌이 아니라 나눗셈입니다. 쿼터 상한을 원하는 레플리카 수로 나누면 파드 하나가 쓸 수 있는 최대치가 나옵니다.

쿼터를 올리지 않고 복구하기

요청을 내리는 것만으로는 끝나지 않습니다. 쿼터가 꽉 찬 상태에서는 새 파드를 만들 자리가 없어 롤링 업데이트가 중간에서 멈춥니다. 옛 요청값을 쓰는 파드가 남아 있는 한 자리는 비지 않습니다. 레플리카를 0으로 내렸다가 다시 올리거나 옛 파드를 먼저 비우십시오. 확인은 파드 수가 아니라 파드마다의 요청값으로 하십시오.

풀을 나누고 워크로드를 퍼뜨리기

라벨은 고르게 하고 테인트는 밀어냅니다. 둘을 함께 걸어야 그 풀이 전용이 됩니다. 라벨만 걸면 아무나 들어오고, 테인트만 걸면 들어올 방법이 없습니다. topologySpreadConstraints 의 labelSelector 는 파드 라벨을 가리켜야 하고, DoNotSchedule 로 두어야 실제로 강제됩니다. 확인은 파드가 어느 노드에 앉았는지로 하십시오.

우선순위를 쓸 수 있는 사람 정하기

PriorityClass 는 클러스터 범위라 만들어 두면 누구나 이름으로 가져다 쓸 수 있습니다. 그래서 우선순위 자체가 아니라 그 우선순위를 쓸 권리를 따로 막아야 합니다. ResourceQuota 의 scopeSelector 에 PriorityClass 스코프를 걸고 상한을 0으로 두면 그 네임스페이스에서는 그 등급의 파드를 아예 만들 수 없습니다. preemptionPolicy: Never 는 자기가 밀려나지 않는다는 뜻이 아니라 남을 밀어내지 않는다는 뜻입니다.

유지보수 중에도 남아 있을 수를 정하기

PodDisruptionBudget 은 스케줄러가 아니라 축출 API 가 봅니다. kubectl drain 과 노드 업그레이드가 그 경로를 지납니다. 레플리카 3에 minAvailable 2 를 걸면 한 번에 한 대만 비울 수 있습니다. 컨트롤러가 계산한 결과는 kubectl get pdb 의 ALLOWED DISRUPTIONS 열에 나오고, 이 값이 0이면 유지보수가 아예 막히며 레플리카 수와 같으면 아무것도 지키지 않는 것입니다.

테넌트 네임스페이스에 기준선 걸기

파드 보안 승인은 Deployment 를 막지 않고 그 Deployment 가 만드는 파드를 막습니다. Deployment 는 만들어졌는데 파드가 없다면 kubectl describe rs 로 사유를 보십시오. restricted 는 파드 수준에 runAsNonRoot 와 seccompProfile 을, 컨테이너 수준에 allowPrivilegeEscalation: false 와 capabilities.drop [ALL] 을 요구합니다. 버전 라벨을 latest 로 두면 클러스터를 올릴 때 기준이 조용히 엄격해집니다.

같은 규칙을 두 곳에서 집행하기

파이프라인 게이트는 빠른 되먹임을 주지만 파이프라인을 거치지 않은 변경은 못 막고, 승인 정책은 무엇으로 들어오든 막지만 개발자는 배포를 눌러 본 뒤에야 압니다. 그래서 같은 규칙을 두 곳에 둡니다. VAP 의 적용 범위는 바인딩이 정하며, matchResources 의 namespaceSelector 로 좁힐 수 있습니다. validationActions 에 Deny 가 없으면 감사만 남기고 통과시킵니다.