CNPA — Cloud Native Platform Engineering Associate
Setting a Platform Baseline and Raising It
한국어 원문으로 표시합니다.
목표
플랫폼이 요구하는 최소 기준선을 네임스페이스에 못박고, 그것이 실제로 거절하는지 확인한 다음, 한 단계 올리는 과정을 영향 조사부터 예외 처리까지 한 바퀴 돌립니다.
왜 중요한가
기준선을 문서로만 두면 아무것도 거절하지 못하고, 거절하지 못하는 규칙은 반년 뒤에 절반이 깨져 있습니다. 반대로 처음부터 전부 막으면 어제 되던 배포가 오늘 막혀 플랫폼이 장애의 원인으로 기억됩니다. 그래서 실무는 "지금 지킬 등급을 강제하고, 다음 등급은 경고로 미리 알리고, 올리기 전에 영향을 조사하고, 못 지키는 쪽에는 만료일이 붙은 예외를 준다" 는 순서로 움직입니다. 이 실습은 그 순서를 그대로 손으로 밟습니다. 준수(conformance)를 함께 다루는 이유도 같습니다. 폐기된 API 를 남겨 두면 어느 날 클러스터를 올리는 순간 배포가 통째로 멈추는데, 그 신호를 미리 읽는 눈이 플랫폼 팀의 기본기입니다.
단계
- 네임스페이스
platform-baseline을 만들고 라벨을 붙이세요.pod-security.kubernetes.io/enforce=baseline,pod-security.kubernetes.io/enforce-version=v1.30,pod-security.kubernetes.io/warn=restricted,pod-security.kubernetes.io/warn-version=v1.30,platform.labhub.io/owner=platform-team입니다. /root/cnpa-base/bad-probe.yaml에platform-baseline네임스페이스의 파드bad-probe를 작성하세요.spec.hostPID: true이고 컨테이너는 이름probe, 이미지ghcr.io/labhub/probe:1.0.0입니다. 적용해 보고 그 실패 출력을 표준 에러까지 함께/root/cnpa-base/reject.txt에 저장하세요. 파드bad-probe는 클러스터에 남아 있으면 안 됩니다./root/cnpa-base/legacy.yaml에apiVersion: extensions/v1beta1인 Ingressorders-legacy(네임스페이스platform-baseline)를 작성하세요. 적용해 보고 그 실패 출력을/root/cnpa-base/legacy-reject.txt에 저장하세요./root/cnpa-base/app.yaml에 apps/v1 Deploymentorders를 작성해 적용하세요.spec.replicas: 2, 메타데이터 라벨app.kubernetes.io/name: orders와app.kubernetes.io/part-of: cnpa-platform, 컨테이너 이름app, 이미지ghcr.io/labhub/orders:1.4.0, 컨테이너 포트는 이름http(8080)와metrics(9102) 두 개입니다./root/cnpa-base/edge.yaml에는 Serviceorders(셀렉터app.kubernetes.io/name: orders, 포트 이름http는 80 에서 8080 으로, 포트 이름metrics는 9102 에서 9102 로)와 networking.k8s.io/v1 Ingressorders(호스트orders.labhub.internal, 경로/,pathType: Prefix, 백엔드는 서비스orders의 포트 이름http)를 작성해 적용하세요./root/cnpa-base/servicemonitor.yaml에 ServiceMonitororders(네임스페이스platform-baseline)를 작성해 적용하세요.spec.selector.matchLabels는app.kubernetes.io/name: orders,spec.endpoints[0].port는metrics,interval은30s입니다./root/cnpa-base/exception.yaml에 네임스페이스platform-legacy를 작성해 적용하세요. 라벨은pod-security.kubernetes.io/enforce: baseline과pod-security.kubernetes.io/enforce-version: v1.30, 어노테이션은platform.labhub.io/exception-expires(YYYY-MM-DD 형식의 만료일)와platform.labhub.io/exception-reason(사유)입니다. 같은 파일에 Deploymentlegacy-batch(replicas 1, 이미지ghcr.io/labhub/legacy-batch:0.9.0, 보안 설정 없음)도 넣으세요. 그다음 이 네임스페이스를restricted로 올리는 라벨 변경을 서버 dry-run 으로 보내고, 그 출력을 표준 에러까지/root/cnpa-base/upgrade-check.txt에 저장하세요.legacy-batch는 고치지 말고 그대로 둡니다.orders의 파드 템플릿을 restricted 기준에 맞추세요. 파드 수준에runAsNonRoot: true,runAsUser: 10001,seccompProfile.type: RuntimeDefault를, 컨테이너 수준에allowPrivilegeEscalation: false와capabilities.drop: [ALL]을 넣습니다. 다시 적용해 파드가 새로 뜬 뒤에platform-baseline의enforce라벨을restricted로 올리세요.enforce-version은v1.30그대로 둡니다./root/cnpa-base/baseline.json에 현황을 남기세요. 키는namespace,enforce,enforce_version,deployments(그 네임스페이스의 Deployment 개수),servicemonitor(이름),exception_namespace,exception_expires입니다.
참고
- 거절 메시지는 표준 출력이 아니라 표준 에러로 나옵니다.
> 파일 2>&1로 함께 담으세요. - 셀렉터가 무엇을 골랐는지는
kubectl get svc -n platform-baseline -l <라벨>로 되물을 수 있습니다. - 6단계에서
legacy-batch에 보안 설정을 넣어 버리면 상향 경고가 사라져 채점이 떨어집니다. 실수로 넣었다면kubectl delete deploy legacy-batch -n platform-legacy로 지우고 다시 적용하세요. - 7단계는 순서가 중요합니다. 등급을 먼저 올리면 새 파드가 거절되어 롤아웃이 멈춥니다.
기준선 네임스페이스
네임스페이스 platform-baseline 을 만들고 라벨을 붙이세요. pod-security.kubernetes.io/enforce=baseline, pod-security.kubernetes.io/enforce-version=v1.30, pod-security.kubernetes.io/warn=restricted, pod-security.kubernetes.io/warn-version=v1.30, platform.labhub.io/owner=platform-team 입니다.
Pod Security Admission 은 네임스페이스 라벨만으로 동작합니다. 지금 지킬 등급은 enforce 에, 다음에 올릴 등급은 warn 에 겁니다. 등급마다 버전 라벨을 함께 고정하세요.
정말 거절하는지 확인
/root/cnpa-base/bad-probe.yaml 에 platform-baseline 네임스페이스의 파드 bad-probe 를 작성하세요. spec.hostPID: true 이고 컨테이너는 이름 probe, 이미지 ghcr.io/labhub/probe:1.0.0 입니다. 적용해 보고 그 실패 출력을 표준 에러까지 함께 /root/cnpa-base/reject.txt 에 저장하세요. 파드 bad-probe 는 클러스터에 남아 있으면 안 됩니다.
라벨을 붙였다고 정책이 도는 것은 아닙니다. 호스트 네임스페이스를 요구하는 파드를 만들어 보고, 그때 나오는 거절 메시지를 표준 에러까지 함께 파일로 남기세요.
폐기된 API 는 왜 거절되는가
/root/cnpa-base/legacy.yaml 에 apiVersion: extensions/v1beta1 인 Ingress orders-legacy(네임스페이스 platform-baseline)를 작성하세요. 적용해 보고 그 실패 출력을 /root/cnpa-base/legacy-reject.txt 에 저장하세요.
폐기된 그룹으로 쓰인 매니페스트는 문법 오류가 아니라 매핑 실패로 떨어집니다. 그 메시지를 그대로 남기고, 정말 그 그룹이 제공되지 않는지 클러스터에 확인해 보세요.
상위 API 로 옮겨 적용
/root/cnpa-base/app.yaml 에 apps/v1 Deployment orders 를 작성해 적용하세요. spec.replicas: 2, 메타데이터 라벨 app.kubernetes.io/name: orders 와 app.kubernetes.io/part-of: cnpa-platform, 컨테이너 이름 app, 이미지 ghcr.io/labhub/orders:1.4.0, 컨테이너 포트는 이름 http(8080)와 metrics(9102) 두 개입니다. /root/cnpa-base/edge.yaml 에는 Service orders(셀렉터 app.kubernetes.io/name: orders, 포트 이름 http 는 80 에서 8080 으로, 포트 이름 metrics 는 9102 에서 9102 로)와 networking.k8s.io/v1 Ingress orders(호스트 orders.labhub.internal, 경로 /, pathType: Prefix, 백엔드는 서비스 orders 의 포트 이름 http)를 작성해 적용하세요.
Deployment 는 apps/v1, Ingress 는 networking.k8s.io/v1 입니다. v1 Ingress 는 경로마다 pathType 을 요구하고, 백엔드는 서비스 이름과 포트를 따로 가리킵니다. 포트는 번호 대신 이름으로 가리키면 Service 와 어긋나지 않습니다.
스크레이프 계약 맺기
/root/cnpa-base/servicemonitor.yaml 에 ServiceMonitor orders(네임스페이스 platform-baseline)를 작성해 적용하세요. spec.selector.matchLabels 는 app.kubernetes.io/name: orders, spec.endpoints[0].port 는 metrics, interval 은 30s 입니다.
ServiceMonitor 는 Service 를 라벨로 고르고, 포트는 번호가 아니라 Service 의 포트 이름으로 가리킵니다. 만든 뒤에는 그 셀렉터가 정말 무엇을 골랐는지 kubectl 로 되물어 확인하세요.
예외와 상향 영향 조사
/root/cnpa-base/exception.yaml 에 네임스페이스 platform-legacy 를 작성해 적용하세요. 라벨은 pod-security.kubernetes.io/enforce: baseline 과 pod-security.kubernetes.io/enforce-version: v1.30, 어노테이션은 platform.labhub.io/exception-expires(YYYY-MM-DD 형식의 만료일)와 platform.labhub.io/exception-reason(사유)입니다. 같은 파일에 Deployment legacy-batch(replicas 1, 이미지 ghcr.io/labhub/legacy-batch:0.9.0, 보안 설정 없음)도 넣으세요. 그다음 이 네임스페이스를 restricted 로 올리는 라벨 변경을 서버 dry-run 으로 보내고, 그 출력을 표준 에러까지 /root/cnpa-base/upgrade-check.txt 에 저장하세요. legacy-batch 는 고치지 말고 그대로 둡니다.
라벨 변경을 서버 dry-run 으로 보내면 지금 떠 있는 파드를 새 등급으로 평가한 경고가 돌아옵니다. 경고는 표준 에러로 나오니 함께 저장하세요. 예외 네임스페이스의 워크로드는 일부러 고치지 않고 그대로 둡니다.
워크로드를 맞추고 등급 올리기
orders 의 파드 템플릿을 restricted 기준에 맞추세요. 파드 수준에 runAsNonRoot: true, runAsUser: 10001, seccompProfile.type: RuntimeDefault 를, 컨테이너 수준에 allowPrivilegeEscalation: false 와 capabilities.drop: [ALL] 을 넣습니다. 다시 적용해 파드가 새로 뜬 뒤에 platform-baseline 의 enforce 라벨을 restricted 로 올리세요. enforce-version 은 v1.30 그대로 둡니다.
restricted 는 네 가지를 요구합니다. 루트로 돌지 않기, seccomp 기본 프로파일, 권한 상승 금지, capability 전부 버리기입니다. 앞의 둘은 파드 수준에, 뒤의 둘은 컨테이너 수준에 씁니다. 워크로드를 먼저 고치고 등급은 나중에 올립니다.
기준선 현황을 값으로 남기기
/root/cnpa-base/baseline.json 에 현황을 남기세요. 키는 namespace, enforce, enforce_version, deployments(그 네임스페이스의 Deployment 개수), servicemonitor(이름), exception_namespace, exception_expires 입니다.
보고서의 숫자는 전부 클러스터에서 다시 셀 수 있는 것이어야 합니다. 네임스페이스 라벨, Deployment 개수, ServiceMonitor 이름, 예외 만료일을 각각 조회해 채우세요.