LabHub
배우기 러닝패스 코스

KCA — Kyverno 인증 어소시에이트 · 설치·운영 — Helm 과 HA, CRD, 업그레이드, CLI, 메트릭 · 이론

복제본을 늘려도 빨라지지 않는 컨트롤러가 있다

LabHub 에서 이어서 보기

한 줄 요약

Kyverno 는 하나의 프로그램이 아니라 admission·background·reports·cleanup 네 컨트롤러가 각각 Deployment 로 도는 구조입니다. 그래서 고가용성(HA)도 컨트롤러별로 정하고, 업그레이드는 이미지 태그만 올려서는 안 되며, 정책을 클러스터에 넣기 전에 CLI 로 시험하고, 넣은 뒤에는 kyverno_* 지표로 지켜봅니다. 이 글은 [설치 방법 문서](https://kyverno.io/docs/installation/methods/), [고가용성 안내](https://kyverno.io/docs/guides/high-availability/), [업그레이드 문서](https://kyverno.io/docs/installation/upgrading/), [CLI 레퍼런스](https://kyverno.io/docs/kyverno-cli/reference/kyverno_test/), [메트릭 레퍼런스](https://kyverno.io/docs/reference/metrics/)를 한데 묶어 설명합니다.

왜 이게 필요했나

어드미션 웹훅은 기본이 fail closed 입니다. 설치 문서는 이 위험을 길게 설명합니다 — API 서버가 Kyverno 에 닿지 못하면, 정책에 걸리는 리소스를 만드는 요청은 정책을 평가할 수 없다는 이유로 실패합니다. 파드를 non-root 로 강제하는 정책 하나가 있는 클러스터에서 Kyverno 파드가 전부 죽으면 새 파드를 하나도 못 만듭니다. 그래서 운영 환경은 HA 로 설치해야 하고, Kyverno 자신의 네임스페이스는 웹훅에서 제외해 두어야 합니다(기본 설정이 kyvernokube-system 을 제외합니다).

여기에 세 가지 운영 질문이 따라옵니다. 어떤 컨트롤러를 몇 개 띄울 것인가, 새 버전으로 어떻게 올릴 것인가, 정책이 실제로 무엇을 막고 있는지를 어떻게 볼 것인가.

어떻게 동작하나

네 컨트롤러와 Helm 설치

설치 문서는 컨트롤러를 이렇게 나눕니다. admission controller 는 필수이며 API 서버의 웹훅 콜백을 받아 validate·mutate·이미지 검증·PolicyException 을 처리합니다. background controller 는 generate 와 mutate-existing 규칙을, reports controller 는 PolicyReport 를, cleanup controller 는 CleanupPolicy 를 맡습니다. 컨트롤러마다 ServiceAccount 가 따로 있어 권한이 분리되고, 기본 설치는 각각 복제본 1개입니다.

helm repo add kyverno https://kyverno.github.io/kyverno/helm repo updatehelm install kyverno kyverno/kyverno -n kyverno --create-namespace \  --set admissionController.replicas=3 \  --set backgroundController.replicas=2 \  --set cleanupController.replicas=2 \  --set reportsController.replicas=2

위는 문서의 HA 설치 예시입니다. "완전한" HA 배포로는 네 컨트롤러 모두 replicas: 3 인 values 예시도 함께 실려 있습니다. YAML 매니페스트로도 설치할 수 있는데, 태그된 릴리스의 install.yamlkubectl create -f 하는 방식이며 문서는 이 방법에서 직접 업그레이드를 지원하지 않는다고 못 박습니다. PSS(Pod Security Standards) 정책 묶음이 필요하면 별도 차트 kyverno/kyverno-policies 를 설치합니다.

복제본이 하는 일이 컨트롤러마다 다르다

HA 안내가 가장 중요하게 말하는 사실입니다. admission controller 는 웹훅 요청에 리더 선출(leader election)을 쓰지 않아 모든 복제본이 요청을 나눠 처리합니다 — 복제본이 가용성과 처리량 둘 다에 쓰입니다. 인증서와 웹훅 관리만 리더 한 개가 맡습니다. HA 로 인정되는 최소 복제본은 3개입니다. 반면 reports controllerbackground controller 는 상태를 가진 서비스라 리더 선출을 쓰고, 복제본이 몇 개든 한 개만 일합니다. 그래서 이 둘의 복제본은 가용성에만 도움이 되고, 처리량을 올리려면 복제본 수가 아니라 개별 파드의 자원(수직 확장)을 늘려야 합니다. 설치 문서의 "복제본이 많다고 모든 컨트롤러에서 성능이 높아지지는 않는다" 는 문장이 이 뜻입니다.

웹훅 자체의 설정도 알아 둘 값이 있습니다. failurePolicy 기본은 Fail 이고, 정책별로 바꾸거나 --forceFailurePolicyIgnore 로 전체를 바꿀 수 있습니다. webhookTimeout 기본은 10초(1~30초)입니다. 기본 resourceFiltersEvent, Node, 그리고 kube-system·kube-public·kube-node-lease·kyverno 네임스페이스의 리소스를 제외합니다.

CRD 의 종류

[CRD 문서](https://kyverno.io/docs/crds/)는 kubectl explain 으로 모든 타입을 볼 수 있다고 안내합니다. 업그레이드 문서의 v1.19 절에 종류가 정리되어 있습니다.

| 종류 | API 그룹/버전 | 역할 |
| --- | --- | --- |
| Policy / ClusterPolicy | kyverno.io/v1 | 전통적인 validate·mutate·generate·verifyImages 정책 |
| ValidatingPolicy 등 CEL 정책 | policies.kyverno.io/v1 | ValidatingPolicy·MutatingPolicy·GeneratingPolicy·DeletingPolicy·ImageValidatingPolicy |
| CleanupPolicy / ClusterCleanupPolicy | kyverno.io/v2 | 스케줄 기반 정리 |
| PolicyException | kyverno.io/v2(레거시) 또는 policies.kyverno.io | 예외 |
| GlobalContextEntry | kyverno.io/v2 (v2alpha1 은 deprecated) | 캐시된 외부 데이터 |
| PolicyReport / ClusterPolicyReport | wgpolicyk8s.io/v1alpha2 | 최종 보고서 |
| EphemeralReport / ClusterEphemeralReport | reports.kyverno.io/v1 | 보고서 중간 산물 |
| UpdateRequest | 내부 타입 | generate·mutate-existing 의 중간 산물 |

v1.19 부터 CRD 는 kyverno-api 라는 차트 의존성으로 관리되며 crds.install 값이 이를 켜고 끕니다. 같은 문서는 v1.19 에서 ClusterPolicy·Policy·CleanupPolicy·레거시 PolicyException 이 deprecated 되고 v1.20 에서 제거된다고 예고합니다.

업그레이드 — 태그만 올리면 안 되는 이유

업그레이드 문서의 첫 문장이 원칙입니다. 새 버전에는 CRD 를 포함해 바뀌는 지원 리소스가 많아서 이미지 태그를 올리는 것만으로는 업그레이드할 수 없습니다. 1.10 이전 버전에서 1.10 이상으로 Helm 업그레이드하려면 직접 업그레이드가 불가능하고 차트 v2→v3 이전 안내를 따라야 합니다. 마이너 버전을 건너뛴다면 그 사이 모든 마이너의 릴리스 노트를 읽어야 합니다.

v1.13 절이 좋은 예입니다. 와일드카드 view 권한이 빠져서 커스텀 리소스를 보는 mutate·generate 정책과 보고서가 영향을 받았고, 예외(PolicyException)가 기본으로 모든 네임스페이스에서 허용되던 것이 보안 문제(CVE-2024-48921)로 바뀌어 features.policyExceptions.namespace 값을 명시해야 했으며, 오래된 CRD API 버전이 제거되면서 Helm 훅이 자동으로 이전을 처리했습니다. v1.19 에서는 저장 버전(storage version) 이전을 위해 kyverno migrate --resource policyexceptions.kyverno.io 같은 CLI 명령이 추가되었습니다. 요점은 하나입니다 — 업그레이드는 코드 교체가 아니라 CRD·권한·기본값이 함께 바뀌는 사건이므로 릴리스 노트가 곧 절차서입니다.

CLI — 클러스터 없이 시험하기

Kyverno CLI 는 컨트롤러와 별개의 실행 파일이고, 레퍼런스의 --kubeconfig 설명이 "클러스터 밖에서 돌릴 때만 필요" 라고 적혀 있듯 클러스터 없이 씁니다. CI 에서는 [정책 테스트 안내](https://kyverno.io/docs/guides/testing-policies/)가 보여 주는 대로 GitHub Action kyverno/action-install-cli 로 설치합니다. 핵심 명령은 셋입니다.

kyverno apply policies/ -r resources/ 는 정책을 리소스 매니페스트에 적용해 결과를 보여 줍니다. 정책은 알고 리소스는 모르는 경우 — 개발팀의 PR 에 들어온 매니페스트를 검사할 때 — 에 씁니다.

kyverno test <디렉터리 또는 git 저장소> 는 반대로 정책과 리소스와 기대 결과를 미리 적어 둔 테스트 매니페스트(kyverno-test.yaml, -f 로 파일명 변경)와 실제 결과를 비교합니다. 기대 결과는 pass·fail·skip 이며, kyverno create test -p policy.yaml -r resource.yaml --pass 정책이름,규칙이름,리소스이름,네임스페이스,종류 로 테스트 파일을 만들 수 있습니다. --git-branch 로 원격 저장소의 브랜치를 시험하고, --test-case-selector "policy=..., rule=..., resource=..." 로 일부만 고르며, -o junit 처럼 출력 형식을 바꿉니다.

kyverno jp 는 Kyverno 의 커스텀 함수가 더해진 JMESPath 명령줄입니다. kyverno jp query -i object.yaml '식' 으로 파일에 대해 식을 평가하고, kyverno jp function 으로 함수 목록을, kyverno jp function truncate 처럼 특정 함수 설명을 보며, kyverno jp parse 로 식의 구문 트리를 봅니다. 앞 모듈에서 본 대로 kubectl get --raw ... | kyverno jp query "items | length(@)" 로 apiCall 의 결과를 미리 확인하는 것이 정석입니다.

메트릭 — 무엇을 볼 것인가

[모니터링 안내](https://kyverno.io/docs/guides/monitoring/)에 따르면 Helm 설치는 컨트롤러마다 metricsService 를 만들고 8000 포트의 /metrics 에서 지표를 냅니다. 기본 서비스 타입은 ClusterIP 라 클러스터 안의 Prometheus 만 긁을 수 있고, 밖에서 긁으려면 NodePort·LoadBalancer 로 바꿉니다. 노출 범위는 kyverno-metrics ConfigMap 으로 조절합니다 — namespaces.include/exclude(exclude 우선), 히스토그램 bucketBoundaries, 그리고 metricsExposure 에서 지표별로 끄거나(enabled: false) 라벨 차원을 떨어뜨리거나(disabledLabelDimensions) 버킷을 바꿉니다. 네임스페이스를 좁히면 메모리 사용이 눈에 띄게 줄어든다고 문서가 적습니다.

메트릭 레퍼런스가 정리한 주요 지표는 이렇습니다.

| 지표 | 종류 | 무엇을 보나 |
| --- | --- | --- |
| kyverno_policy_rule_info_total | Gauge(활성 규칙이면 1) | 지금 클러스터에 어떤 정책·규칙이 있나. policy_type(cluster/namespaced), policy_validation_mode(enforce/audit), rule_type, status_ready |
| kyverno_policy_results | Counter | 규칙 실행 결과. rule_result(PASS/FAIL), rule_execution_cause(admission_request/background_scan), resource_kind |
| kyverno_policy_execution_duration_seconds | Histogram | 규칙 하나의 실행 지연 |
| kyverno_admission_review_duration_seconds | Histogram | 요청 하나에 대한 전체 어드미션 지연(모든 정책 합산) |
| kyverno_admission_requests_total | Counter | 어드미션 요청 수와 request_allowed |

rate(kyverno_policy_results{resource_kind="Pod", rule_execution_cause="admission_request"}[1m])*60

위는 레퍼런스에 실린 질의로, 파드 요청이 일으키는 분당 규칙 실행 수입니다. 실무에서 먼저 보는 것은 두 가지입니다 — 어드미션 지연 히스토그램이 webhookTimeout 에 가까워지고 있는가, 그리고 rule_result="FAIL" 이 어떤 정책·네임스페이스에서 늘고 있는가. 전자는 fail closed 웹훅이 클러스터를 멈추기 전의 경고이고, 후자는 정책이 실제로 막고 있는 것의 목록입니다. Grafana 대시보드 JSON 은 차트 안에 들어 있으며 grafana.enabled 값으로 배포할 수 있습니다.

현장에서 만나는 모습

한 조직이 reports controller 가 느리다며 복제본을 5개로 늘렸는데 아무것도 빨라지지 않았습니다. 리더 하나만 일하기 때문입니다. 답은 복제본이 아니라 리더 파드의 CPU·메모리였고, 동시에 kyverno-metrics 의 네임스페이스 제외로 지표 부담을 줄였습니다.

업그레이드에서 흔한 사고는 Helm 으로 두 마이너를 한 번에 건너뛰면서 릴리스 노트를 읽지 않은 경우입니다. 1.13 의 권한 변경으로 커스텀 리소스를 대상으로 하던 generate 정책이 조용히 멈췄고, kyverno_policy_rule_info_totalstatus_ready="false" 로 뒤늦게 발견했습니다.

다음 퀴즈에서 확인할 것

퀴즈에서는 필수 컨트롤러가 무엇인지, 복제본이 처리량에 도움이 되는 컨트롤러와 아닌 컨트롤러, 업그레이드에서 태그만 올리면 안 되는 이유, kyverno testkyverno apply 의 차이, kyverno jp 의 용도, 그리고 kyverno_policy_results 의 라벨을 묻습니다.