Kyverno

 

개요

Kyverno 운영 가이드입니다.

쿠버네티스 클러스터를 운영하고 보안을 관리하는 DevOps Engineer, SRE 그리고 클라우드 보안 엔지니어를 대상으로 작성된 문서입니다.

 

배경지식

Kyverno

Kyverno는 Kubernetes용으로 설계된 정책 엔진입니다.

Kyverno의 정책은 ClusterPolicy라고 하는 Kubernetes 리소스(CRD)로 관리됩니다. Kyverno는 OPA와 달리 정책을 작성하는 데 새로운 프로그래밍 언어가 필요하지 않습니다.

 

Admission Controllers

쿠버네티스에서 사용할 수 있는 대표적인 Admission Controllers 3가지

대표적인 3가지 Kuberntes Admission Controller 비교

OPA와 Kyverno 둘다 사용해본 AWS Solutions Architect의 경우, OPA 보다 정책 작성이 쉬운 Kyverno를 적극 추천했습니다.

 

Kyverno 운영 가이드

버전 호환성 표

쿠버네티스 클러스터 버전에 맞는 Kyverno 버전을 설치해야 합니다.

Kyverno VersionKubernetes MinKubernetes Max
1.6.x1.161.23
1.7.x1.211.23
1.8.x1.231.25
1.9.x1.241.26
1.10.x1.241.26
1.11.x1.251.28
1.12.x1.261.29

자세한 사항은 Kyverno 공식문서 Compatibility Matrix 페이지를 참고합니다.

 

정책 마켓플레이스

모든 Policy의 YAML을 처음부터 작성할 필요 없이 약 292개의 정책이 이미 올라와 있습니다.

Kyverno Policies
Github: kyverno/policies

 

정책 타입

Kyverno는 쿠버네티스 리소스를 검증, 변경, 생성 및 정리하고 컨테이너 이미지 검증을 수행할 수 있는 정책을 생성합니다.

Kyverno 주요 룰 종류

총 5개 타입의 룰을 사용할 수 있습니다.

Kyverno 정책 작성 방법은 Kyverno 공식문서 Writing Policies를 참고합니다.

 

정책 모드

Validate Policy는 2개의 모드 중 하나로 동작합니다.

요약하자면, Enforce 모드는 실제로 정책을 리소스에 적용하여 적절한 변경을 강제화하는 반면, Audit 모드는 정책의 영향을 사전에 확인하고 분석하기 위해 사용되는 모드입니다.

 

ClusterPolicy 리소스에서는 spec.validationFailureAction에서 설정할 수 있습니다.

apiVersion: kyverno.io/v1
# The `ClusterPolicy` kind applies to the entire cluster.
kind: ClusterPolicy
metadata:
  name: require-ns-purpose-label
# The `spec` defines properties of the policy.
spec:
  # The `validationFailureAction` tells Kyverno if the resource being validated should be allowed but reported (`Audit`) or blocked (`Enforce`).
  validationFailureAction: Enforce # or `Audit`

 

Kyverno 컨트롤러

Kyverno는 4개의 컨트롤러 파드로 구성됩니다.
각 컨트롤러 파드별 역할은 공식문서 Controllers in kyverno를 참고합니다.

Kyverno 파드 아키텍처

 

고가용성

kyverno 헬름 차트는 각 컨트롤러에 대해 여러 개의 파드를 구성하기 위한 설정값을 제공합니다. 공식문서에서 권장하는 고가용성 설정은 다음과 같습니다.

admissionController.replicas: 3
backgroundController.replicas: 2
cleanupController.replicas: 2
reportsController.replicas: 2

Note
Kyverno 설치시 반드시 Admission Controller Pod가 포함되어 있어야 합니다. Admission Controller의 replicas 파드 개수는 2개를 지원하지 않습니다. 고가용성 구성으로 Kyverno를 설치해야하는 경우, Admission Controller가 지원하는 유일한 replicas 개수는 3개입니다.

자세한 사항은 Kyverno 공식문서 High Availability를 참고합니다.

 

설치방식

필요 helm chart

Kyverno를 운영하려면 2개의 helm chart 설치가 필요합니다.

Kyverno는 설치 방식Installation method으로 오퍼레이터 패턴을 지원하지 않으며, 헬름 차트와 YAML 직접 설치 방식만 지원하고 있습니다.

 

설치 순서

기본적으로 Kyverno 정책 엔진인 kyverno chart를 먼저 설치한 다음, 정책 리소스 모음인 kyvenro-policies chart를 설치합니다.

helm chart 레포 구조를 잡는 방법에는 명확한 정답이나 모범 사례는 없습니다. 제 경우는 git clone을 사용해서 kyvernokyverno-policies 공식 차트들을 다운로드 받고 helm install을 사용해 설치해 운영하는 걸 선호합니다.

아래는 charts 레포에 구성해놓은 디렉토리 트리 구조입니다.

charts
├── kyverno
│   ├── values_dev.yaml
│   ├── values_qa.yaml
│   ├── values_prd.yaml
│   ├── Chart.yaml
│   └── templates/
└── kyverno-policies
    ├── values_dev.yaml
    ├── values_qa.yaml
    ├── values_prd.yaml
    ├── Chart.yaml
    └── templates/

하나의 차트는 환경별로 values 파일이 분리되어 환경마다 자유롭게 차트 설정을 조정할 수 있습니다.

 

헬름 차트 설치 과정을 그림으로 표현하면 다음과 같습니다.

helm 차트 설치 과정

 

정책 운영 팁

Kyverno를 운영하면서 가장 높은 비중을 차지하는 정책 타입은 리소스 YAML로부터 설정을 검증하는 Validate Policy 입니다. Validate Policy를 작성하고 적용하는 과정에서 유용하게 쓸 수 있는 몇 가지 팁을 소개합니다.

Validate Rule

자세한 사항은 Kyverno 공식문서 Tips & Tricks를 참고합니다.

 

Policy 작성 가이드

Context

변수는 Kyverno Rule의 context 키워드에서 정의될 수 있습니다.

변수는 정적 값, 다른 변수 또는 중첩된 오브젝트 등으로 정의될 수 있습니다. 아래 정책에서는 값이 foo인 컨텍스트 변수를 설정합니다.

    context:
    - name: foodata
      variable:
        value: "foo"

 

이번 코드 조각은 컨텍스트 변수를 request.object.metadata.name 값으로 설정합니다. 값 필드가 정의되지 않은 경우 JMESPath의 내용은 전체 컨텍스트에 적용됩니다.

    context:
    - name: preStopSleepSeconds
      variable:
        jmesPath: request.object.metadata.labels.preStopSleepSeconds
        default: 30

 

Context와 JMESPath의 여러 함수가 적용된 Mutate Policy 예시입니다.

{{- $name := "mutate-pre-post-scripts-injection" }}
{{- if eq (include "kyverno-policies.customMutatePolicies" (merge (dict "name" $name) .)) "true" }}
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: {{ $name }}
  annotations:
    policies.kyverno.io/title: Mutate termination grace period seconds
    policies.kyverno.io/category: Other, EKS Best Practices
    policies.kyverno.io/subject: Pod
    kyverno.io/kyverno-version: 1.8.0-rc2
    kyverno.io/kubernetes-version: "1.24"
    policies.kyverno.io/minversion: 1.6.0
    policies.kyverno.io/description: >-
      Mutate policy for preStop script sleep.      
spec:
  schemaValidation: false
  rules:
  - name: mutate-poststart-script-raptor-config-curl
    match:
      any:
      - resources:
          kinds:
          - Pod
          operations:
          - CREATE
          namespaceSelector:
            matchLabels:
              policies.kyverno.io/test-mutate-rules-injection: enabled
    exclude:
      any:
      - resources:
          kinds:
          - Pod
          namespaces:
          - default
          selector:
            matchLabels:
              role: spring-app
      - resources:
          kinds:
          - Pod
          namespaces:
          - default
          selector:
            matchLabels:
              policies.kyverno.io/test-mutate-rules-injection: disabled
    context:
    - name: postStartSleepSeconds
      variable:
        jmesPath: request.object.metadata.labels.postStartSleepSeconds
        default: 5
    mutate:
      patchesJson6902: |-
        - op: add
          path: "/spec/containers/0/lifecycle/postStart/exec/command"
          value:
          - "/bin/sh"
          - "-c"
          - "if ! curl -s http://raptor-config.default > /dev/null; then sleep {{ `{{ postStartSleepSeconds }}` }}; exit 1; fi"
                  
  - name: mutate-prestop-script-sleep-test
    match:
      any:
      - resources:
          kinds:
          - Pod
          operations:
          - CREATE
          namespaceSelector:
            matchLabels:
              policies.kyverno.io/test-mutate-rules-injection: enabled
    exclude:
      any:
      - resources:
          kinds:
          - Pod
          namespaces:
          - default
          selector:
            matchLabels:
              role: spring-app
      - resources:
          kinds:
          - Pod
          namespaces:
          - default
          selector:
            matchLabels:
              policies.kyverno.io/test-mutate-rules-injection: disabled
    context:
    - name: preStopSleepSeconds
      variable:
        jmesPath: request.object.metadata.labels.preStopSleepSeconds
        default: 30
    mutate:
      patchesJson6902: |-
        - op: add
          path: "/spec/containers/0/lifecycle/preStop/exec/command"
          value:
          - "/bin/sh"
          - "-c"
          - "sleep {{ `{{ preStopSleepSeconds }}` }}"        

  - name: mutate-termination-grace-period-seconds-test
    match:
      any:
      - resources:
          kinds:
          - Pod
          operations:
          - CREATE
          namespaceSelector:
            matchLabels:
              policies.kyverno.io/test-mutate-rules-injection: enabled
    exclude:
      any:
      - resources:
          kinds:
          - Pod
          namespaces:
          - default
          selector:
            matchLabels:
              role: spring-app
      - resources:
          kinds:
          - Pod
          namespaces:
          - default
          selector:
            matchLabels:
              policies.kyverno.io/test-mutate-rules-injection: disabled
    context:
    - name: preStopSleepSeconds
      variable:
        jmesPath: request.object.metadata.labels.preStopSleepSeconds
        default: 30
    mutate:
      patchStrategicMerge:
        spec:
          # JMESpath should be enclosed in " "
          terminationGracePeriodSeconds: "{{ `{{ add(to_number(preStopSleepSeconds), to_number('10')) }}` }}"
{{- end }}

 

JMESPath 활용

Kyverno에서는 JMESPath라는 언어를 지원합니다.

JMESPath를 사용하면 여러가지 문자열 변환 처리, 함수, 사칙연산 등의 수학 계산식 등을 Cluster Policy 안에서 실행할 수 있습니다.

아래는 예제 Cluster Policy YAML 코드 중 일부입니다.

    context:
    - name: preStopSleepSeconds
      variable:
        jmesPath: request.object.metadata.labels.preStopSleepSeconds
        default: 30
    mutate:
      patchStrategicMerge:
        spec:
          # JMESpath should be enclosed in " "
          # Defining an integer as an argument in JMESPath requires enclosure in backticks.
          terminationGracePeriodSeconds: "{{ add(to_number(preStopSleepSeconds), `10`) }}"

위 Cluster Policy는 다음 순서대로 수행됩니다.

 

Cluster Policy 작성시 팁입니다.

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: test-mutate-policy
  annotations:
    ...
spec:
  schemaValidation: false
error admission webhook "validate-policy.kyverno.svc" denied the request on ClusterPolicy resource.`
terminationGracePeriodSeconds: "{{ add(to_number(preStopSleepSeconds), `10`) }}"

관련된 Github Issue

 

정책 모니터링

Kyverno 엔진을 설치하고 ClusterPolicy를 적용하는 이유는 결과적으로 모범사례를 위반된 쿠버네티스 리소스들을 인지하고 개선하기 위함입니다.

Kyverno의 정책을 모니터링 해야하는 이유는 크게 4가지가 있습니다.

 

시각화 방법

Helm 차트로 Kyverno를 설치하면 8000번 포트로 메트릭을 노출하는 추가 서비스가 생성됩니다.

Kyverno는 이 문제를 해결하기 위해 정책 운영결과 시각화 도구 2가지를 제공합니다.

  1. Policy Reporter UI : Policy Reporter UI는 공식 헬름차트로 간단하게 설치할 수 있습니다.
  2. Prometheus + Grafana : Kyverno 전용 Grafana 대시보드를 제공하고 있습니다.

 

Policy Reporter 설치

헬름 차트를 사용해서 Policy Reporter를 설치하는 방법입니다.

Github의 policy-reporter를 로컬에 다운로드 받습니다.

git clone https://github.com/kyverno/policy-reporter
cd policy-reporter/charts/policy-reporter/

 

Policy Reporter values.yaml 파일에서 아래 3가지 값을 수정합니다.

# values.yaml for policy-reporter
kyvernoPlugin:
  enabled: true  # [1] Set to true

...

# enable policy-report-ui
ui:
  enabled: true  # [2] Set to true

  plugins:         # [3] Add
    kyverno: true  # [3] Add

...

ui는 Policy Reporter 웹 컴포넌트를 의미하는데, Policy Reporter 웹에서 Kyverno 정책 대시보드를 보기 위해 kyverno plugin을 켜주는 설정입니다.

자세한 내용은 Policy Reporter 아키텍처 공식문서를 참고하세요.

 

기본 values.yaml 파일을 사용해서 Policy Reporter 차트를 배포합니다.

helm install \
  policy-reporter . \
  -n policy-reporter \
  --create-namespace \
  -f values.yaml \
  --wait

 

사내망에서만 접근 가능한 Policy Reporter를 아래와 같이 구성할 수 있습니다.

Policy Reporter 구성 예시

Policy Reporter 헬름 차트의 Ingress를 활성화해서 Internal 타입의 ALB를 만들었습니다.

 

Prometheus 방식

kyverno 헬름차트를 설치하면 기본적으로 8000번 포트로 각 컨트롤러마다 prometheus 수집을 위한 메트릭 서비스가 생성됩니다.

Kyverno 차트의 values.yaml 파일은 기본적으로 다음과 같이 설정되어 있습니다.

# values.yaml (helm chart for kyverno)

admissionController:
  ...
  metricsService:
    create: true
    port: 8000
    type: ClusterIP

backgroundController:
  ...
  metricsService:
    create: true
    port: 8000
    type: ClusterIP

cleanupController:
  ...
  metricsService:
    create: true
    port: 8000
    type: ClusterIP

reportsController:
  ...
  metricsService:
    create: true
    port: 8000
    type: ClusterIP

메트릭 수집 전용 서비스 타입이 TCP/8000에 ClusterIP로 생성되므로 기본적으로 같은 클러스터의 Prometheus 서버만 메트릭을 수집할 수 있다는 한계가 있습니다.

 

다른 쿠버네티스 클러스터에 위치한 Prometheus 서버가 Kyverno 메트릭을 수집하려면, 다음과 같이 metrics용 서비스를 기본값 ClusterIP에서 NodePort로 변경해야 합니다.

# values.yaml (helm chart for kyverno)

admissionController:
  metricsService:
    create: true
    type: NodePort
    port: 8000
    nodePort: 8000
  # ...

backgroundController:
  metricsService:
    create: true
    type: NodePort
    port: 8000
    nodePort: 8000
  # ...

cleanupController:
  metricsService:
    create: true
    type: NodePort
    port: 8000
    nodePort: 8000
  # ...

reportsController:
  metricsService:
    create: true
    type: NodePort
    port: 8000
    nodePort: 8000
  # ...

기본값 ClusterIPNodePort로 변경해서 노출하게 되면, Kyverno Pod들이 위치한 클러스터 바깥에 Prometheus Server가 있더라도 Kyverno 메트릭을 수집해갈 수 있습니다.

Prometheus와 Kyverno가 다른 클러스터에 위치한 경우 다음과 같은 아키텍처로 메트릭을 수집하게 됩니다.

외부 노출을 위한 NodePort 구성

 

serviceMonitor 구성

만약 Prometheus Operator를 설치해서 사용하고 있을 경우 아래와 같이 service monitor 리소스를 생성해서 메트릭을 수집할 수 있습니다.

serviceMonitor 구조

현재 클러스터에서 serviceMonitor 리소스를 사용할 수 있는지 확인하기 위해 먼저 API Resource 목록을 조회합니다.

$ kubectl api-resources --api-group monitoring.coreos.com
NAME                  SHORTNAMES   APIVERSION                       NAMESPACED   KIND
alertmanagerconfigs   amcfg        monitoring.coreos.com/v1alpha1   true         AlertmanagerConfig
alertmanagers         am           monitoring.coreos.com/v1         true         Alertmanager
podmonitors           pmon         monitoring.coreos.com/v1         true         PodMonitor
probes                prb          monitoring.coreos.com/v1         true         Probe
prometheusagents      promagent    monitoring.coreos.com/v1alpha1   true         PrometheusAgent
prometheuses          prom         monitoring.coreos.com/v1         true         Prometheus
prometheusrules       promrule     monitoring.coreos.com/v1         true         PrometheusRule
scrapeconfigs         scfg         monitoring.coreos.com/v1alpha1   true         ScrapeConfig
servicemonitors       smon         monitoring.coreos.com/v1         true         ServiceMonitor
thanosrulers          ruler        monitoring.coreos.com/v1         true         ThanosRuler

해당 클러스터에서 Prometheus Operator와 CRD가 이미 설치되어 있기 때문에 serviceMonitor가 존재하며 사용 가능한 상태입니다.

더 자세한 serviceMonitor 사용 예시는 제 다른 글 Prometheus Operator를 참고합니다.

 

ServiceMonitor는 Kyverno 헬름 차트 안에 이미 포함되어 있어서, enabled: true로만 설정해주면 알아서 생성해줍니다.

아래는 serviceMonitor 활성화 예시입니다.

# values.yaml
admissionController:
  serviceMonitor:
    # -- Create a `ServiceMonitor` to collect Prometheus metrics.
    enabled: true

...

backgroundController:
  serviceMonitor:
    # -- Create a `ServiceMonitor` to collect Prometheus metrics.
    enabled: true

...

cleanupController:
  serviceMonitor:
    # -- Create a `ServiceMonitor` to collect Prometheus metrics.
    enabled: true

...

reportController:
  serviceMonitor:
    # -- Create a `ServiceMonitor` to collect Prometheus metrics.
    enabled: true

이후 helm upgrade로 Kyverno 변경사항을 반영합니다.

helm upgrade \
  kyverno . \
  -n kyverno \
  -f values.yaml \
  --wait

 

kyverno 네임스페이스에 4개의 serviceMonitor가 새로 생성된 걸 확인할 수 있습니다.

$ kubectl get smon -n kyverno
NAME                            AGE
kyverno-admission-controller    12m
kyverno-background-controller   9m42s
kyverno-cleanup-controller      9m42s
kyverno-reports-controller      9m42s

 

Kyverno의 4개 컨트롤러는 각각 메트릭 수집 전용 포트인 8000/TCP을 ClusterIP로 노출하게 됩니다.

$ kubectl get svc -n kyverno -o wide
NAME                                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE    SELECTOR
kyverno-background-controller-metrics   ClusterIP   172.xx.xxx.xxx   <none>        8000/TCP   223d   app.kubernetes.io/component=background-controller,app.kubernetes.io/instance=kyverno,app.kubernetes.io/part-of=kyverno
kyverno-cleanup-controller              ClusterIP   172.xx.xxx.xxx   <none>        443/TCP    223d   app.kubernetes.io/component=cleanup-controller,app.kubernetes.io/instance=kyverno,app.kubernetes.io/part-of=kyverno
kyverno-cleanup-controller-metrics      ClusterIP   172.xx.xx.xx     <none>        8000/TCP   223d   app.kubernetes.io/component=cleanup-controller,app.kubernetes.io/instance=kyverno,app.kubernetes.io/part-of=kyverno
kyverno-reports-controller-metrics      ClusterIP   172.xx.xxx.xxx   <none>        8000/TCP   223d   app.kubernetes.io/component=reports-controller,app.kubernetes.io/instance=kyverno,app.kubernetes.io/part-of=kyverno
kyverno-svc                             ClusterIP   172.xx.xxx.xxx   <none>        443/TCP    223d   app.kubernetes.io/component=admission-controller,app.kubernetes.io/instance=kyverno,app.kubernetes.io/part-of=kyverno
kyverno-svc-metrics                     ClusterIP   172.xx.xx.xx     <none>        8000/TCP   223d   app.kubernetes.io/component=admission-controller,app.kubernetes.io/instance=kyverno,app.kubernetes.io/part-of=kyverno

 

kube-prometheus-stack chart에 포함되어 있는 Prometheus Operator의 동작방식을 설명합니다.

  1. 클러스터 관리자에 의해 kyverno 네임스페이스에 serviceMonitor 리소스가 새로 생성됩니다.
  2. Prometheus Operator는 kyverno servicMonitor를 자동 감지합니다. 기본적으로 Prometheus Operator Pod는 ServiceMonitor 리소스의 생성 및 변경을 지속적으로 감시합니다.
  3. 새로운 ServiceMonitor가 감지되거나 기존의 것이 업데이트되면, Prometheus Operator는 Prometheus Pod의 메트릭 수집Scrape 설정을 자동으로 업데이트합니다.
  4. 업데이트된 메트릭 수집Scrape 설정에 따라 Prometheus는 지정된 서비스로부터 메트릭을 수집하기 시작합니다. 기본값으로 수집간격 30s로 설정되며 이는 serviceMonitor spec에 선언되어 있습니다.

 

메트릭 제공용 서비스의 타입을 NodePort 말고 LoadBalancer 타입을 쓰는 방법도 있습니다.

공식문서 Monitoring에서는 크게 3가지 서비스 타입을 기준으로 각각 설정하는 방법을 설명합니다.

  1. ClusterIP (Default) : 메트릭 제공을 위한 기본 포트로 TCP/8000을 사용합니다.
  2. NodePort
  3. LoadBalancer

 

참고자료

Charts
kube-prometheus-stack
kyverno
kyverno-policies
policy-reporter

Kyverno
Kyverno 공식문서
Kyverno 설치 가이드
Kyverno Policy 저장소