Kyverno и OPA Gatekeeper решают одну задачу — не пустить «плохой» ресурс в кластер до того, как он будет сохранён в etcd. Оба проекта окончили CNCF с отличием, оба работают как admission webhook, у обоих есть готовые библиотеки политик на сотни правил. Разница не в качестве: вопрос в том, где заканчивается ваш Kubernetes. Если политика нужна только внутри кластера — Kyverno позволяет писать её тем же YAML, которым вы пишете Deployment. Если та же проверка должна сработать и в Terraform, и в GitHub Actions, и на API-шлюзе — OPA Rego переиспользуется везде без переписывания.
Разберём матрицу «когда что» без религии.
Содержание
Открыть содержание
- Что такое admission webhook и зачем нужны оба инструмента
- Язык политик: YAML против Rego
- Возможности: validation, mutation, generation
- ValidatingAdmissionPolicy (k8s 1.30+): кто обнимает лучше
- Таблица сравнения
- Что нужно, чтобы запустить Kyverno
- Что нужно, чтобы запустить OPA Gatekeeper
- Когда оправдан гибридный вариант
- Итог
Что такое admission webhook и зачем нужны оба инструмента
Любой kubectl apply проходит через несколько уровней защиты внутри API Server: аутентификацию субъекта, авторизацию через RBAC, OpenAPI-схемную валидацию поля типа — и лишь потом попадает в цепочку admission контроллеров. В этой цепочке работают два вида webhook: MutatingAdmissionWebhook (может изменить объект) и ValidatingAdmissionWebhook (только разрешить или отклонить). Только после прохождения обоих объект записывается в etcd и становится «реальным» состоянием кластера.
Kyverno и OPA Gatekeeper реализуют именно эти webhook: они перехватывают создание и обновление Pod, Deployment, Namespace, ServiceAccount и любого другого ресурса — в том числе Custom Resources — и применяют ваши политики до того, как что-то изменится. Ни один Pod с образом nginx:latest не проскочит мимо, если политика запрещает плавающие теги. Ни один Deployment без лимита CPU не уйдёт в etcd, если есть соответствующее правило.
Принципиальное различие начинается на следующем шаге — как именно вы описываете, что считать нарушением.
Язык политик: YAML против Rego
Kyverno описывает политики как нативные Kubernetes Custom Resources — ClusterPolicy (действует на весь кластер) и Policy (ограничена одним namespace). Это обычные YAML-манифесты: вы кладёте их в GitOps-репозиторий, применяете через kubectl apply или Helm, и они живут рядом с Deployment и ConfigMap. Логика строится декларативно: блок match определяет, к каким ресурсам применяется правило, а дальше — один из трёх действий: validate (проверить), mutate (изменить), generate (создать новый ресурс).
С версии Kyverno 1.17 CEL-выражения получили статус v1. CEL — это тот же язык, что Kubernetes использует во встроенном ValidatingAdmissionPolicy; он компактен, типизирован и проверяется заранее без запуска. Сложная логика, которая раньше требовала громоздких deny.conditions в Rego, теперь пишется в одну строку прямо в поле expression.
Вот как выглядит запрет тегов :latest на Kyverno с CEL:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-tag
spec:
validationFailureAction: Enforce
rules:
- name: require-pinned-tag
match:
any:
- resources:
kinds: [Pod]
validate:
cel:
expressions:
- expression: >
object.spec.containers.all(c,
!c.image.endsWith(':latest'))
message: "Теги :latest запрещены. Укажите точный тег или SHA-дайджест."
Если вы когда-нибудь писали Kubernetes YAML — здесь нет ни одной новой концепции. Поле match работает как labelSelector, cel.expressions читается почти как SQL WHERE.
OPA Gatekeeper использует Rego — функциональный язык Open Policy Agent. Подход двухслойный: ConstraintTemplate описывает саму логику (содержит Rego-код и определяет CRD для нового типа Constraint), а Constraint — конкретный экземпляр правила с параметрами и областью применения. Один и тот же ConstraintTemplate можно применить в нескольких Constraint с разными параметрами — например, запретить :latest в prod, но разрешить в dev.
Rego — не YAML и не Go. Это декларативный язык логического программирования в духе Datalog: переменные неизменяемы, правила — это наборы выражений, все из которых должны быть истинны одновременно, а результат — набор значений переменной violation. Он мощнее CEL для очень сложных случаев, но требует отдельного изучения.
Та же политика на Gatekeeper:
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sdisallowlatestimage
spec:
crd:
spec:
names:
kind: K8sDisallowLatestImage
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sdisallowlatestimage
violation[{"msg": msg}] {
c := input.review.object.spec.containers[_]
endswith(c.image, ":latest")
msg := sprintf("Контейнер %v использует тег :latest", [c.name])
}
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDisallowLatestImage
metadata:
name: disallow-latest
spec:
match:
kinds:
- apiGroups: [""]
kinds: [Pod]
Вдвое больше файлов, новый язык, новые концепции. Зато ровно тот же Rego работает в OPA-сервере рядом с Terraform, в Conftest в CI-пайплайне, и в политиках доступа к API. Если ваша команда уже использует OPA где-то ещё — переиспользование перевесит кривую обучения.
Возможности: validation, mutation, generation
Kyverno умеет три вещи, которых OPA Gatekeeper не умеет из коробки.
Validation — есть у обоих. Отклонить некорректный ресурс с чёткой ошибкой. Режим Audit позволяет сначала наблюдать нарушения без блокировки и видеть их в PolicyReport; переключить на Enforce — одна строка в манифесте. Этот режим особенно ценен при внедрении: сначала смотришь, что нарушается, потом включаешь принудительное исполнение.
Mutation — изменение ресурса перед сохранением, без отклонения. Kyverno поддерживает её в стабильном виде с богатым DSL: добавить label или аннотацию, выставить securityContext.runAsNonRoot: true, удалить hostNetwork: true, изменить образ. Это особенно удобно при постепенном переходе на новые стандарты: сначала мутируешь молча, потом добавляешь validation. Gatekeeper реализовал мутацию в v3.14 как beta-функцию, и она до сих пор имеет заметные ограничения в синтаксисе и типах ресурсов.
Generation — автоматическое создание сопутствующих ресурсов по триггеру. Классический сценарий: создали новый Namespace — Kyverno автоматически сгенерировал NetworkPolicy с дефолтным запретом входящего трафика, ResourceQuota с лимитами CPU/памяти и LimitRange для Pod. Платформенные инженеры ставят это однажды, и каждый новый Namespace сразу получает корректные guardrails без ручной работы. OPA не умеет генерировать ресурсы вообще — это за пределами его модели.
ValidatingAdmissionPolicy (k8s 1.30+): кто обнимает лучше
Kubernetes 1.30 стабилизировал ValidatingAdmissionPolicy (VAP) — встроенный механизм validation-политик на CEL, который живёт прямо в API Server без стороннего webhook. Главное преимущество: нет сетевого вызова к webhook-серверу. На практике это значит меньше латентности admission path и один потенциальный источник отказа минус.
VAP не умеет мутировать и генерировать ресурсы, поэтому не заменяет Kyverno и Gatekeeper полностью — только дополняет их для простых validation-правил.
Kyverno 1.17 умеет автоматически синхронизировать ClusterPolicy в нативные VAP-ресурсы. Включаете флаг generateValidatingAdmissionPolicy: true при установке через Helm — и Kyverno пишет объекты ValidatingAdmissionPolicy и ValidatingAdmissionPolicyBinding за вас. Простые validation-правила отрабатывают через Kubernetes нативно; webhook вызывается только для mutation и generation. Оператору не нужно делать ничего особенного: пишете обычную ClusterPolicy, Kyverno сам решает, пустить ли проверку через VAP.
Gatekeeper v3.22 тоже умеет маршрутизировать validation через VAP: параметр enforcementAction: denyAction в Constraint направляет исполнение через нативный VAP без вызова Gatekeeper webhook. Rego-логика остаётся прежней — меняется только путь исполнения. Интеграция более явная и требует явного выбора на уровне Constraint, в отличие от прозрачного авто-генерации у Kyverno.
Таблица сравнения
| Фича | Kyverno | OPA Gatekeeper |
|---|---|---|
| Язык политик | YAML + CEL | Rego |
| Validation | ✅ стабильная | ✅ стабильная |
| Mutation | ✅ стабильная | ⚠️ beta |
| Generation | ✅ | ❌ |
| ValidatingAdmissionPolicy | ✅ авто-генерация | ✅ через denyAction |
| Политики вне Kubernetes | ❌ | ✅ Terraform, CI, API |
| Готовая библиотека | ✅ Kyverno Policies | ✅ Gatekeeper Library |
| Кривая входа | Низкая (YAML) | Средняя (Rego) |
| Audit-режим | ✅ PolicyReport | ✅ .status.violations |
| Параметризация правил | Через переменные | Через Constraint params |
Что нужно, чтобы запустить Kyverno
Требования: Kubernetes ≥ 1.25, Helm 3.
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
helm install kyverno kyverno/kyverno \
--namespace kyverno --create-namespace \
--set features.generateValidatingAdmissionPolicy.enabled=true
После установки Kyverno добавляет несколько webhook в кластер — вы увидите их через kubectl get validatingwebhookconfigurations. При отказе Kyverno pod поведение определяется параметром failurePolicy: по умолчанию Fail (webhook недоступен — запрос отклонён), что консервативно и безопасно для production.
Применяем готовую политику из официальной библиотеки — не нужно писать самостоятельно:
kubectl apply -f \
https://raw.githubusercontent.com/kyverno/policies/main/best-practices/disallow-latest-tag/disallow-latest-tag.yaml
Проверяем:
kubectl run test --image=nginx:latest --restart=Never
# Error from server: admission webhook "kyverno-resource-validating-webhook-cfg.kyverno.svc"
# denied the request: Теги :latest запрещены. Укажите точный тег или SHA-дайджест.
kubectl run ok --image=nginx:1.27.0 --restart=Never
# pod/ok created
Смотрим audit-отчёт по всем существующим ресурсам:
kubectl get policyreport -A
# NAMESPACE NAME PASS FAIL WARN
# default cpol-disallow-latest-tag 12 3 0
PolicyReport — CRD от Policy Report Working Group; Kyverno заполняет его автоматически при audit-сканировании. Удобно подключать к Grafana через kubectl exporter.
Что нужно, чтобы запустить OPA Gatekeeper
Требования: Kubernetes ≥ 1.25, Helm 3.
helm repo add gatekeeper \
https://open-policy-agent.github.io/gatekeeper/charts
helm repo update
helm install gatekeeper gatekeeper/gatekeeper \
--namespace gatekeeper-system --create-namespace
Gatekeeper устанавливает три компонента: controller-manager (обрабатывает admission запросы), audit (фоново проверяет существующие ресурсы) и webhook (сам сервер). При отказе webhook поведение тоже регулируется failurePolicy.
Применяем ConstraintTemplate и Constraint:
kubectl apply -f constraint-template.yaml
# ConstraintTemplate - создаёт CRD K8sDisallowLatestImage
# Подождём, пока Gatekeeper зарегистрирует CRD
kubectl wait --for condition=established \
crd/k8sdisallowlatestimages.constraints.gatekeeper.sh --timeout=60s
kubectl apply -f constraint.yaml
Проверяем:
kubectl run test --image=nginx:latest --restart=Never
# Error from server ([disallow-latest] Контейнер test использует тег :latest)
kubectl run ok --image=nginx:1.27.0 --restart=Never
# pod/ok created
Смотрим нарушения, которые audit нашёл в существующих ресурсах:
kubectl get k8sdisallowlatestimage disallow-latest \
-o jsonpath='{.status.violations}' | jq .
# [{"enforcementAction":"deny","group":"","kind":"Pod",
# "message":"Контейнер legacy использует тег :latest",
# "name":"legacy","namespace":"default","version":"v1"}]
Когда оправдан гибридный вариант
Большие платформенные команды нередко держат оба инструмента в одном кластере. Такой выбор оправдан, когда обе потребности присутствуют одновременно: нужна богатая mutation и generation (Kyverno), и при этом те же проверки должны работать за пределами Kubernetes (OPA).
Типичное разделение: Kyverno занимается mutation и generation — проставляет обязательные labels на все ресурсы, добавляет securityContext в Pod если не указан, создаёт NetworkPolicy для каждого нового Namespace. OPA занимается validation, причём тот же Rego-код запускается и в кластере, и в GitHub Actions через conftest check, и против terraform plan через OPA CLI.
# Conftest: та же политика в CI
conftest test deployment.yaml \
--policy policies/disallow-latest.rego
# FAIL - deployment.yaml - Контейнер app использует тег :latest
Главная ловушка гибридного варианта — дублирование. Если вы написали validation на Kyverno «для быстроты», а потом добавили ту же проверку в OPA, вы сразу получаете два источника правды. При изменении правила нужно менять оба; при расхождении между ними — разобраться, какое «главное». Если вы запускаете оба, строго разграничьте зоны ответственности письменно, и ревьюьте каждую новую политику на предмет «не дублирует ли она то, что уже есть в другом движке».
Итог
Kyverno — правильный выбор для команды, которая живёт в YAML и работает только внутри Kubernetes. Меньше новых концепций, mutation и generation из коробки, прозрачная авто-интеграция с нативным VAP в k8s 1.30+. Kyverno Policies — библиотека из сотен готовых правил для быстрого старта.
OPA Gatekeeper — правильный выбор, если ваша политика выходит за пределы кластера в Terraform, CI или другие системы. Одним языком Rego покрываете всё, а библиотека Gatekeeper Library даёт те же готовые политики для Kubernetes-специфичных проверок.
Гибридный вариант (Kyverno мутирует и генерирует, OPA проверяет везде) оправдан, когда обе потребности есть одновременно, — но требует строгого разграничения зон ответственности с первого дня. Иначе вы получите две конкурирующие копии одних и тех же правил, и через год никто не будет понимать, какая из них «главная».