LabHub
学习 学习路径 课程

CNPA — 云原生平台工程助理

策略一开启,紧急补丁就被拦下了

在 LabHub 中继续学习

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

목표

진짜 k3s 와 Kyverno 에서 플랫폼 규칙 하나(소유자 라벨과 리소스 요청)를 세 팀에 들입니다. 감사로 먼저 세고, 보고서로 위반을 찾고, 차단으로 바꿨을 때 무엇이 막히는지 겪어 본 뒤, 고칠 수 없는 대상에 기한부 예외를 주고 준수율을 계산합니다.

왜 중요한가

정책 엔진은 플랫폼의 가드레일을 코드로 강제하는 도구입니다. 그런데 규칙을 처음부터 차단으로 켜면 이미 규칙을 어긴 워크로드의 긴급 패치까지 막혀 장애 대응이 멈춥니다. 그래서 거버넌스는 보통 감사 → 보고서로 현황 파악 → 팀별 수정 → 차단 → 좁고 기한 있는 예외의 순서로 굴러갑니다. 이 실습은 그 순서의 각 단계에서 클러스터가 실제로 어떻게 반응하는지, 그리고 예외가 조용한 영구 허용으로 변하지 않게 하는 장치가 무엇인지를 확인합니다.

단계

  1. /root/cnpa-pol/tenants.yaml 에 세 네임스페이스와 Deployment 네 개를 작성해 적용하세요. 네임스페이스 team-pay·team-legacy·team-vendor 에는 각각 라벨 platform.labhub.io/tenantpay·legacy·vendor 로 붙입니다. Deployment 는 모두 replicas 1, 컨테이너 이름 app, 이미지 registry.k8s.io/pause:3.10, 메타데이터와 셀렉터 라벨 app.kubernetes.io/name: <이름> 입니다. team-pay/checkout 은 메타데이터 라벨 app.kubernetes.io/owner: pay 와 requests(cpu 10m, memory 16Mi)를 둘 다 가지고, team-legacy/report-gen 은 requests 만, team-legacy/batch-sync 는 owner 라벨(legacy)만, team-vendor/vendor-agent 는 둘 다 없습니다.
  2. /root/cnpa-pol/policy.yaml 에 ValidatingPolicy(policies.kyverno.io/v1) tenant-deploy-baseline 을 작성해 적용하세요. validationActions: [Audit], evaluation.background.enabled: true, matchConstraints 는 apps/v1 deployments 의 CREATE·UPDATE 이고 namespaceSelector 로 라벨 platform.labhub.io/tenant 가 있는(Exists) 네임스페이스만 고릅니다. 검증 두 개를 이 순서로 둡니다. ① 메타데이터 라벨에 app.kubernetes.io/owner 가 있음, 메시지 Deployment 에 app.kubernetes.io/owner 라벨이 필요합니다 ② 모든 컨테이너에 requests 의 cpu 와 memory 가 있음, 메시지 모든 컨테이너에 cpu·memory requests 가 필요합니다.
  3. Kyverno 가 남긴 PolicyReport 를 읽어, tenant-deploy-baseline 결과가 fail 인 Deployment 를 /root/cnpa-pol/violations.json 에 배열로 적으세요. 원소마다 namespace, name, uid(보고서의 scope.uid), message(그 결과의 메시지)를 둡니다.
  4. 정책의 validationActions[Deny] 로 바꾸세요. 그다음 legacy 팀의 긴급 패치를 흉내 내어 kubectl -n team-legacy set image deploy/batch-sync app=registry.k8s.io/pause:3.9 를 실행하고 그 출력(표준 에러 포함)을 /root/cnpa-pol/blocked.txt 에 저장하세요. 이어서 kubectl -n team-legacy scale deploy/batch-sync --replicas=2 를 실행하고, /root/cnpa-pol/deny-effects.jsonimage_update_denied, scale_allowed(불리언), batch_sync_image(지금 batch-sync 의 이미지)를 적으세요.
  5. legacy 팀의 두 Deployment 를 규칙에 맞추세요. report-gen 에 메타데이터 라벨 app.kubernetes.io/owner: legacy 를, batch-sync 의 컨테이너 app 에 requests(cpu 50m, memory 64Mi)를 넣습니다. 그다음 4단계에서 막혔던 set image ... app=registry.k8s.io/pause:3.9 를 다시 실행해 통과시키고, 두 Deployment 의 PolicyReport 결과가 pass 로 바뀔 때까지 기다리세요.
  6. vendor-agent 는 업체가 주는 매니페스트라 당장 고칠 수 없습니다. 먼저 kyverno-admission-controller Deployment 의 인자 --enablePolicyException=false--enablePolicyException=true 로 바꾸고 롤아웃이 끝나기를 기다리세요. 그다음 /root/cnpa-pol/exception.yaml 에 PolicyException(policies.kyverno.io/v1) vendor-agent-requeststeam-vendor 네임스페이스에 만듭니다. policyRefs 는 ValidatingPolicy tenant-deploy-baseline, matchConditions 는 이름이 vendor-agent 인 오브젝트만, expiresAt 은 지금부터 30일 뒤(RFC3339, UTC)입니다. 적용 뒤 vendor-agent 에 어노테이션 platform.labhub.io/reviewed=true 를 붙이세요.
  7. /root/cnpa-pol/expired.yaml 에 PolicyException old-cron-requeststeam-legacy 에 만드세요. 대상은 이름이 old-cron 인 오브젝트, 정책은 tenant-deploy-baseline, expiresAt 은 지금보다 하루 전입니다. 그다음 owner 라벨도 requests 도 없는 Deployment old-cron(이미지 registry.k8s.io/pause:3.10)을 team-legacy 에 서버 dry-run 으로 만들어 보고, 그 출력(표준 에러 포함)을 /root/cnpa-pol/expired.txt 에 저장하세요.
  8. 세 테넌트 네임스페이스의 PolicyReport 에서 tenant-deploy-baseline 결과를 세어 /root/cnpa-pol/compliance.json 에 적으세요. 키는 pass, fail, skip(개수), rate_pct(pass / (pass + fail) × 100, 소수 첫째 자리, 분모가 0 이면 100.0), exceptions(지금 만료되지 않은 PolicyException 을 네임스페이스/이름 문자열로, 정렬한 배열)입니다.

참고

정책 전의 세 팀

/root/cnpa-pol/tenants.yaml 에 세 네임스페이스와 Deployment 네 개를 작성해 적용하세요. 네임스페이스 team-pay·team-legacy·team-vendor 에는 각각 라벨 platform.labhub.io/tenantpay·legacy·vendor 로 붙입니다. Deployment 는 모두 replicas 1, 컨테이너 이름 app, 이미지 registry.k8s.io/pause:3.10, 메타데이터와 셀렉터 라벨 app.kubernetes.io/name: <이름> 입니다. team-pay/checkout 은 메타데이터 라벨 app.kubernetes.io/owner: pay 와 requests(cpu 10m, memory 16Mi)를 둘 다 가지고, team-legacy/report-gen 은 requests 만, team-legacy/batch-sync 는 owner 라벨(legacy)만, team-vendor/vendor-agent 는 둘 다 없습니다.

정책 엔진을 들이기 전의 클러스터에는 이미 규칙을 어긴 워크로드가 섞여 있습니다. 이 단계에서는 아무것도 막지 않으므로 네 개가 모두 만들어져야 합니다. 여러 오브젝트를 --- 로 이어 한 파일에 둘 수 있습니다.

막지 않고 먼저 센다

/root/cnpa-pol/policy.yaml 에 ValidatingPolicy(policies.kyverno.io/v1) tenant-deploy-baseline 을 작성해 적용하세요. validationActions: [Audit], evaluation.background.enabled: true, matchConstraints 는 apps/v1 deployments 의 CREATE·UPDATE 이고 namespaceSelector 로 라벨 platform.labhub.io/tenant 가 있는(Exists) 네임스페이스만 고릅니다. 검증 두 개를 이 순서로 둡니다. ① 메타데이터 라벨에 app.kubernetes.io/owner 가 있음, 메시지 Deployment 에 app.kubernetes.io/owner 라벨이 필요합니다 ② 모든 컨테이너에 requests 의 cpu 와 memory 가 있음, 메시지 모든 컨테이너에 cpu·memory requests 가 필요합니다.

ValidatingPolicy 의 검증은 CEL 식이고 object 가 요청된 오브젝트입니다. 키가 있는지는 '키' in 맵 으로, 없을 수도 있는 필드는 has() 로 먼저 확인합니다. 리스트의 모든 원소에 대한 조건은 .all(c, ...) 입니다. Audit 은 거절하지 않고 결과만 남기는 동작입니다. 정책의 status.conditionStatus.ready 가 true 가 될 때까지 기다리세요. 채점기는 정책이 뒤 단계에서 Deny 로 바뀐 경우도 받아들입니다.

몰랐던 위반이 보고서에 드러났다

Kyverno 가 남긴 PolicyReport 를 읽어, tenant-deploy-baseline 결과가 fail 인 Deployment 를 /root/cnpa-pol/violations.json 에 배열로 적으세요. 원소마다 namespace, name, uid(보고서의 scope.uid), message(그 결과의 메시지)를 둡니다.

백그라운드 평가는 정책이 준비된 뒤 몇 초 안에 네임스페이스마다 PolicyReport 를 만듭니다. 보고서 하나가 리소스 하나에 대응하고 scope 에 대상이, results 에 정책별 결과가 있습니다. kubectl get policyreport -A 로 요약을 보고 -o json 으로 자세히 읽으세요. 둘 다 어긴 리소스의 메시지는 먼저 실패한 검증의 것입니다.

정책을 켜자 긴급 패치가 막혔다

정책의 validationActions[Deny] 로 바꾸세요. 그다음 legacy 팀의 긴급 패치를 흉내 내어 kubectl -n team-legacy set image deploy/batch-sync app=registry.k8s.io/pause:3.9 를 실행하고 그 출력(표준 에러 포함)을 /root/cnpa-pol/blocked.txt 에 저장하세요. 이어서 kubectl -n team-legacy scale deploy/batch-sync --replicas=2 를 실행하고, /root/cnpa-pol/deny-effects.jsonimage_update_denied, scale_allowed(불리언), batch_sync_image(지금 batch-sync 의 이미지)를 적으세요.

Deny 는 새로 만드는 것뿐 아니라 이미 규칙을 어긴 오브젝트의 갱신 요청도 평가합니다. 갱신된 오브젝트 전체가 규칙을 지켜야 받아들여집니다. scale 은 Deployment 본체가 아니라 scale 하위 리소스로 가는 요청이라 규칙이 고른 리소스와 다릅니다. 이미 떠 있는 파드는 어드미션을 다시 거치지 않습니다.

규칙을 맞추자 같은 패치가 통과했다

legacy 팀의 두 Deployment 를 규칙에 맞추세요. report-gen 에 메타데이터 라벨 app.kubernetes.io/owner: legacy 를, batch-sync 의 컨테이너 app 에 requests(cpu 50m, memory 64Mi)를 넣습니다. 그다음 4단계에서 막혔던 set image ... app=registry.k8s.io/pause:3.9 를 다시 실행해 통과시키고, 두 Deployment 의 PolicyReport 결과가 pass 로 바뀔 때까지 기다리세요.

거절된 요청은 저장되지 않았으므로 먼저 규칙을 맞추는 변경이 들어가야 합니다. 그 변경 자체가 규칙을 지키는 오브젝트를 만들므로 Deny 아래에서도 받아들여집니다. requests 를 넣는 가장 짧은 방법은 kubectl set resources 입니다. 보고서는 리소스가 바뀌면 다시 평가됩니다. 이름이 리소스 uid 라서 같은 보고서가 갱신됩니다.

고칠 수 없는 외부 매니페스트에 기한부 예외

vendor-agent 는 업체가 주는 매니페스트라 당장 고칠 수 없습니다. 먼저 kyverno-admission-controller Deployment 의 인자 --enablePolicyException=false--enablePolicyException=true 로 바꾸고 롤아웃이 끝나기를 기다리세요. 그다음 /root/cnpa-pol/exception.yaml 에 PolicyException(policies.kyverno.io/v1) vendor-agent-requeststeam-vendor 네임스페이스에 만듭니다. policyRefs 는 ValidatingPolicy tenant-deploy-baseline, matchConditions 는 이름이 vendor-agent 인 오브젝트만, expiresAt 은 지금부터 30일 뒤(RFC3339, UTC)입니다. 적용 뒤 vendor-agent 에 어노테이션 platform.labhub.io/reviewed=true 를 붙이세요.

이 설치는 예외 기능이 기본으로 꺼져 있습니다. 꺼진 상태에서 예외를 만들면 경고만 나고 어드미션은 여전히 거절합니다. JSON 패치로 args 배열의 그 원소를 바꾸면 됩니다(jq 로 인덱스를 찾을 수 있습니다). 예외는 필요한 대상만 좁게 고르고 만료일을 두어야 원칙이 조용히 무너지지 않습니다. 날짜는 date -u -d '+30 days' +%Y-%m-%dT%H:%M:%SZ 로 만들 수 있습니다. 채점기는 같은 네임스페이스의 다른 이름이 여전히 거절되는지도 봅니다.

기한이 지난 예외는 아무것도 지켜 주지 않는다

/root/cnpa-pol/expired.yaml 에 PolicyException old-cron-requeststeam-legacy 에 만드세요. 대상은 이름이 old-cron 인 오브젝트, 정책은 tenant-deploy-baseline, expiresAt 은 지금보다 하루 전입니다. 그다음 owner 라벨도 requests 도 없는 Deployment old-cron(이미지 registry.k8s.io/pause:3.10)을 team-legacy 에 서버 dry-run 으로 만들어 보고, 그 출력(표준 에러 포함)을 /root/cnpa-pol/expired.txt 에 저장하세요.

만료는 예외 오브젝트가 사라지는 것이 아니라 어드미션이 그 예외를 더 이상 쓰지 않는 것입니다. 오브젝트는 남아 있어 누가 언제까지 무엇을 허락했는지 기록으로 남습니다. dry-run 은 저장하지 않지만 어드미션 웹훅은 거칩니다. 이 단계에서 old-cron 이 실제로 만들어져서는 안 됩니다.

준수율을 보고서에서 계산한다

세 테넌트 네임스페이스의 PolicyReport 에서 tenant-deploy-baseline 결과를 세어 /root/cnpa-pol/compliance.json 에 적으세요. 키는 pass, fail, skip(개수), rate_pct(pass / (pass + fail) × 100, 소수 첫째 자리, 분모가 0 이면 100.0), exceptions(지금 만료되지 않은 PolicyException 을 네임스페이스/이름 문자열로, 정렬한 배열)입니다.

예외로 건너뛴 리소스는 fail 이 아니라 skip 으로 보고됩니다. 준수율에서 skip 을 어떻게 다룰지는 조직이 정하는 일이지만, 여기서는 분모에서 뺍니다. 만료 여부는 expiresAt 을 지금 시각과 비교해 가립니다. 보고서가 늦게 갱신될 수 있으니 vendor-agent 가 skip 으로 바뀔 때까지 기다린 뒤 세세요. 채점기는 같은 보고서를 다시 셉니다.