KCA — Kyverno 인증 어소시에이트 · 어드미션 컨트롤과 Kyverno 구조 · 이론
요청은 어디서 걸리고, Kyverno 는 어디에 서 있나
한 줄 요약
kubectl apply 한 번은 API 서버 안에서 여섯 정거장을 지난다. 인증 → 인가 → mutating admission → 스키마 검증 → validating admission → etcd 저장이다. Kyverno 의 mutate 는 세 번째 정거장에, validate 는 다섯 번째에 선다. 이 순서를 모르면 자기 정책에 자기가 걸린다.
왜 이게 필요했나
클러스터에 규칙을 강제하는 방법은 원래 두 가지였다. 하나는 RBAC 으로 "누가 무엇을 할 수 있는가" 를 막는 것이고, 다른 하나는 리뷰로 사람이 매니페스트를 보는 것이다. 그런데 실무의 요구는 대부분 그 사이에 있다. 개발자가 Deployment 를 만드는 것 자체는 허용해야 하는데, 그 Deployment 가 리소스 리밋 없이 뜨거나 latest 태그를 쓰거나 privileged 로 도는 것은 막아야 한다. RBAC 은 동사와 리소스 종류까지만 보고 내용은 보지 않으므로 이 요구를 표현할 수 없다. 그래서 API 서버는 인가 다음에 "요청 내용을 보고 판단하는" 확장 지점을 열어 두었고, 그것이 어드미션 웹훅이다.
정거장이 순서를 갖는 데도 이유가 있다. 인증과 인가를 먼저 하는 것은 신원도 모르는 요청에 무거운 처리를 하지 않기 위해서다. mutating 이 validating 보다 앞인 것은 "고쳐 놓고 검사한다" 는 흐름이 자연스럽기 때문이다. 기본값을 채워 넣는 웹훅이 검사보다 뒤에 있으면 아무도 통과하지 못한다.
어떻게 동작하나
이 순서에서 곧바로 따라 나오는 실무 결론이 있다. validate 규칙이 보는 오브젝트는 이미 mutate 규칙이 손을 댄 뒤의 오브젝트다. 사이드카를 주입하는 mutate 정책과 모든 컨테이너에 리소스 리밋을 요구하는 validate 정책을 함께 걸어 두면, 주입된 사이드카도 리밋 검사 대상이 된다. 주입 스펙에 리밋을 넣어 두지 않았다면 배포가 막히고, 그때 로그에는 사용자가 쓰지도 않은 컨테이너 이름이 찍힌다. 사이드카 주입 정책에 리소스 리밋을 함께 넣는 것은 취향이 아니라 요구사항이다.
Kyverno 는 컨트롤러 세 개로 이뤄지고 각각 하는 일이 다르다.
| 컨트롤러 | 언제 도나 | 하는 일 |
| --- | --- | --- |
| admission | 요청이 들어올 때 실시간 | mutate 와 validate 를 웹훅에서 평가하고 응답한다 |
| background | 주기적으로, 그리고 UpdateRequest 가 생길 때 | 기존 리소스를 훑고, generate 규칙의 실제 생성과 동기화를 수행한다 |
| reports | 평가 결과가 쌓일 때 | PolicyReport / ClusterPolicyReport 오브젝트를 만들고 집계한다 |
셋이 나뉜 이유는 성격이 다르기 때문이다. admission 은 밀리초 안에 답해야 하고, background 는 몇 분이 걸려도 되며, reports 는 쓰기가 많다. 한 프로세스에 넣으면 리포트 집계가 밀릴 때 배포가 같이 밀린다. generate 규칙이 admission 에서 리소스를 직접 만들지 않고 UpdateRequest 를 남긴 뒤 background 가 처리하는 것도 같은 이유다.
마지막으로 failurePolicy 다. 기본값은 Fail 이고, 이것은 웹훅이 시간 안에 응답하지 못하면 요청을 거부한다는 뜻이다. 보안 관점에서는 옳은 기본값이지만 가용성 관점에서 뜻하는 바가 무겁다. 와일드카드로 전 리소스를 매칭하는 정책을 걸면 모든 요청이 웹훅을 타면서 클러스터 전체에 지연세를 매기게 되고, 기본 타임아웃 안에 답하지 못하는 순간 요청이 거부된다. 즉 Kyverno 가 느려지면 클러스터가 느려지는 것이 아니라 클러스터가 멈춘다. 매치를 필요한 종류와 네임스페이스로 좁히는 작업은 성능 튜닝이 아니라 가용성 작업이다. 웹훅 타임아웃 기본값은 10초이고 1에서 30초 사이만 허용된다.
현장에서 만나는 모습
저자의 홈랩은 컨트롤 플레인 3대와 GPU 워커 4대로 이뤄진 7노드 클러스터이고, CNI 는 Cilium 1.20 eBPF 로 kube-proxy 없이 돈다. 여기서 반복해서 확인한 교훈이 "상태가 Ready 인 것과 실제로 동작하는 것은 다른 명제" 라는 것이다. KubeVirt 는 컴포넌트가 전부 AllComponentsReady 였는데 VM 이 뜨지 않았고, 원인은 virt-launcher 파드 명세의 볼륨 마운트 누락이었다. Gateway API 는 CRD 를 v1.2 로 두었더니 tlsroutes 와 referencegrants 가 v1 이 아니라며 컨트롤러가 기동을 거부해 v1.6.1 로 올려야 했다.
어드미션 정책에도 똑같은 함정이 있다. 정책 오브젝트가 존재하고 상태가 정상으로 보인다고 그 정책이 실제로 요청을 타고 있다는 뜻이 아니다. 웹훅 설정이 등록되지 않았거나 match 블록이 아무것도 고르지 않으면 정책은 멀쩡히 목록에 뜨면서 아무 일도 하지 않는다. 그리고 아무것도 매칭하지 않는 정책은 잘 도는 정책과 눈으로 구분되지 않는다. 이것이 이 코스에서 반복해 강조할 진단 원칙이고, 뒤에서 배울 로컬 검증 습관이 그 답이다.
다음 퀴즈에서 확인할 것
이 모듈은 개념만 다룬다. 다음 모듈부터 ClusterPolicy 를 직접 쓰기 시작하고, 같은 규칙을 쿠버네티스 내장 ValidatingAdmissionPolicy 로도 세워 두 접근을 나란히 비교한다.