#Kubernetes08.11.2026

ValidatingAdmissionPolicy vs Kyverno: бенчмарк на Kubernetes

Инженер сравнил Kyverno, Gatekeeper и нативные admission-политики Kubernetes по задержке, ресурсам и функциям

Сравнение Policy Engines в Kubernetes

Инженер Mustafa Erdinç Zorba прогнал бенчмарк, который сравнивает три способа управлять admission-политиками в Kubernetes: встроенные ValidatingAdmissionPolicy и MutatingAdmissionPolicy, Kyverno и Gatekeeper. В разборе на своём блоге merdincz.com он прогнал 144 000 dry-run запросов на kind-кластере с Kubernetes 1.36.1 и сравнил задержку, потребление ресурсов и возможности каждого движка.

Краткий ответ

Нативные ValidatingAdmissionPolicy и MutatingAdmissionPolicy выполняются через CEL прямо внутри kube-apiserver, без отдельного вызова вебхука, поэтому в бенчмарке Zorba они добавили наименьшую задержку и потребовали меньше ресурсов, чем Kyverno и Gatekeeper. Gatekeeper держал более стабильный хвост задержки, но заметно дороже базовой линии. Kyverno в части прогонов уходил в насыщение. Выбор между движками зависит от того, нужны ли политике внешние API-вызовы, сканирование существующих ресурсов или генерация объектов.

Что именно сравнивал автор

Zorba задал один policy-контракт и реализовал его тремя способами: добавить аннотацию policy-bench-injected: "true" при мутации, если её ещё нет, и потребовать на валидации аннотацию policy-bench-team: "platform" у подов с меткой policy-bench-subject: "true" в namespace policy-bench. Файлы политик не могут быть текстуально одинаковыми: Kyverno использует свой язык, Gatekeeper собирает Rego и CRD для мутаций, нативные политики написаны на CEL. Поэтому автор проверял семантическую эквивалентность — одинаковый результат на одних и тех же объектах. Перед каждым нагрузочным прогоном он прогонял 180 проверочных запросов, сверяя фактическое решение admission с ожидаемым.

Среда теста: kind 0.32.0 на Ubuntu 24.04.4 LTS (aarch64, 4 ядра Neoverse-N1, 7,5 ГиБ памяти), Kubernetes 1.36.1, Kyverno (Helm-чарт 3.8.2, приложение 1.18.2), Gatekeeper (Helm-чарт 3.23.0). На каждую конфигурацию отправлялось 12 000 валидных dry-run запросов CREATE пода при целевой нагрузке 200 RPS в течение 60 секунд с конкурентностью до 128 клиентов, после 15-секундного разогрева. Все 144 000 измеренных запросов завершились успешно. Dry-run выбран специально: он проходит admission, дефолты и валидацию схемы, но не пишет объект в хранилище, убирая шум от планирования и запуска подов.

Как работает admission control: вебхук против CEL внутри kube-apiserver

Запрос сначала проходит аутентификацию и авторизацию, затем мутирующие контроллеры и только потом валидирующие. Для вебхуков kube-apiserver шлёт AdmissionReview по HTTPS на сервис вебхука, тот возвращает патч или решение allow/reject. Мутирующие вебхуки вызываются последовательно, валидирующие — параллельно; это одна из причин, почему много мутирующих вебхуков дорого обходится.

CEL (Common Expression Language) — язык выражений, на котором Kubernetes вычисляет встроенные политики прямо в API-сервере. У native-политик нет отдельного сетевого прыжка к сервису вебхука и TLS-рукопожатия: MutatingAdmissionPolicy возвращает ApplyConfiguration или JSON Patch, ValidatingAdmissionPolicy — решение валидации, и всё это внутри одного процесса. Как указывает автор блога, официальные рекомендации Kubernetes по хорошим практикам для admission-вебхуков советуют использовать встроенный CEL-контроль, когда он покрывает задачу, и переходить на вебхуки только для сложной или внешней логики.

Результаты по задержке: кто быстрее

По объединённым данным трёх прогонов:

Конфигурацияp50p95p99
Без политик (baseline)4,16 мс7,60 мс14,27 мс
Native (Validating/MutatingAdmissionPolicy)6,38 мс12,77 мс24,38 мс
Gatekeeper8,76 мс19,72 мс51,83 мс
Kyverno25,03 мс1198,86 мс1892,25 мс

Объединённая таблица скрывает разброс между прогонами. HTTP p95 Gatekeeper держался в районе 18–21 мс во всех трёх прогонах — стабильно. У Kyverno p95 по прогонам составил 66,76 мс, 758,36 мс и 1629,04 мс: в третьем прогоне движок ушёл в насыщение, очередь клиента на p95 достигла 3,454 секунды, а реально достигнутая пропускная способность после дренажа упала до 188,75 запроса в секунду вместо заявленных 200.

Сколько это стоит по CPU и памяти

Автор снимал метрики всего kind-узла раз в секунду: у native-политик нет отдельного контейнера, они работают внутри kube-apiserver. Средние значения под нагрузкой:

КонфигурацияCPU, ядра (среднее)Память, МиБ (среднее)
Baseline1,10543,7
Native1,44556,95
Gatekeeper2,03701,7
Kyverno2,661086,0

Для сравнения, контейнеры в namespace Gatekeeper потребляли в среднем 0,632 ядра и 40,2 МиБ, а контейнеры Kyverno — 1,249 ядра и 80,94 МиБ. Для теста автор отключил аудит Gatekeeper и background/cleanup/reporting-контроллеры Kyverno, чтобы измерить именно стоимость admission, а не дополнительных функций.

Чего не умеют нативные admission-политики

Kyverno — open-source policy-движок для Kubernetes с собственным YAML-языком политик, развивается под эгидой CNCF. Gatekeeper реализует admission control поверх Open Policy Agent (OPA), политики пишутся на Rego, проект тоже развивается под эгидой CNCF.

По матрице функций из разбора у native-политик CEL нет произвольных GET/LIST-запросов к API и нет исходящих HTTP-вызовов — только namespaceObject и параметры, выбранные биндингом. Gatekeeper в бета-режиме умеет вызывать внешний HTTPS-провайдер (External Data) и держит реплицированный инвентарь через data.inventory. Kyverno может напрямую ходить в API и по внешним HTTP(S), кэшируя ответы через GlobalContextEntry. Нативные политики работают только в момент admission и не сканируют уже существующие объекты — у Gatekeeper это делает Audit, у Kyverno — background scan. Генерировать, клонировать или синхронизировать сопутствующие ресурсы умеет только Kyverno через GeneratingPolicy, а удалять объекты по расписанию — только его DeletingPolicy. У нативных политик из отчётности есть только предупреждения клиенту и аудит-события Kubernetes, без накопленного реестра соответствия.

Что с этим делать платформенной команде

Сам Zorba давно использует Kyverno именно из-за DeletingPolicy и API-вызовов — для части его политик это обязательное требование, отказаться от которого он не может. Но не все его политики нуждаются во внешних вызовах, и по итогам бенчмарка он планирует переносить такие политики на нативные ValidatingAdmissionPolicy и MutatingAdmissionPolicy постепенно. Прежде чем переводить на них нагруженные окружения с большим числом узлов и подов, он хочет сначала проверить нативные политики на живом кластере рядом со всеми остальными компонентами платформы.

Для политик без внешних вызовов и сканирования существующих ресурсов нативные admission-policies выглядят в этом бенчмарке разумной заменой тяжёлых движков, но у Kyverno и Gatekeeper остаётся набор возможностей — генерация ресурсов, плановая очистка, богатая отчётность, — которые CEL-политики внутри kube-apiserver пока не покрывают. Автор отдельно предупреждает: он не активный пользователь Gatekeeper и собирал его матрицу функций с помощью ИИ-ассистента, поэтому не исключает неточностей именно в этой части и просит сообщать о расхождениях.

FAQ

Что такое ValidatingAdmissionPolicy и MutatingAdmissionPolicy?

Это встроенные в Kubernetes механизмы admission control, которые проверяют и изменяют объекты API на лету с помощью выражений CEL. По данным автора блога merdincz.com, ValidatingAdmissionPolicy стала общедоступной (GA) начиная с Kubernetes 1.30, а MutatingAdmissionPolicy — начиная с Kubernetes 1.36.

Чем нативные admission-политики отличаются от Kyverno и Gatekeeper по механике?

Нативные политики выполняются CEL-выражениями прямо внутри kube-apiserver, без отдельного сетевого вызова. Kyverno и Gatekeeper работают как внешние вебхуки: kube-apiserver отправляет им AdmissionReview по HTTPS и ждёт ответа, что добавляет сетевой прыжок и TLS-обработку на каждый запрос.

Можно ли полностью заменить Kyverno нативными admission-политиками?

По итогам бенчмарка — нет, не полностью. Нативные политики не умеют делать произвольные внешние HTTP-вызовы, сканировать уже существующие ресурсы, генерировать и клонировать объекты или удалять их по расписанию — всё это есть в Kyverno. Автор сам планирует переносить только те политики, которым эти возможности не нужны.

Какие данные легли в основу бенчмарка?

Автор прогнал 144 000 dry-run запросов на создание подов (36 000 на каждую из четырёх конфигураций: без политик, native, Gatekeeper, Kyverno) на kind-кластере с Kubernetes 1.36.1, измеряя HTTP-задержку, задержку очереди клиента и потребление CPU и памяти всего узла.

Насколько репрезентативны эти результаты для продакшен-кластера?

Это микробенчмарк на одном control-plane узле kind с dry-run запросами, без записи объектов в хранилище, планирования и запуска подов. Сам автор указывает, что перед переносом политик на живые кластеры хочет проверить нативные admission-policies под реальной нагрузкой вместе с остальными компонентами платформы.