Kustomize и управление конфигурацией без шаблонов

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

Kustomize и управление конфигурацией без шаблонов

Сообщение 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
Боль, которую мы лечим: один манифест на три окружения

У тебя есть Deployment, Service и ConfigMap. В dev ты хочешь одну реплику, образ с тегом latest и лимит памяти 256Mi. В stage - две реплики и образ по коммиту. В prod - шесть реплик, anti-affinity, лимит 2Gi и отдельный namespace. Что обычно происходит дальше? Появляются три почти одинаковые папки с тремя почти одинаковыми YAML, и через месяц они расходятся: в prod кто-то добавил probe, а в dev забыл, и баг ловится только на бою. Копипаста манифестов - это технический долг, который зреет молча.

Helm решает это Go-шаблонами: ты обмазываешь YAML конструкциями {{ .Values.replicas }}, и манифест перестаёт быть валидным YAML - это уже текстовый шаблон, который kubectl не понимает без рендера. Kustomize заходит с другой стороны. Идея простая до неприличия: храни чистый валидный YAML как базу, а различия между окружениями описывай отдельными наложениями (overlays), которые патчат базу. Никаких шаблонов, никакого нового языка - только Kubernetes YAML и операции наложения. Поэтому связку "kustomize kubernetes" так любят в GitOps: то, что лежит в git, можно прочитать глазами и скормить в kubectl as-is.

Kustomize встроен прямо в kubectl - флаг -k. Отдельный бинарь ставить не обязательно, хотя для свежих фич он бывает полезен (об этом ниже). Это и есть главная развилка в споре "helm vs kustomize": Helm - про упаковку и дистрибуцию чужого софта, Kustomize - про управление твоей собственной kubernetes конфигурацией по окружениям.

Изображение

Механика: base, overlays и как наложение работает под капотом

Сердце Kustomize - файл kustomization.yaml. Он не описывает ресурсы, он описывает, какие ресурсы взять и что с ними сделать. Когда ты запускаешь kubectl kustomize или kubectl apply -k, движок собирает граф: читает resources, применяет к ним трансформеры (генераторы, патчи, префиксы, лейблы) и на выходе печатает финальный плоский YAML. Важно понять: Kustomize ничего не отправляет в кластер сам по себе - он рендерит. apply -k = "отрендерь и примени". kubectl kustomize = "просто отрендерь, покажи".

Каноническая структура - база плюс наложения:

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

myapp/
  base/
    deployment.yaml
    service.yaml
    kustomization.yaml
  overlays/
    dev/
      kustomization.yaml
      replicas-patch.yaml
    prod/
      kustomization.yaml
      replicas-patch.yaml
      resources-patch.yaml
База - это полный рабочий комплект манифестов для абстрактного окружения. Её kustomization.yaml минималистичен:

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

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml
  - service.yaml

commonLabels:
  app: payment-api
apiVersion здесь - kustomize.config.k8s.io/v1beta1, это актуальная стабильная версия схемы Kustomize (не путай с apiVersion самих ресурсов вроде apps/v1). Поле resources - список того, что втягиваем; туда можно класть не только файлы, но и каталоги, и даже удалённые git-ссылки.

Наложение overlays/prod указывает на базу как на свой ресурс и добавляет различия:

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

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: payment-prod
namePrefix: prod-

resources:
  - ../../base

patches:
  - path: replicas-patch.yaml
  - path: resources-patch.yaml

images:
  - name: registry.cyberlake.ru/payment-api
    newTag: 1.8.4
Разберём по полям. namespace: payment-prod - проставит этот namespace всем ресурсам наложения, не трогая базу. namePrefix: prod- - добавит префикс ко всем именам (Deployment станет prod-payment-api), и что важно - Kustomize сам перепишет ссылки: если Service селектит Deployment или ConfigMap монтируется в Pod, имена в ссылках обновятся согласованно. Это не тупой sed, это понимание схемы ресурсов. images - переопределение образа без правки самого манифеста: ищем образ по name (исходное имя в манифесте) и подменяем тег через newTag (или весь образ через newName). Это то самое "images-override", ради которого половина людей и приходит в Kustomize - менять теги в pipeline одной строкой, не трогая Deployment.

Патчи: strategic merge против JSON 6902

Патч - это способ сказать "в базовом ресурсе поменяй вот это поле". Есть два диалекта, и понимать разницу критично.

Strategic merge patch - ты пишешь кусок того же ресурса, указываешь, кого патчить (по kind и name), и заполняешь только те поля, что хочешь изменить. Kustomize мёрджит это поверх базы по правилам Kubernetes:

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

apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-api
spec:
  replicas: 6
  template:
    spec:
      containers:
        - name: api
          resources:
            limits:
              memory: 2Gi
Тут вся соль в слове strategic. Обычный merge для массива containers просто заменил бы весь список. Но Kubernetes знает, что containers - это список с ключом name (merge key), поэтому Kustomize найдёт контейнер api и обновит только его resources, не снося остальные контейнеры и остальные поля. Это даёт хирургическую точность: меняешь лимит памяти, и больше ничего. Грабли: если в патче ты опечатался в name контейнера, merge key не совпадёт, и вместо изменения ты получишь второй контейнер. Манифест отрендерится без ошибки, а Pod упадёт - классическая ловушка.

JSON 6902 patch - это адресная операция по пути JSON Pointer: add, replace, remove, и т.д. Применяй, когда strategic merge не умеет: например, удалить элемент массива по индексу или поправить поле, у которого нет merge key.

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

patches:
  - target:
      kind: Deployment
      name: payment-api
    patch: |-
      - op: replace
        path: /spec/template/spec/containers/0/imagePullPolicy
        value: IfNotPresent
      - op: remove
        path: /spec/template/spec/tolerations/0
Заметь: в современном Kustomize единое поле patches принимает оба диалекта - он сам определяет по содержимому, strategic merge это или JSON6902. Старые поля patchesStrategicMerge и patchesJson6902 ещё работают, но помечены как устаревшие; в новых проектах пиши только patches с target. JSON6902 мощнее, но хрупче: путь привязан к индексам (containers/0), и стоит кому-то переставить контейнеры местами в базе - патч молча попадёт не туда.

Генераторы ConfigMap и Secret: почему хеш в имени спасает деплои

Тут Kustomize делает то, чего нет в голом kubectl и что в Helm приходится городить руками. configMapGenerator и secretGenerator создают ConfigMap/Secret из файлов или литералов - и добавляют к имени суффикс-хеш от содержимого:

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

configMapGenerator:
  - name: app-config
    literals:
      - LOG_LEVEL=info
      - FEATURE_X=true
    files:
      - app.properties

secretGenerator:
  - name: db-creds
    envs:
      - db.env
На выходе имя будет app-config-7h6t2k9m4d. Зачем хеш? Вот реальный сценарий: ты поменял LOG_LEVEL в ConfigMap и сделал apply. Если имя ConfigMap не изменилось, Deployment остаётся тем же - Kubernetes не видит причины пересоздавать Pod'ы, и они продолжают крутиться со старым конфигом, который читается только при старте. Конфиг обновился, а приложение об этом не знает. С генератором имя меняется на новый хеш, Kustomize перепишет ссылку в Deployment на новое имя, спецификация Pod-шаблона изменится - и rolling update произойдёт автоматически. Изменил конфиг - получил перекатку. Это огромный плюс для надёжности.

Поведением суффикса можно управлять через generatorOptions (например, disableNameSuffixHash: true, если хеш мешает - скажем, ConfigMap читает внешний оператор по фиксированному имени). По умолчанию хеш лучше оставлять.

Практика: смотрим, что реально отрендерится

Золотое правило Kustomize - всегда смотри вывод перед apply. Команда:

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

$ kubectl kustomize overlays/prod
печатает финальный YAML в stdout, ничего не применяя. Кусок вывода для нашего примера:

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

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: payment-api
  name: prod-payment-api
  namespace: payment-prod
spec:
  replicas: 6
  template:
    spec:
      containers:
        - image: registry.cyberlake.ru/payment-api:1.8.4
          name: api
          resources:
            limits:
              memory: 2Gi
Разбор: name стал prod-payment-api (отработал namePrefix), namespace - payment-prod, label app: payment-api подтянулся из commonLabels базы, replicas: 6 и memory: 2Gi прилетели из патчей, а image получил тег 1.8.4 из секции images. Всё сошлось в одном месте, и это валидный YAML - его можно git diff'ить, ревьюить в PR и хранить как источник истины.

Применяем:

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

$ kubectl apply -k overlays/prod
deployment.apps/prod-payment-api created
service/prod-payment-api created
Для отладки разницы между окружениями удобно сравнивать рендеры напрямую:

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

$ diff <(kubectl kustomize overlays/dev) <(kubectl kustomize overlays/prod)
Сразу видно, чем prod отличается от dev - построчно, без догадок. Это и есть прозрачность "overlays kubernetes", за которую любят подход.

Грабли и антипаттерны из практики
  • Версия kustomize в kubectl отстаёт. Встроенный в kubectl Kustomize - это вмороженная версия, обычно на пару релизов позади отдельного бинаря. Свежие фичи (например, новые трансформеры или replacements в полном объёме) могут не работать через kubectl -k. Если упёрся в "поле не распознано" - ставь отдельный kustomize (актуальная линейка v5.x) и рендери им: kustomize build overlays/prod | kubectl apply -f -.
  • Overlay-наследование больше двух уровней. Соблазн сделать base -> region-base -> prod-eu -> prod-eu-canary. Каждый уровень добавляет неявности: чтобы понять, что получится, надо держать в голове четыре файла. Глубокие цепочки наложений - путь к манифестам, которые никто не понимает. Держи плоско: одна база, тонкие наложения.
  • vars и хрупкие замены. Старый механизм vars для проброса значений (имя сервиса в аргументы) ненадёжен и выводится из обращения - используй современный трансформер replacements, который берёт значение из одного ресурса по пути и кладёт в другой. Не строй логику на vars в новых проектах.
  • commonLabels трогает селекторы. commonLabels проставляет лейблы и в metadata, и в spec.selector. Если ты докинул commonLabels в наложение поверх уже задеплоенного Deployment, selector у которого иммутабелен, apply упадёт. Для лейблов, которые не должны попадать в селекторы, есть labels с опцией includeSelectors: false.
  • Секреты в открытом YAML. secretGenerator кладёт значения в git как base64 - это не шифрование. Для GitOps используй sealed-secrets, SOPS или внешний vault, а в Kustomize держи только ссылки.
Когда Kustomize, когда Helm, а когда оба

Не воюй с инструментами, выбирай по задаче. Kustomize бери, когда это твои манифесты и тебе нужны окружения: dev/stage/prod, фиче-ветки, регионы. Чистый YAML, прозрачный diff, дружелюбность к GitOps - Argo CD и Flux умеют рендерить Kustomize нативно.

Helm бери, когда нужна упаковка и дистрибуция: ты ставишь чужое приложение (postgres-operator, ingress-nginx, cert-manager) из Artifact Hub, тебе важны версионирование релиза, rollback и values как публичный API чарта. Параметризовать сотню значений шаблонами проще, чем сотней патчей.

И связка - часто лучший ответ. Два рабочих паттерна. Первый: helm template печатает чистый YAML из чужого чарта, ты кладёшь его в base и накатываешь сверху свои overlays Kustomize - получаешь и экосистему Helm, и патчинг без форка чарта. Второй: Kustomize умеет вызывать Helm сам через helmCharts в kustomization.yaml (нужен флаг --enable-helm), рендерит чарт и патчит его на лету. Argo CD поверх этого даёт GitOps-обёртку. Так и работают зрелые платформенные команды - не "или-или", а Helm для пакетов, Kustomize для окружений.

Мини-лаба: собери три окружения руками
  • Создай base/ с deployment.yaml (nginx, 1 реплика) и kustomization.yaml, перечисли ресурс и добавь commonLabels.
  • Сделай overlays/dev/kustomization.yaml с resources: [../../base] и namePrefix: dev-. Запусти kubectl kustomize overlays/dev и убедись, что имя получило префикс.
  • Сделай overlays/prod/ с патчем replicas: 4 (strategic merge) и секцией images, меняющей тег nginx на конкретную версию.
  • Прогони diff <(kubectl kustomize overlays/dev) <(kubectl kustomize overlays/prod) и найди все различия.
  • Добавь в prod configMapGenerator с одним литералом, отрендери дважды, меняя значение, и убедись, что суффикс-хеш в имени меняется.
  • Примени dev в кластер: kubectl apply -k overlays/dev, проверь kubectl get deploy.
Контрольные вопросы
  • Чем принципиально отличается подход Kustomize от Helm и почему манифест Kustomize остаётся валидным YAML, а шаблон Helm - нет?
  • Зачем configMapGenerator добавляет хеш-суффикс к имени и как это связано с rolling update при изменении конфига?
  • В каком случае нужен JSON6902 patch вместо strategic merge, и почему привязка к индексу массива - это риск?
  • Назови два паттерна связки Helm и Kustomize и объясни, когда какой уместен.
Итог

Kustomize - это наложение, а не шаблонизация: чистая база плюс тонкие overlays на окружение. Ты получаешь валидный YAML, прозрачный diff, автоматическую перекатку при смене конфига через генераторы и идеальную совместимость с GitOps. Helm и Kustomize не конкуренты, а инструменты под разные задачи: Helm упаковывает и раздаёт, Kustomize настраивает под окружение, и в продакшене они отлично работают вместе.
👍4 ❤️4 🔥 😄 🤔4
Аватара пользователя
jesters
Сообщения: 1
Зарегистрирован: 01 июн 2026, 23:10

Re: Kustomize и управление конфигурацией без шаблонов

Сообщение jesters »

Наконец дошло, зачем хеш в имени конфигмапы - у меня поды реально не перекатывались после правки LOG_LEVEL, теперь понятно почему. Спасибо!
👍 ❤️ 🔥1 😄 🤔
Аватара пользователя
midnightsegfault
Сообщения: 1
Зарегистрирован: 04 июн 2026, 01:08

Re: Kustomize и управление конфигурацией без шаблонов

Сообщение midnightsegfault »

А вопрос по граблям с версией: я через kubectl -k получал ошибку про неизвестное поле, поставил отдельный kustomize v5 и собрал через kustomize build - заработало. Так что да, встроенная реально отстаёт.
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Helm глубже: шаблоны, хуки, зависимости, OCI
Следующая глава →
RBAC и аутентификация глубже

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

Поделиться темой: ✈ Telegram VK
Похожие запросы: Установка и первый конфиг nginxnginx в Docker

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

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

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