Skip to content

클라우드 네이티브

Kyverno 해설: 모던 플랫폼이 Policy-as-Code를 필요로 하는 이유

Kyverno가 실제로 무엇을 하는지, 플랫폼 팀의 부담을 어떻게 덜어주는지, 그리고 CEL 네이티브 CRD와 어드미션 아키텍처가 이를 어떻게 제공하는지에 대한 실무자 가이드.

Ivan Porta

창립자 겸 프린시펄 엔지니어

6 분 소요
#kyverno#policy-as-code#kubernetes#admission-control#cel#platform-engineering
Kyverno 해설: 모던 플랫폼이 Policy-as-Code를 필요로 하는 이유

Kyverno는 CNCF Graduation 단계에 도달한 정책 엔진으로, Kubernetes YAML과 CEL로 작성하는 정책을 통해 Kubernetes 리소스를 검증하고, 변형하고, 생성하고, 정리합니다. 새로운 정책 언어를 배울 필요가 없습니다. 어드미션 시점과 백그라운드 스캔에서 보안 및 운영 표준을 강제하는 데 사용하세요. 간단한 검증이 몇 개뿐이라면 Kubernetes 빌트인 ValidatingAdmissionPolicy만으로 충분할 수 있습니다.

Pod를 root로 실행하기. 어디에나 latest 태그로 이미지를 배포하면서 핀(pin)되지도, 서명(sign)되지도, 다이제스트로 추적되지도 않는 상황. Bash 스크립트에 의존해 새 네임스페이스에 기본 NetworkPolicy, ResourceQuota, 풀 시크릿(pull secret)을 부트스트랩하지만, 다음 업데이트에서 거의 확실히 깨지는 구조. 데모를 위해 비싼 EKS LoadBalancer를 띄워놓고 잊어버린 채로 예산을 갉아먹는 일.

플랫폼 팀은 이런 운영 부담을 수동으로 처리해 왔고, 새로운 팀이나 클러스터가 추가될 때마다 기술 부채만 쌓여 갔습니다. Kyverno는 이 모든 것을 하나의 시스템 안으로 가져와 해결합니다.

Kyverno는 오픈소스, Kubernetes 네이티브 정책 엔진입니다. YAML로, 그리고 이제는 CEL로도 정책을 작성할 수 있게 해주며, 정책은 리소스 생성 시점에 검사되고 실행 중인 워크로드에 대해 정기적으로 감사됩니다. Coinbase, LinkedIn, Spotify와 같은 기업은 물론 미국 국방부(DoD)도 프로덕션에서 Kyverno를 사용합니다. Kyverno는 2026년 3월 KubeCon Amsterdam에서 CNCF Graduation 단계에 도달했습니다.

Kyverno란 실제로 무엇인가?

Kyverno는 정책을 Kubernetes 리소스로 작성할 수 있게 해주는 오픈소스 Kubernetes 정책 엔진입니다. 룰을 작성하기 전에 Rego를 학습해야 하는 OPA나 Gatekeeper와 달리, Kyverno는 YAML을 사용하고 CEL을 지원하므로 이미 익숙한 동일한 API 객체로 작업할 수 있습니다.

개발자나 SRE는 Deployment를 작성하듯이 ValidatingPolicy를 작성하고, kubectl로 적용한 뒤, 위반 사항을 표준 Kubernetes 이벤트와 PolicyReport 리소스로 확인할 수 있습니다. 새로운 언어, 쿼리 모델, 사고방식을 배울 필요가 없습니다.

동작 방식

Kyverno는 Kubernetes의 동적 어드미션 컨트롤러 아키텍처 위에 구축되어 있으며, 그 위에 백그라운드 스캐너가 얹혀 있습니다. 정책은 리소스 라이프사이클의 세 시점에서 평가됩니다:

  1. Admission(어드미션): 개발자, GitOps 도구 또는 CI/CD 파이프라인이 리소스를 제출하면 API 서버가 어드미션 웹훅을 트리거합니다. Kyverno의 웹훅은 요청을 검사하고 일치하는 정책을 적용하며, 요청 경로에서 허용(allow), 거부(deny), 또는 변형된 리소스(mutated resource)를 반환합니다.

  2. Reconciliation(재조정): 백그라운드 컨트롤러는 존재해야 하는 리소스(클린업 정책, 생성 규칙)를 감시하고, 어드미션과는 별도의 루프에서 이를 재조정합니다.

  3. 새로운 현실: Kyverno의 리포팅 시스템은 기존 리소스를 현재 정책에 대해 정기적으로 스캔하여, 정책이 적용되기 전에 생성되었거나 이후에 변경된 워크로드의 위반 사항을 표면화합니다.

애플리케이션 코드는 그대로 유지됩니다. API 서버를 거치는 모든 리소스는 검사되고, 변형되며, 기록됩니다.

정책 유형

Kyverno의 CEL 네이티브 정책 유형은 v1.14부터 점진적으로 도입되어, v1.17에서 Namespaced 버전이 추가되며 확장되었습니다. 이로써 policies.kyverno.io 그룹은 총 11개의 CRD(클러스터 스코프 5개, 네임스페이스 스코프 5개, 그리고 PolicyException)로 구성됩니다. 각 유형은 API 서버가 정책 결과를 어떻게 처리해야 하는지 지시합니다:

  • ValidatingPolicy: CEL 표현식에 대해 리소스를 검증합니다.
apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
  name: require-team-label
spec:
  validationActions: [Deny]
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"]
  validations:
    - expression: "has(object.metadata.labels) && 'team' in object.metadata.labels"
      message: "Pods must carry a 'team' label."
  • MutatingPolicy: Kubernetes의 MutatingAdmissionPolicy와 유사하게, CEL 기반 applyConfiguration 또는 jsonPatch 표현식을 사용해 어드미션 동안 리소스를 변형합니다.
apiVersion: policies.kyverno.io/v1
kind: MutatingPolicy
metadata:
  name: default-run-as-non-root
spec:
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE"]
        resources: ["pods"]
  mutations:
    - patchType: ApplyConfiguration
      applyConfiguration:
        expression: >
          Object{
            spec: Object.spec{
              securityContext: Object.spec.securityContext{
                runAsNonRoot: true
              }
            }
          }
  • GeneratingPolicy: 트리거 리소스가 생성될 때 다운스트림 리소스를 만듭니다. 네임스페이스 기본값, 네트워크 정책, RBAC 바인딩 등이 해당됩니다.
apiVersion: policies.kyverno.io/v1
kind: GeneratingPolicy
metadata:
  name: default-deny-on-namespace
spec:
  evaluation:
    synchronize:
      enabled: true
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE"]
        resources: ["namespaces"]
  variables:
    - name: targetNs
      expression: "object.metadata.name"
    - name: downstream
      expression: >-
        [
          {
            "kind": dyn("NetworkPolicy"),
            "apiVersion": dyn("networking.k8s.io/v1"),
            "metadata": dyn({
              "name": "default-deny"
            }),
            "spec": dyn({
              "podSelector": dyn({}),
              "policyTypes": dyn(["Ingress", "Egress"])
            })
          }
        ]
  generate:
    - expression: generator.apply(variables.targetNs, variables.downstream)
  • ImageValidatingPolicy: Cosign, Notary, sigstore TUF 같은 신뢰할 수 있는 attestor에 대해 컨테이너 이미지를 검사하고, in-toto 어테스테이션을 지원합니다. SBOM 또한 어테스테이션 페이로드의 한 종류로 지원됩니다.
apiVersion: policies.kyverno.io/v1
kind: ImageValidatingPolicy
metadata:
  name: require-signed-images
spec:
  validationActions: [Deny]
  webhookConfiguration:
    timeoutSeconds: 30
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["pods"]
  matchImageReferences:
    - glob: "ghcr.io/kyverno/test-verify-image*"
  attestors:
    - name: kyvernoCosign
      cosign:
        key:
          data: |
            -----BEGIN PUBLIC KEY-----
            MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM
            5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==
            -----END PUBLIC KEY-----
  validations:
    - expression: >-
        images.containers.map(image, verifyImageSignatures(image, [attestors.kyvernoCosign])).all(e, e > 0)
      message: "Images from ghcr.io/kyverno/test-verify-image must be signed by the Kyverno project key."
  • DeletingPolicy: 정해진 스케줄에 따라 셀렉터에 일치하는 리소스를 제거합니다. 임시 환경과 정리 작업에 유용합니다.
apiVersion: policies.kyverno.io/v1
kind: DeletingPolicy
metadata:
  name: cleanup-stale-ephemeral-pods
spec:
  schedule: "0 2 * * *"
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["*"]
        resources: ["pods"]
        scope: "Namespaced"
    namespaceSelector:
      matchLabels:
        environment: test
  conditions:
    - name: olderThan48h
      expression: "time.now() - timestamp(object.metadata.creationTimestamp) > duration('48h')"
    - name: ephemeralOrFinished
      expression: >-
        (has(object.metadata.labels) && 'ephemeral' in object.metadata.labels && object.metadata.labels.ephemeral == 'true') || (has(object.status) && has(object.status.phase) && (object.status.phase == 'Succeeded' || object.status.phase == 'Failed'))

Namespaced 버전들(NamespacedValidatingPolicy, NamespacedMutatingPolicy, NamespacedGeneratingPolicy, NamespacedImageValidatingPolicy, NamespacedDeletingPolicy)은 멀티테넌시에 중요합니다. 팀은 클러스터 어드민 권한 없이 자기 네임스페이스에 정책을 만들고 관리할 수 있는 반면, 플랫폼 소유자는 모든 곳에 적용되어야 하는 정책에는 클러스터 스코프 유형을 사용합니다.

이러한 CEL 기반 정책은 Kubernetes의 ValidatingAdmissionPolicy만큼 성숙했으며, API 서버가 더 많은 작업을 처리합니다. Kyverno는 정책 엔진, 리포팅, 예외, 생성, 이미지 검증과 같은 VAP에 포함되지 않은 기능을 추가합니다.

기존 ClusterPolicyCleanupPolicy 유형은 아직 동작하지만 단계적으로 폐지되고 있습니다. Kyverno 1.17에서 deprecated로 표시되었으며 2026년 후반에 제거될 예정입니다. 곧 CEL 네이티브 CRD로의 마이그레이션을 계획하고, 특별한 이유가 없는 한 새 정책은 이 유형들로 작성하세요.

언제 ValidatingAdmissionPolicy를 대신 사용해야 할까?

Kubernetes 1.30 이후에는 별도 컨트롤러 없이 사용할 수 있는 빌트인 CEL 검증기인 ValidatingAdmissionPolicy가 포함되어 있습니다. 일부 팀에게는 이것으로 충분하지만, 대부분의 팀에게는 그렇지 않습니다.

주요 기능어드미션 시 동기 검증검증, 변형, 생성, 삭제
적용 시점어드미션만어드미션, 백그라운드 스캔, 파이프라인, CLI
페이로드Kubernetes 객체Kubernetes 객체 외 모든 JSON/YAML
CEL 라이브러리기본확장(HTTP 호출, 이미지 검증, Kubernetes 룩업)
외부 데이터Kubernetes 리소스 또는 HTTP 호출
정책 바인딩수동자동
백그라운드 스캔주기적, 정책 변경 시에도 실행
리포팅 및 감사PolicyReport + EphemeralReport CRD
예외 처리세분화된 PolicyException
이미지 서명 검증
자동 생성Pod 컨트롤러, ValidatingAdmissionPolicy
테스트Kyverno CLI(단위), Chainsaw(e2e)
아키텍처API 서버에 내장(컨트롤러 불필요)Kyverno 컨트롤러 필요

어드미션 시점의 리소스 검증만 필요하다면 VAP가 더 가벼운 옵션이며 올바른 선택입니다. 더 많은 기능이 필요하다면 정책 엔진을 원하게 되는데, Kyverno는 YAML로 정책을 작성하고 이를 코드로 다루는 것 사이의 가장 가까운 매칭을 제공합니다.

운영 현실

  • 설치가 아닌 롤아웃: Kyverno는 audit 모드로 시작하는 것을 권장합니다. CEL 네이티브 유형에는 validationActions: [Audit]를, 레거시 ClusterPolicy에는 failureAction: Audit을 사용하세요(레거시 Enforce는 Deny에 해당). deny 정책을 곧바로 배포하는 것은 피해야 합니다. 컨트롤러의 ServiceAccount가 라벨 요구사항을 충족하지 못하면 컨트롤러를 망가뜨릴 수 있기 때문입니다. 최소 일주일 동안 audit 모드로 운영하고, PolicyReport 리소스를 검토하며, 위반 사항을 해결한 후에 deny로 전환하세요.

  • 변형(Mutation)은 보안 통제가 아니다: MutatingPolicy는 기본값 설정, 이미지 풀 시크릿, 리소스 요청, 표준 라벨 적용에 잘 맞지만, 검증의 대체재가 아닙니다. Pod 스펙을 제출할 수 있는 사용자는 어드미션 순서나 예외 때문에 변형이 처리하지 못하는 스펙을 제출할 수 있습니다. 항상 참이어야 하는 것은 ValidatingPolicy로 처리해야 합니다.

실용적인 권장 사항

Kubernetes 플랫폼을 위한 정책 엔진을 평가하고 있고 OPA/Gatekeeper를 선택할 강한 이유가 아직 없다면, 먼저 Kyverno를 파일럿하세요. 설치는 한나절이면 끝납니다. 필요한 기능을 제공한다면 한 분기 분의 운영 작업을 절약한 셈입니다. 그렇지 않더라도 다른 곳으로 갈 명확하고 문서화된 이유를 갖게 됩니다.

FAQ

Kyverno에 대해 가장 자주 받는 질문 세 가지.

Kyverno란 무엇이며, Kubernetes 어드미션 흐름의 어디에 위치하나요?

Kyverno는 어드미션 컨트롤러로 동작하는 Kubernetes 네이티브 정책 엔진입니다. 웹훅을 통해 모든 API 요청을 가로채어 리소스가 영속화되기 전에 검증, 변형, 또는 생성을 수행합니다.

레거시 ClusterPolicy로 시작해야 할까요, 아니면 새로운 CEL 기반 정책 유형으로 시작해야 할까요?

새로운 작업에는 CEL 기반 유형(ValidatingPolicy, MutatingPolicy, GeneratingPolicy, ImageValidatingPolicy)을 사용하세요. ClusterPolicy도 여전히 동작하지만 Kyverno 1.17부터 deprecated 상태입니다.

Audit과 Enforce는 어떻게 다르며, 백그라운드 스캔은 무엇을 하나요?

Deny(레거시 ClusterPolicy에서는 Enforce라고 부름)는 어드미션에서 실패하는 요청을 거부합니다. Audit은 요청을 허용하지만 실패를 기록합니다. 백그라운드 스캔은 정해진 일정에 따라 기존 리소스를 재평가하고 결과를 EphemeralReport 중간 산출물에 기록합니다. Kyverno의 reports 컨트롤러는 이를 운영자가 사용하는 PolicyReport 리소스로 집계합니다.

이 사이트는 분석을 위해 쿠키를 사용합니다.