Policy as code: Kyverno и OPA Gatekeeper

Рейтинг: 72.2% · 10 голосов
Kubernetes для разработчиков: поды, деплойменты, сервисы, ingress, конфиги и отладка. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
anton_k8s
Сообщения: 55
Зарегистрирован: 12 май 2026, 03:23

Policy as code: Kyverno и OPA Gatekeeper

Сообщение anton_k8s »

Оглавление курса (48)
  1. Зачем нужен Kubernetes и из чего состоит кластер
  2. Поднимаем локальный кластер: minikube и kind
  3. Поды: базовая единица запуска
  4. Deployment и ReplicaSet: управляем репликами
  5. Service: сетевой доступ к подам
  6. ConfigMap и Secret: выносим конфигурацию
  7. Ingress: пускаем трафик снаружи
  8. Хранилище: Volumes и PersistentVolumeClaim
  9. Namespaces, requests и limits
  10. Health checks: liveness и readiness пробы
  11. Отладка: почему под не стартует
  12. Helm: пакетный менеджер для Kubernetes
  13. Базовая безопасность: RBAC и доступы
  14. Job и CronJob: разовые и периодические задачи
  15. StatefulSet и DaemonSet: stateful-нагрузки и системные агенты
  16. Стратегии обновления и планирование: rollout и rollback, graceful shutdown, nodeSelector, affinity, taints
  17. Автомасштабирование: HPA по метрикам, обзор VPA и Cluster Autoscaler
  18. Наблюдаемость: логи, метрики, events, обзор Prometheus и Grafana
  19. Безопасность глубже: securityContext, Pod Security Standards, NetworkPolicy, шифрование секретов
  20. Архитектура control plane: api-server, etcd, scheduler
  21. Узел кластера: kubelet, kube-proxy и container runtime
  22. Декларативная модель: reconciliation, контроллеры, CRD
  23. kubectl профессионально: get, describe, explain, jsonpath
  24. Под глубже: init, sidecar, lifecycle hooks и QoS
  25. Метки, селекторы и организация ресурсов
  26. Сервисы и kube-proxy глубже: типы, IPVS, EndpointSlices
  27. Ingress, ingress-контроллеры и Gateway API
  28. Секреты в кластере: шифрование, External Secrets, Vault
  29. Хранилище глубже: PV, StorageClass, CSI и StatefulSet
  30. Сеть кластера: CNI, NetworkPolicy и CoreDNS
  31. Планировщик: affinity, taints, topology spread
  32. Ресурсы, QoS и вытеснение: requests, limits, eviction
  33. Helm глубже: шаблоны, хуки, зависимости, OCI
  34. Kustomize и управление конфигурацией без шаблонов
  35. RBAC и аутентификация глубже
  36. Admission и Pod Security: контроль на входе
  37. Policy as code: Kyverno и OPA Gatekeeper (вы здесь)
  38. GitOps: Argo CD и Flux
  39. Операторы и CRD: расширяем Kubernetes
  40. Сервис-меш: Istio, Linkerd и когда он нужен
  41. Эксплуатация кластера: апгрейд, узлы, бэкап etcd
  42. Где запускать кластер: managed, self-hosted, k3s, Deckhouse
  43. Траблшутинг кластера: Pending, CrashLoop, узлы NotReady
  44. Стоимость и эффективность кластера: FinOps
  45. CI/CD в Kubernetes: сборка, деплой, прогрессивные релизы
  46. Лучшие практики и антипаттерны Kubernetes
  47. Сквозной проект и путь дальше: от манифеста до прода
  48. Деплой приложений через Argo CD: Application, sync и App-of-Apps
Боль: "у нас же есть код-ревью" - и тут в проде latest:latest без лимитов

Представь типичную картину. В кластере десяток команд, у каждой свой namespace, свой CI, свои привычки. Кто-то катит образ с тегом latest, кто-то забыл прописать resources.requests, кто-то на проде поднял Pod с privileged: true "чтобы быстро отладить" и забыл выключить. Код-ревью это не ловит - YAML огромный, глаз замыливается. SecurityContext проверяет один человек по пятницам. В итоге кластер превращается в проходной двор, а ты узнаешь о privileged-контейнере только когда из него вылезли в host.

Ровно эту дыру закрывает policy as code kubernetes - кластерные политики как код. Идея простая до гениальности: ты один раз описываешь правила ("в проде нельзя latest", "у всех Pod обязаны быть requests", "образы только из нашего реестра") в виде манифестов, кладёшь их в Git рядом с остальной инфраструктурой, и кластер сам отклоняет всё, что нарушает. Не человек на ревью, а admission-контроллер на входе в API-сервер. Машина не устаёт, не делает исключений по пятницам и не пропускает privileged.

В 2026 это уже не экзотика, а гигиенический минимум зрелого кластера. Два игрока делят рынок: kyverno (политики прямо в YAML, k8s-нативно, проще порог входа) и opa gatekeeper (язык Rego, мощнее, но сложнее). Разберём оба под капотом, сравним честно и покажем, как внедрить без того, чтобы в первый же день положить прод.

Изображение

Механика: где именно живут эти kubernetes политики

Чтобы понять, как работает policy as code, нужно вспомнить путь запроса в Kubernetes. Когда ты делаешь kubectl apply, манифест летит в kube-apiserver и проходит конвейер: аутентификация -> авторизация (RBAC) -> admission-контроллеры -> запись в etcd. Admission - это последний шлагбаум перед сохранением. И он делится на две фазы.
  • Mutating admission - может изменить объект на лету. Дописать недостающую метку, навесить sidecar, проставить дефолтный securityContext.
  • Validating admission - может только сказать "да" или "нет". Если объект нарушает правило - запрос отклоняется с ошибкой, и в etcd ничего не попадает.
И Kyverno, и Gatekeeper подключаются к API-серверу через механизм admission webhook. Технически это объекты ValidatingWebhookConfiguration и MutatingWebhookConfiguration: API-сервер на каждый create/update нужных ресурсов делает HTTP-вызов в pod контроллера политик, тот проверяет объект против всех политик и возвращает вердикт. Поэтому, кстати, политики работают не только на kubectl, но и на всё подряд - на Argo CD, на Helm, на оператор, который сам создаёт Pod. Любой путь в API-сервер проходит через webhook.

Второй режим работы - background scan (фоновое сканирование, оно же audit). Webhook ловит только новые и изменённые объекты. А что с тем, что уже лежит в кластере? Для этого оба инструмента периодически проходят по существующим ресурсам и пишут отчёты о нарушениях, ничего не ломая. Это и есть основа безопасного внедрения - сначала смотрим, кто нарушает, потом включаем блокировку.

Kyverno: политики на чистом YAML без нового языка

Главная фишка Kyverno в том, что политика - это обычный Kubernetes-ресурс. Никакого отдельного языка: ты описываешь правило теми же match/pattern, что и манифесты. Базовый объект - ClusterPolicy (на весь кластер) или Policy (в рамках namespace), apiVersion kyverno.io/v1. У Kyverno четыре типа действий: validate (проверить), mutate (изменить), generate (создать сопутствующий ресурс) и cleanup (удалить по расписанию).

Вот политика, запрещающая тег latest - классика жанра. Разберём по полям.

Код: Выделить всё

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-latest-tag
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: require-image-tag
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Тег образа обязателен, latest запрещен"
        pattern:
          spec:
            containers:
              - image: "!*:latest"
Что здесь происходит. validationFailureAction: Enforce - это режим блокировки (есть ещё Audit, о нём ниже). background: true разрешает фоновое сканирование уже существующих Pod. Блок match говорит "применять к Pod". А validate.pattern - сердце правила: образ каждого контейнера не должен матчиться по маске *:latest (восклицательный знак - отрицание). Pattern в Kyverno работает как "манифест-шаблон": ты пишешь кусок желаемой структуры, а Kyverno сверяет реальный объект с ним. Удобно тем, что синтаксис ты уже знаешь - это тот же YAML.

Теперь mutate - где Kyverno сам чинит объект. Допустим, хотим всем Pod без метки team навешивать team: unassigned, чтобы потом не было сирот.

Код: Выделить всё

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: add-default-team-label
spec:
  rules:
    - name: add-team
      match:
        any:
          - resources:
              kinds:
                - Pod
      mutate:
        patchStrategicMerge:
          metadata:
            labels:
              +(team): unassigned
Конструкция +(team) означает "добавить ключ team, только если его ещё нет". Если метка уже стоит - Kyverno её не трогает. Это anchor-синтаксис Kyverno, ради него и стоит читать доку - там есть условные =(...), отрицания и прочее.

Применяем и смотрим, что отвечает кластер на нарушителя:

Код: Выделить всё

$ kubectl run bad --image=nginx:latest
Error from server: admission webhook "validate.kyverno.svc-fail" denied the request:

resource Pod/default/bad was blocked due to the following policies

disallow-latest-tag:
  require-image-tag: 'validation error: Тег образа обязателен, latest запрещен.
    rule require-image-tag failed at path /spec/containers/0/image/'
Видишь? Запрос отклонён на уровне admission, Pod даже не создан. В тексте ошибки - имя политики, имя правила и путь /spec/containers/0/image, где сломалось. Это важно для дебага: пользователь сразу понимает, какое поле чинить.

Отдельно стоит знать про новое поколение Kyverno. В свежих версиях (актуальная linия - 1.18.x на середину 2026) появились отдельные CRD ValidatingPolicy и MutatingPolicy с apiVersion policies.kyverno.io/v1alpha1 - они построены на CEL и сближают Kyverno с нативным механизмом ValidatingAdmissionPolicy самого Kubernetes. Старые ClusterPolicy при этом никуда не делись и остаются основным рабочим инструментом. Если видишь оба синтаксиса в чужих репозиториях - не пугайся, это переходный период.

OPA Gatekeeper: Rego, ConstraintTemplate и Constraint

Gatekeeper - это проекция проекта Open Policy Agent (OPA) на Kubernetes. Здесь логика политики пишется на Rego - декларативном языке запросов, заточенном под "достать из объекта данные и проверить условие". Rego мощнее YAML-паттернов: в нём есть полноценные циклы по спискам, агрегации, обращения к другим объектам кластера. Цена - его надо учить, и отлаживать Rego сложнее, чем YAML.

Архитектура gatekeeper kubernetes держится на двух ресурсах, и их разделение - ключевая идея.
  • ConstraintTemplate - шаблон политики. Это как класс: внутри лежит Rego-код с логикой и описание параметров, которые политика принимает. Создавая ConstraintTemplate, ты заодно регистрируешь в кластере новый CRD.
  • Constraint - экземпляр шаблона с конкретными параметрами. Это как объект класса: "примени шаблон required-labels с параметром labels=[team] вот к этим kinds".
Зачем такое разделение? Один раз написал сложный Rego в шаблоне - дальше команды переиспользуют его, просто меняя параметры, без знания Rego. Платформенная команда пишет templates, продуктовые - применяют constraints.

Шаблон, требующий наличие заданных меток:

Код: Выделить всё

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        openAPIV3Schema:
          type: object
          properties:
            labels:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredlabels
        violation[{"msg": msg}] {
          provided := {label | input.review.object.metadata.labels[label]}
          required := {label | label := input.parameters.labels[_]}
          missing := required - provided
          count(missing) > 0
          msg := sprintf("Отсутствуют обязательные метки: %v", [missing])
        }
Разбор Rego без паники. provided - множество меток, что есть на объекте. required - множество меток из параметров. missing := required - provided - разность множеств, то есть чего не хватает. Если count(missing) > 0 - правило срабатывает и формирует msg. Блок violation[...] - это контракт Gatekeeper: если он непустой, есть нарушение. input.review.object - проверяемый объект, input.parameters - параметры из Constraint. Вот тебе и вся механика.

Теперь сам Constraint, который включает шаблон в работу:

Код: Выделить всё

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: ns-must-have-team
spec:
  enforcementAction: deny
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    labels: ["team"]
kind: K8sRequiredLabels - тот самый CRD, что родился из шаблона. enforcementAction: deny - блокировать (альтернативы warn и dryrun - о них в граблях). match сужает действие до Namespace, parameters передаёт список обязательных меток. Меняя только parameters в разных Constraint, ты переиспользуешь один Rego-шаблон под десятки правил.

Kyverno против Gatekeeper: что выбрать в 2026

Честное сравнение без маркетинга.
  • Порог входа. Kyverno выигрывает с разгромом для команд без опыта в policy as code: YAML ты уже знаешь. Gatekeeper требует Rego - это отдельная компетенция, и в маленькой команде её может физически не быть.
  • Мощность. Gatekeeper берёт сложные кейсы: "запретить два Ingress с одинаковым host во всём кластере", агрегации, межобъектные проверки через data.inventory. На Rego это пишется, на YAML-паттернах Kyverno - больно или никак.
  • Mutate и generate. Это сильная сторона Kyverno и почти его монополия: автодобавление меток, генерация NetworkPolicy на каждый новый namespace, проброс ConfigMap. Gatekeeper исторически про валидацию (мутации у него появились позже и слабее).
  • Отчётность. Kyverno из коробки пишет PolicyReport - нативные k8s-объекты с нарушениями, которые удобно тащить в Grafana через policy-reporter. У Gatekeeper аудит через статус Constraint.
Практический вывод. Стартуешь с нуля, нужны и валидация, и мутации, команда DevOps без академического бэкграунда - бери Kyverno. Нужна тяжёлая логика, в команде уже есть Rego (например, OPA крутится для авторизации в API), хочешь единый язык политик на инфру и кластер - Gatekeeper. На многих кластерах в РФ (Yandex Managed Kubernetes, VK Cloud, Deckhouse) оба ставятся через Helm одинаково просто, managed-специфики тут почти нет.

Связь с Pod Security Standards и место в CI

Логичный вопрос: в Kubernetes же есть встроенный Pod Security Admission (PSS) - три уровня privileged/baseline/restricted, включаются меткой на namespace. Зачем тогда Kyverno?

PSS - это фиксированный набор правил безопасности Pod, его нельзя расширить. Он отлично закрывает "не давать privileged, hostNetwork, hostPath". Но он не умеет твоих правил: "образы только из registry.cyberlake.ru", "обязательна метка cost-center", "у Service запрещён type LoadBalancer в dev". Вот тут и нужен policy engine - он дополняет PSS произвольной логикой бизнеса и платформы. Грамотная схема 2026: PSS включён на baseline/restricted как база, а Kyverno/Gatekeeper навешивает organization-specific правила сверху.

И второй фронт - CI. Главная мысль: одну и ту же политику надо проверять дважды. В кластере через admission - это страховка. И в пайплайне до кластера - это быстрая обратная связь разработчику. Лучше отклонить плохой манифест за 5 секунд в merge request, чем через 10 минут на деплое. Kyverno умеет это нативно своим CLI:

Код: Выделить всё

$ kyverno apply ./policies/ --resource ./manifests/deployment.yaml

Applying 3 policies to 1 resource...

policy disallow-latest-tag -> resource default/Deployment/web failed:
  1. require-image-tag: validation error: latest запрещен.

pass: 0, fail: 1, warn: 0, error: 0, skip: 0
Этот вызов кладёшь в GitHub Actions / GitLab CI как обязательный шаг - и плохой YAML не доедет до кластера. У Gatekeeper аналогичную роль играет gator (gator test). Связка с GitOps очевидна: сами политики тоже лежат в Git и раскатываются Argo CD или Flux, как любой другой ресурс. Политики - это код, значит, они в репозитории, под ревью и с историей.

Грабли внедрения: сразу Enforce - и прод лежит

Главная ошибка новичка, набившая шишек половине индустрии: написать политику в режиме Enforce/deny и сразу выкатить на боевой кластер. Что происходит дальше? Старые Deployment с latest, легаси без requests, чей-то privileged-DaemonSet мониторинга - всё это при следующем рестарте Pod не сможет создаться. HPA не отскейлит, упавший Pod не поднимется, ночью у тебя инцидент. Admission блокирует не только новое, но и пересоздание существующего.

Правильный путь внедрения - в три шага:
  • Сначала Audit/warn. В Kyverno ставь validationFailureAction: Audit, в Gatekeeper enforcementAction: warn или dryrun. Политика работает, но ничего не блокирует - только пишет отчёты. Дай ей покрутиться дни-недели.
  • Собери нарушения. kubectl get policyreport -A в Kyverno или статус Constraint в Gatekeeper покажут реальный масштаб. Почини существующее, договорись с командами.
  • Только потом Enforce. И то лучше точечно - через exclude/match по namespace, чтобы kube-system и старые namespace не задеть.
Вторая грабля - забыть исключить системные namespace. Если политика "запрет privileged" заденет kube-system, ты сломаешь CNI, CSI и сам кластер: многие системные компоненты легитимно privileged. Всегда добавляй exclude для kube-system, kube-node-lease и namespace самого policy-движка.

Третья - webhook как точка отказа. Admission webhook синхронный: если pod Kyverno/Gatekeeper лёг, а failurePolicy: Fail - API-сервер начнёт отклонять запросы, и ты не сможешь даже починить кластер. Держи контроллер политик в нескольких репликах, следи за его здоровьем, и на критичных namespace продумывай failurePolicy осознанно. Антипаттерн - одна реплика движка с Fail на весь кластер.

Четвёртая, тихая - слишком широкий match без namespaceSelector. Политика на "все Pod" будет дёргаться на каждый системный Pod и тормозить admission. Сужай match, исключай служебное.

Мини-лаба: запрети latest и проверь в CI

Делай руками на любом кластере (kind, minikube, k3s - подойдёт всё).
  • Поставь Kyverno: helm repo add kyverno https://kyverno.github.io/kyverno/ && helm install kyverno kyverno/kyverno -n kyverno --create-namespace
  • Создай ClusterPolicy disallow-latest-tag из урока, но сначала с validationFailureAction: Audit.
  • Запусти kubectl run bad --image=nginx:latest - Pod создастся (режим Audit). Посмотри отчёт: kubectl get policyreport -A и найди там нарушение.
  • Переключи политику на Enforce, повтори kubectl run - теперь получишь отказ admission webhook. Сравни поведение.
  • Скачай kyverno CLI и прогони kyverno apply ./policy.yaml --resource ./bad-pod.yaml - убедись, что нарушение ловится без кластера. Это твой будущий шаг в CI.
  • Бонус: повтори тот же запрет на Gatekeeper через ConstraintTemplate + Constraint и сравни, насколько Rego многословнее YAML.
Контрольные вопросы
  • В какую фазу admission (mutating или validating) попадает политика "запретить latest", а в какую - "дописать метку team"? Почему порядок фаз важен?
  • Чем ConstraintTemplate отличается от Constraint в Gatekeeper и зачем их разделили на два объекта?
  • Почему нельзя выкатывать новую политику сразу в режиме Enforce/deny на боевой кластер и какая последовательность безопасна?
  • Зачем проверять политики и в CI, и в admission, если admission и так всё ловит?
Итог

Policy as code превращает разрозненные договорённости команд в исполняемые правила, которые кластер применяет сам. Kyverno даёт быстрый старт на YAML и силён в мутациях и генерации; OPA Gatekeeper берёт мощью Rego и переиспользуемыми шаблонами. Оба дополняют, а не заменяют PSS, и оба обязаны жить в Git и проверяться в CI до кластера. Запомни главное правило эксплуатации: начинай с Audit, собирай нарушения, исключай системные namespace - и только потом включай Enforce. Тогда политики защищают прод, а не роняют его.
👍4 ❤️2 🔥1 😄 🤔1
Аватара пользователя
Str3koza
Сообщения: 1
Зарегистрирован: 29 май 2026, 23:08

Re: Policy as code: Kyverno и OPA Gatekeeper

Сообщение Str3koza »

Полгода назад влетели ровно в эту грабли - выкатили запрет requests сразу в Enforce, и ночью HPA не смог поднять Pod. Теперь только через Audit, спасибо что разжевали последовательность.
👍1 ❤️1 🔥 😄 🤔2
Аватара пользователя
lesliecslim
Сообщения: 1
Зарегистрирован: 11 май 2026, 01:33

Re: Policy as code: Kyverno и OPA Gatekeeper

Сообщение lesliecslim »

А подскажите, если в кластере уже крутится OPA для авторизации в сервисах, есть смысл ставить ещё и Kyverno ради мутаций, или лучше всё тащить на Gatekeeper для единообразия? Не хочется два движка кормить.
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Admission и Pod Security: контроль на входе
Следующая глава →
GitOps: Argo CD и Flux

Все главы курса «Kubernetes: оркестрация контейнеров от основ до продакшена»

Поделиться темой: ✈ Telegram VK

Вернуться в «Kubernetes на практике»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость