1. Kyverno가 뭔가? (한 줄 답)
Kubernetes 클러스터에 "이런 리소스는 허용/금지"를 YAML로 선언하는 경찰관
- 누군가 kubectl apply 하거나 ArgoCD가 sync 하거나 Helm이 install 할 때
- Kyverno가 "이거 우리 규칙에 맞나?" 검사하고
- 안 맞으면 거부, 맞으면 통과, 필요하면 자동 수정
2. 사전 지식: Admission Controller
Kyverno를 이해하려면 먼저 k8s가 리소스를 받는 과정을 알아야 합니다.
k8s 요청 흐름
사용자 kubectl apply
↓
kube-apiserver (API 서버)
↓
1. Authentication (너 누구?)
↓
2. Authorization (너 이거 할 권한 있어? — RBAC)
↓
3. Admission Control ← 🔥 Kyverno가 여기 끼어듦
↓
4. etcd에 저장
↓
5. 실제 리소스 생성 (Pod, Ingress 등)
Admission Control의 두 종류
| 종류 | 역할 | 예시 |
| Mutating | 요청 내용을 수정 | Pod에 기본 라벨 자동 추가 |
| Validating | 요청을 검증 후 허용/거부 | 금지된 annotation 차단 |
Kyverno는 두 가지 다 됩니다. (VAP는 Validating만)
Webhook 방식
Kyverno는 자기 자신을 Admission Webhook으로 kube-apiserver에 등록합니다.
apiserver: "나 Ingress 받았어, Kyverno야 검사해줘"
Kyverno: "음... policy 평가 중... 통과!"
apiserver: "OK, etcd에 저장"
→ Kyverno가 죽으면 admission이 막힐 수 있음 (그래서 HA 구성이 중요)
3. Kyverno의 4가지 능력
Kyverno 정책은 rule 단위로 작성하는데, 각 rule은 4가지 중 하나.
3.1 Validate (검증) — 가장 많이 씀
"이 리소스가 규칙에 맞으면 통과, 아니면 거부"
validate:
message: "container는 반드시 resources.limits를 설정해야 함"
pattern:
spec:
containers:
- resources:
limits:
memory: "?*" # 아무 값이나 있어야 함
3.2 Mutate (수정)
"리소스를 받으면 이걸 자동으로 추가/수정"
mutate:
patchStrategicMerge:
metadata:
labels:
team: "platform" # 모든 Pod에 team 라벨 자동 주입
3.3 Generate (생성)
"A가 만들어지면 B도 자동으로 만들어라"
generate:
apiVersion: v1
kind: ResourceQuota
# 새 namespace 생성되면 기본 ResourceQuota 자동 생성
3.4 VerifyImages (이미지 서명 검증)
"cosign 서명된 이미지만 허용"
verifyImages:
- imageReferences:
- "*"
attestors:
- entries:
- keys:
publicKeys: "..."
우리 사고 방지용은 전부 Validate 유형입니다.
4. 핵심 리소스: ClusterPolicy vs Policy
Kyverno에서 정책 자체가 CRD(Custom Resource) 입니다.
| 리소스 | 범위 | 언제 씀 |
| ClusterPolicy | 클러스터 전체 | 대부분 이걸 씀 |
| Policy | 특정 namespace만 | 팀별/앱별 격리 필요 시 |
이번 ALB 정책은 어느 namespace든 위반하면 안 되니까 ClusterPolicy 사용.
5. 정책 YAML 해부학
실제 정책 하나를 뜯어봅시다:
apiVersion: kyverno.io/v1 # ① Kyverno API 버전
kind: ClusterPolicy # ② 정책 종류
metadata:
name: require-pod-resources # ③ 정책 이름
spec:
validationFailureAction: Enforce # ④ 위반 시 행동: Enforce(차단) | Audit(경고만)
background: true # ⑤ 기존 리소스도 주기적 검사할지
rules: # ⑥ 규칙 목록 (여러 개 가능)
- name: check-container-resources # ⑥-1 규칙 이름
match: # ⑥-2 어떤 리소스에 적용할지
any:
- resources:
kinds: [Pod]
validate: # ⑥-3 검증 내용
message: "resources.limits 필수"
pattern: # ⑥-4 리소스가 이 모양이어야 함
spec:
containers:
- resources:
limits:
memory: "?*"
핵심 필드 설명
| 필드 | 의미 |
| validationFailureAction | Enforce = 위반 시 거부 / Audit = 허용하되 리포트 |
| background | true면 이미 배포된 리소스도 주기적 검사해서 리포트 생성 |
| match | 이 정책이 어떤 리소스에 적용되는지 (kind, namespace, label 등) |
| exclude | match에서 제외할 대상 |
| preconditions | 추가 조건 (annotation 값에 따라 적용 여부 결정 등) |
| validate.pattern | 리소스가 반드시 이 모양이어야 함 |
| validate.message | 위반 시 사용자에게 보여줄 메시지 |
6. 패턴 매칭 문법 (anchors)
Kyverno의 가장 헷갈리는 부분. YAML에 특수 기호로 조건을 표현합니다.
| 기호 | 이름 | 의미 |
| ?* | 아무 값 | "이 key가 있고 값은 뭐든 OK" |
| !value | 부정 | "이 값이 아니어야 함" |
| (key) | 조건 anchor | "만약 이 key가 있다면 ~" |
| =(key) | 존재 anchor | "이 key가 있을 때만 검사" |
| X(key) | 부재 anchor | "이 key가 없어야 함" |
| ^(key) | 글로벌 anchor | "이 조건 중 하나라도 맞으면" |
| +(key) | 추가 anchor | "없으면 추가 (mutate)" |
예제 1: 필수 key
pattern:
metadata:
labels:
team: "?*" # team 라벨은 반드시 있어야 함 (값은 뭐든)
예제 2: 금지 key
pattern:
metadata:
=(annotations):
X(alb.ingress.kubernetes.io/subnets): "null"
# annotations가 있을 때, subnets key는 없어야 함
예제 3: 값 제한
pattern:
spec:
containers:
- image: "!*:latest" # latest 태그 금지
예제 4: 여러 허용 값
pattern:
metadata:
annotations:
alb.ingress.kubernetes.io/scheme: "internal | internet-facing"
초보자 팁: 처음에는 ?*, X(key), pipe(|)로 여러 값 허용 3가지만 알아도 80% 커버 가능.
7. 설치
7.1 Helm으로 설치 (권장)
# 1. Helm repo 추가
helm repo add kyverno <https://kyverno.github.io/kyverno/>
helm repo update
# 2. 네임스페이스 생성 + 설치
helm install kyverno kyverno/kyverno \\
-n kyverno --create-namespace \\
--set admissionController.replicas=3 \\
--set backgroundController.replicas=2 \\
--set cleanupController.replicas=2 \\
--set reportsController.replicas=2
# 3. 설치 확인
kubectl get pods -n kyverno
# NAME READY STATUS
# kyverno-admission-controller-xxx 1/1 Running
# kyverno-background-controller-xxx 1/1 Running
# kyverno-cleanup-controller-xxx 1/1 Running
# kyverno-reports-controller-xxx 1/1 Running
7.2 컴포넌트 역할
| 컴포넌트 | 역할 |
| admission-controller | webhook으로 들어오는 요청 검증 (핵심) |
| background-controller | 이미 배포된 리소스 주기 검사 (Mutate/Generate) |
| reports-controller | PolicyReport 생성 |
| cleanup-controller | CleanupPolicy 실행 (기간 만료 리소스 삭제) |
7.3 CLI 설치 (로컬 테스트용)
# Windows
choco install kyverno
# 또는 직접 다운로드
# <https://github.com/kyverno/kyverno/releases>
# 확인
kyverno version
8. 첫 정책 작성 - Hello Kyverno
실습: 모든 Pod은 team 라벨 필수
Step 1: 정책 파일 require-team-label.yaml 작성
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-team-label
spec:
validationFailureAction: Audit # 일단 Audit으로 시작
background: true
rules:
- name: check-team-label
match:
any:
- resources:
kinds: [Pod]
validate:
message: "모든 Pod에 team 라벨 필수"
pattern:
metadata:
labels:
team: "?*"
Step 2: 적용
kubectl apply -f require-team-label.yaml
Step 3: 위반 Pod 시도
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- name: nginx
image: nginx
EOF
# Audit 모드라 생성은 됨, 리포트만 생성
Step 4: 리포트 확인
kubectl get policyreport -A
# NAME PASS FAIL WARN
# cpol-require-team-label 0 1 0
Step 5: Enforce로 전환
kubectl patch clusterpolicy require-team-label \\
--type merge -p '{"spec":{"validationFailureAction":"Enforce"}}'
# 다시 시도 → 거부됨
# Error from server: admission webhook "validate.kyverno.svc-fail" denied the request:
# resource Pod/default/test-pod was blocked due to the following policies:
# require-team-label: 모든 Pod에 team 라벨 필수
축하합니다. 첫 정책 완성.
9. 이번 ALB 정책 단계별 설명
9.1 Policy 1: internal shared-alb에 subnets annotation 금지
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: alb-no-subnets-on-internal # ① 정책 이름
spec:
validationFailureAction: Enforce # ② 위반 시 차단
background: true # ③ 기존 리소스도 검사
rules:
- name: block-subnets-annotation
match: # ④ Ingress 리소스에만 적용
any:
- resources:
kinds: [Ingress]
preconditions: # ⑤ 사전 조건: shared-alb 그룹일 때만
all:
- key: "{{ request.object.metadata.annotations.\\"alb.ingress.kubernetes.io/group.name\\" || '' }}"
operator: Equals
value: shared-alb
validate:
message: >-
internal shared-alb 그룹 ingress에
alb.ingress.kubernetes.io/subnets annotation 사용 금지 pattern:
metadata:
=(annotations): # ⑥ annotations이 있으면
X(alb.ingress.kubernetes.io/subnets): "null" # ⑦ subnets key는 없어야 함
한 줄씩 설명
- ①: 정책 이름 (kubectl get으로 보일 이름)
- ②: Enforce = 위반하면 리소스 생성 거부 (Audit은 허용+리포트)
- ③: 이미 배포된 Ingress도 주기적으로 검사해서 리포트 생성
- ④: 이 정책은 Ingress 리소스에만 적용 (Pod, Service 등은 무시)
- ⑤: group.name이 정확히 shared-alb일 때만 검사 (public-shared-alb는 무시)
- {{ ... }} 는 JMESPath/변수 참조
- || '' 는 annotation 없을 때 빈 문자열로 처리
- ⑥: =(annotations) = annotations 필드가 존재한다는 가정 하에
- ⑦: X(key) = 이 key는 없어야 한다 (있으면 위반)
9.2 preconditions 동작 예시
| 시나리오 | group.name | 정책 적용 |
| cs-api (internal) | shared-alb | ✅ 검사 |
| ecount-api (public) | public-shared-alb | ❌ 검사 안 함 (preconditions 탈락) |
| group.name 없음 | (없음) | ❌ 검사 안 함 |
→ public ALB는 subnet annotation 필요하니까 검사에서 제외하는 로직.
9.3 Policy 2: scheme과 group.name 조합 강제
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: alb-scheme-group-consistency
spec:
validationFailureAction: Enforce
rules:
# 규칙 1: shared-alb는 internal 이어야 함
- name: shared-alb-must-be-internal
match:
any:
- resources:
kinds: [Ingress]
preconditions:
all:
- key: "{{ request.object.metadata.annotations.\\"alb.ingress.kubernetes.io/group.name\\" || '' }}"
operator: Equals
value: shared-alb
validate:
message: "shared-alb 그룹은 scheme=internal 만 허용"
pattern:
metadata:
annotations:
alb.ingress.kubernetes.io/scheme: internal
# 규칙 2: public-shared-alb는 internet-facing 이어야 함
- name: public-shared-alb-must-be-internet-facing
match:
any:
- resources:
kinds: [Ingress]
preconditions:
all:
- key: "{{ request.object.metadata.annotations.\\"alb.ingress.kubernetes.io/group.name\\" || '' }}"
operator: Equals
value: public-shared-alb
validate:
message: "public-shared-alb 그룹은 scheme=internet-facing 만 허용"
pattern:
metadata:
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
9.4 Policy 3: ssl-redirect 금지
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: alb-no-ssl-redirect-on-shared
spec:
validationFailureAction: Enforce
rules:
- name: block-ssl-redirect
match:
any:
- resources:
kinds: [Ingress]
preconditions:
any: # ← any (shared-alb 또는 public-shared-alb)
- key: "{{ request.object.metadata.annotations.\\"alb.ingress.kubernetes.io/group.name\\" || '' }}"
operator: AnyIn
value: [shared-alb, public-shared-alb]
validate:
message: "shared ALB에 ssl-redirect annotation 금지 (그룹 전체 영향)"
pattern:
metadata:
=(annotations):
X(alb.ingress.kubernetes.io/ssl-redirect): "null"
10. Audit vs Enforce 모드
Audit 모드 (처음 배포 시 권장)
validationFailureAction: Audit
- 위반해도 리소스 생성됨
- PolicyReport에 "이거 위반이야" 기록만
- 장점: 운영 중단 없이 현황 파악
- 용도: 신규 정책 안정화 기간 (1~2주)
Enforce 모드 (안정화 후)
validationFailureAction: Enforce
- 위반하면 리소스 생성 거부
- 사용자에게 에러 메시지 반환
- 용도: 정식 차단
전환 방법
# Audit → Enforce
kubectl patch clusterpolicy alb-no-subnets-on-internal \\
--type merge -p '{"spec":{"validationFailureAction":"Enforce"}}'
# 긴급 시 Enforce → Audit (차단 해제)
kubectl patch clusterpolicy alb-no-subnets-on-internal \\
--type merge -p '{"spec":{"validationFailureAction":"Audit"}}'
11. PolicyReport — 위반 현황 보기
Kyverno는 위반 현황을 PolicyReport CRD로 자동 생성합니다.
기본 조회
# 전체 리포트
kubectl get policyreport -A
# 클러스터 단위 리포트
kubectl get clusterpolicyreport
# 상세 보기
kubectl describe policyreport cpol-alb-no-subnets-on-internal -n default
출력 예시
NAME PASS FAIL WARN ERROR SKIP
cpol-alb-no-subnets-on-internal 15 2 0 0 0
Policy Reporter UI (선택)
웹 대시보드로 보고 싶으면 별도 설치:
helm repo add policy-reporter <https://kyverno.github.io/policy-reporter>
helm install policy-reporter policy-reporter/policy-reporter \\
-n policy-reporter --create-namespace \\
--set ui.enabled=true
12. 테스트 방법
12.1 CLI로 로컬 테스트 (배포 전 필수)
# 정책 파일 작성 후
kyverno apply ./policies/alb-no-subnets.yaml --resource ./test-ingress-bad.yaml
# 출력 예시:
# Applying 1 policy rule(s) to 1 resource(s)
# policy alb-no-subnets-on-internal -> resource default/Ingress/cs-api-ingress failed
# 1. block-subnets-annotation: internal shared-alb 그룹 ingress에...
# pass: 0, fail: 1, warn: 0, error: 0, skip: 0
12.2 테스트 리소스 예시
나쁜 ingress (위반)
# test-ingress-bad.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: bad-ingress
namespace: default
annotations:
alb.ingress.kubernetes.io/group.name: shared-alb
alb.ingress.kubernetes.io/subnets: subnet-xxx,subnet-yyy # ← 위반!
spec:
ingressClassName: alb
rules:
- host: test.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: test-svc
port:
number: 80
좋은 ingress (통과)
# test-ingress-good.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: good-ingress
namespace: default
annotations:
alb.ingress.kubernetes.io/group.name: shared-alb
alb.ingress.kubernetes.io/scheme: internal
# subnets annotation 없음 → OK
spec:
ingressClassName: alb
rules:
- host: test.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: test-svc
port:
number: 80
12.3 클러스터 dry-run 테스트
kubectl apply -f test-ingress-bad.yaml --dry-run=server
# 출력:
# Error from server: admission webhook "validate.kyverno.svc-fail" denied the request:
# resource Ingress/default/bad-ingress was blocked due to the following policies:
# alb-no-subnets-on-internal:
# block-subnets-annotation: internal shared-alb 그룹 ingress에 ...
12.4 Kyverno Test CRD (회귀 테스트)
정책 변경 시 깨지지 않게 CI에 통합:
# kyverno-test.yaml
apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
name: alb-policies-test
policies:
- ../policies/alb-no-subnets.yaml
resources:
- test-ingress-bad.yaml
- test-ingress-good.yaml
results:
- policy: alb-no-subnets-on-internal
rule: block-subnets-annotation
resource: bad-ingress
kind: Ingress
result: fail
- policy: alb-no-subnets-on-internal
rule: block-subnets-annotation
resource: good-ingress
kind: Ingress
result: pass
kyverno test .
13. 운영 시 주의사항
13.1 failurePolicy 설정
Kyverno 자체가 죽었을 때 동작:
# Helm values
admissionController:
webhookFailurePolicy: Fail # Kyverno 죽으면 admission 차단 (안전)
# webhookFailurePolicy: Ignore # Kyverno 죽으면 그냥 통과 (가용성)
권장: Fail + HA 3 replicas
13.2 kyverno 네임스페이스 제외
Kyverno 자체 리소스에 정책 적용되면 순환 참조. 기본으로 제외됨:
exclude:
any:
- resources:
namespaces: ["kyverno", "kube-system"]
13.3 기존 리소스 처리
background: true
- 배포된 리소스도 주기 검사 → PolicyReport 생성
- 하지만 기존 리소스를 강제로 거부하진 않음 (update 시점에만 검증)
- 현황 파악용으로 매우 유용
13.4 정책 비활성화 (응급)
# 특정 정책 비활성화 (삭제 안 하고)
kubectl patch cpol alb-no-subnets-on-internal \\
--type merge -p '{"spec":{"validationFailureAction":"Audit"}}'
# 또는 완전 삭제
kubectl delete cpol alb-no-subnets-on-internal
13.5 Policy Exceptions (예외 처리)
특정 리소스만 예외 허용:
apiVersion: kyverno.io/v2
kind: PolicyException
metadata:
name: legacy-ingress-exception
namespace: legacy-app
spec:
exceptions:
- policyName: alb-no-subnets-on-internal
ruleNames: [block-subnets-annotation]
match:
any:
- resources:
kinds: [Ingress]
names: [legacy-service]
namespaces: [legacy-app]
14. 자주 쓰는 명령어 치트시트
# 정책 목록
kubectl get clusterpolicy
kubectl get policy -A
# 정책 상세
kubectl describe cpol <name>
# 리포트 확인
kubectl get policyreport -A
kubectl get clusterpolicyreport
# 특정 리포트 상세
kubectl describe polr <name> -n <namespace>
# Kyverno pod 로그
kubectl logs -n kyverno -l app.kubernetes.io/component=admission-controller
# 정책 메트릭 (Prometheus)
kubectl port-forward -n kyverno svc/kyverno-svc-metrics 8000:8000
# → <http://localhost:8000/metrics>
# CLI 테스트
kyverno apply <policy.yaml> --resource <resource.yaml>
kyverno test .
# 일시 Audit 전환
kubectl patch cpol <name> --type merge -p '{"spec":{"validationFailureAction":"Audit"}}'
# 정책 삭제
kubectl delete cpol <name>
15. 다음 단계 학습 자료
공식 문서
- Kyverno 공식 가이드 — 전체 개념
- Writing Policies — 정책 작성법
- Policy Library — 200+ 검증된 정책 샘플 (꿀)
실습 예제
- Kyverno Playground — 브라우저에서 정책 테스트
- Pod Security Standards 정책 — 복사해 쓸만한 정책들
심화 주제
- JMESPath 문법 — {{ request.object... }} 변수 참조
- CEL in Kyverno — Kyverno 1.11+ CEL 지원
- Mutation 실전 — default 값 주입
- Generation 실전 — namespace 기반 리소스 자동 생성
커뮤니티
- Kyverno Slack: https://slack.cncf.io/ → #kyverno 채널
- GitHub Discussions: https://github.com/kyverno/kyverno/discussions
'K8S' 카테고리의 다른 글
| ArgoCD의 timeout.hard.reconciliation 이란? (0) | 2026.05.26 |
|---|---|
| Kubernetes AWS ALB/NLB 구성 yaml (0) | 2026.01.26 |
| Kubernetes hostNetwork & externalTrafficPolicy - AWS ALB, NLB 에서 사용하는가? (0) | 2026.01.26 |
| Kubernetes Windows 지원: 기능 비교와 스케줄링 방식 정리 (0) | 2026.01.23 |
| 쿠버네티스 캐스케이딩 삭제(Cascading Deletion) (0) | 2026.01.22 |
댓글