본문 바로가기
K8S

[Kubernetes]Kyverno 학습 가이드 (처음 시작하는 사람용)

by Rainbound-IT 2026. 4. 20.
반응형
 

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. 다음 단계 학습 자료

공식 문서

실습 예제

심화 주제

커뮤니티

 

반응형

댓글