Kustomize появился как ответ на боль от копирования одинаковых YAML-манифестов по окружениям. Вместо шаблонов с подстановками он предлагает наложение слоёв: есть базовый набор ресурсов и есть overlay, который меняет только отличия. В 2026 году Kustomize встроен в kubectl, поэтому отдельная установка нужна не всегда, а Helm и Kustomize спокойно сосуществуют в одном GitOps-репозитории. Разберём структуру каталогов, патчи, генераторы и проверку результата до применения в кластер.
Зачем нужен Kustomize при живом Helm
Helm хорош, когда приложение поставляется как продукт: чарт, values, версия, релиз. Kustomize хорош, когда манифесты уже есть и нужно собрать из них несколько вариантов — dev, stage, prod — без языка шаблонов. Он не хранит состояние релиза, не умеет условий и циклов и не подставляет значения в произвольные места. Зато результат полностью детерминирован, а kustomize build можно сравнить с тем, что реально лежит в кластере.
На практике их комбинируют: Helm рендерит сторонние компоненты, а Kustomize накладывает правки площадки — ресурсы, реплики, теги образов. Есть и обратный порядок, когда чарт генерируется из kustomize-сборки, но он сложнее в поддержке.
Структура: base и overlay
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
База содержит ресурсы без привязки к среде. Overlay ссылается на неё и добавляет отличия: namespace, префикс имён, тег образа, количество реплик.
# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namespace: shop-prod
namePrefix: prod-
labels:
- pairs:
env: prod
includeSelectors: true
images:
- name: registry.example.com/shop/api
newTag: "1.42.0"
replicas:
- name: api
count: 3
Поле labels с includeSelectors заменило устаревшее commonLabels: метка попадает и в селектор, поэтому меняется вместе с шаблоном пода. Старое поле bases в Kustomize 5 удалено, вместо него везде используется resources. Если в репозитории остались bases или patchesStrategicMerge, сборка предупредит или упадёт — миграция занимает минуты, но её лучше сделать заранее.
Патчи: strategic merge и JSON 6902
Патч strategic merge выглядит как частичный манифест: вы описываете только те поля, которые меняете. Для списков Kubernetes применяет стратегию слияния по ключу, поэтому контейнер с тем же именем обновляется, а не дублируется.
# overlays/prod/patch-resources.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
template:
spec:
containers:
- name: api
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
memory: 1Gi
Сам по себе файл ничего не меняет: его нужно подключить в kustomization.yaml overlay. Без строки patches он останется мёртвым грузом, и вы будете долго искать, почему ресурсы не применились.
# overlays/prod/kustomization.yaml
patches:
- path: patch-resources.yaml
Имя в патче указывайте такое, как в базе: префикс из overlay добавляется на этапе трансформации имён, и с namePrefix исходное имя иногда приходится проверять отдельно.
Тот же раздел patches принимает JSON 6902: он удобен для полей, которые нужно именно заменить или удалить.
# фрагмент overlays/prod/kustomization.yaml
patches:
- target:
kind: Deployment
labelSelector: app=api
patch: |-
- op: replace
path: /spec/template/spec/containers/0/resources/limits/memory
value: 2Gi
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: APP_ENV
value: production
Путь с индексом 0 хрупок: порядок контейнеров может измениться. Если есть возможность, используйте strategic merge с именем контейнера. Селектор в target надёжнее имени, когда включён namePrefix.
Генераторы ConfigMap и Secret
Kustomize умеет собирать ConfigMap и Secret из файлов и литералов и добавлять к имени хеш содержимого. Хеш меняет имя объекта, Deployment ссылается на новое имя, и поды перезапускаются при изменении конфигурации. Это поведение по умолчанию, и его стоит сохранять.
configMapGenerator:
- name: app-config
literals:
- LOG_LEVEL=info
- FEATURE_FLAGS=orders,search
files:
- nginx.conf
secretGenerator:
- name: db-auth
envs:
- db.env
generatorOptions:
immutable: false
Секреты из db.env не должны попадать в Git: в репозитории держите шаблон, а реальные значения подставляйте в CI из хранилища секретов. disableNameSuffixHash: true отключает хеш, но тогда обновление ConfigMap не приведёт к перезапуску подов — это частая причина ситуации «конфиг изменил, а приложение работает по-старому».
Компоненты и порядок слоёв
Компонент — переиспользуемый кусок конфигурации, который подключается не ко всем средам. В отличие от базы, компонент не является самостоятельным приложением: это набор патчей и ресурсов, который добавляют в overlay выборочно.
# components/observability/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
patches:
- path: metrics-sidecar.yaml
target:
kind: Deployment
В overlay достаточно добавить components: [../../components/observability]. Порядок применения фиксирован и не зависит от того, как ключи расположены в файле. Поэтому не рассчитывайте, что патч одного overlay переопределит патч другого: конфликтующие правки лучше держать в одном слое и проверять собранный результат целиком.
Проверка и типичные ошибки
kustomize build overlays/prod
kubectl diff -k overlays/prod
kubectl apply -k overlays/prod --dry-run=server
(cd overlays/prod && kustomize edit set image registry.example.com/shop/api=registry.example.com/shop/api:1.43.0)
kubectl diff показывает, что изменится, и не трогает кластер. --dry-run=server прогоняет валидацию API, включая вебхуки и квоты. Если сборка не находит файл за пределами каталога, помогает флаг --load-restrictor LoadRestrictionsNone, но злоупотреблять им не стоит: он ломает воспроизводимость.
- Namespace не переименовывает ClusterRole и CRD. Это кластерные ресурсы, префикс к ним не применяется автоматически.
- ConfigMap стал неизменяемым. При
immutable: trueповторное применение с новыми данными упадёт; удалите объект или снимите флаг. - Патч не применяется. Проверьте
target, имя и метки: селектор должен совпадать с исходным ресурсом. - Дубликаты ресурсов. Два overlay подключают один и тот же файл; Kustomize сообщит об этом, а не молча перезапишет.
- Секреты в открытом виде.
secretGeneratorне шифрует данные, он лишь кодирует их в base64.
Чек-лист перед применением
- Сборка воспроизводится из чистого клона без локальных файлов.
kubectl diffпоказывает только ожидаемые изменения.- Тег образа зафиксирован, а не равен
latest. - Секреты приходят из CI или внешнего хранилища, а не из Git.
- Есть план отката: предыдущий коммит и известная рабочая ревизия.
Итог
Kustomize остаётся самым предсказуемым способом собрать манифесты для нескольких сред: база хранит общее, overlay — отличия, патчи описывают только изменяемые поля. В 2026 году его достаточно иметь в составе kubectl, а отдельный бинарник нужен лишь для CI и старых скриптов. Начните с двух overlay и одного strategic merge патча, проверьте результат через kubectl diff — и только потом добавляйте компоненты и генераторы.