Заметка

Helm против Kustomize в 2026: кто рендерит и откуда берётся чарт

Helm vs Kustomize в 2026: под Argo CD половина аргументов спора не работает — релизов Helm там нет, а граница между движками проходит по тому, кто умеет предъявить реестру учётные данные.

Сколько Helm-релизов в кластере, если приложения раскатывает Argo CD? Ноль. helm ls пуст, helm history нечего показывать. Argo CD использует Helm как шаблонизатор: запускает helm template, получает манифесты и применяет их своим движком — helm install не вызывается никогда. Это написано в FAQ проекта прямым текстом и видно в коде (util/helm/helm.go).

Отсюда первое следствие для спора «Helm против Kustomize»: доводы про историю релизов, helm rollback и управление жизненным циклом через хуки в GitOps-контуре не работают. Историю и откат даёт Argo CD — argocd app history, argocd app rollback.

Сравнивать остаётся три вещи: язык описания конфигурации, происхождение артефакта и способность достать артефакт из закрытого реестра. Первое — дело вкуса. Два других в 2026 году превратились в жёсткие технические ограничения.

Что изменилось за год

Ветка Helm 3 закрывается. Релиз 3.22.0 (сентябрь 2026) — последний минор, и выпущен он ограниченно: только обновление Kubernetes client-библиотек под новые версии кластеров. Security-патчи идут до 10 февраля 2027. Изначально в HIP-0012 стоял ноябрь 2026, мейнтейнеры добавили три месяца. После февраля 3.x не получает ничего. Актуальный Helm — 4.3.0 из того же сентябрьского окна.

Для GitOps важнее другое. Начиная с Argo CD 3.5 чарты рендерит только Helm 4. Поле spec.source.helm.version игнорируется — оставлять его можно, эффекта нет. В 3.5.3 вшит Helm 4.2.1; ветки 3.3 и 3.4, пока поддерживаемые, работают на Helm 3.19.4. Версию шаблонизатора в пайплайне определяет версия Argo CD, а не то, что установлено у инженера локально.

Отсюда практическое следствие для CI. Если пайплайн гоняет helm template или helm diff бинарником Helm 3, а кластерный Argo CD уже на 3.5, вы сравниваете вывод двух разных рендереров и надеетесь, что они совпадут. Версию Helm в CI стоит выровнять по версии Argo CD, а не по тому, что лежит в образе раннера. Топология тут решает: в hub-and-spoke рендерит один Argo CD на хабе, значит версия Helm в контуре одна; при Argo CD в каждом кластере кластеры расходятся по минорам Argo CD, а вместе с ними — по версиям шаблонизатора.

Kustomize живёт тише: 5.8.2 вышел 30 сентября 2026, предыдущие миноры — 5.8.1 в феврале и 5.8.0 в ноябре 2025. Три релиза за одиннадцать месяцев. Встроенная в kubectl версия отстаёт от отдельного бинарника: kubectl 1.36 и 1.37 несут 5.8.1, Argo CD 3.5.3 — тоже 5.8.1.

Апгрейд, который ломает внутренний реестр

Переход Argo CD на Helm 4 принёс неочевидное breaking change — OCI-реестры без TLS. Helm 4 требует явного --plain-http там, где предшественник обходился без него. В Argo CD это лечится флагом в repository-Secret:

stringData:
  insecureOCIForceHttp: "true"

Тонкость сидит в зависимостях. Если в Chart.yaml указан repository: oci://… на реестр без TLS, этот реестр нужно отдельно зарегистрировать в Argo CD как репозиторий с тем же флагом. Под Helm 3 такие зависимости тянулись прозрачно, без регистрации. Есть и готовая ловушка: при одновременно выставленных --insecure-skip-server-verification и --insecure-oci-force-http Helm 4 молча теряет --plain-http.

Где они уживаются и где связка ломается

Смешивать движки приходится почти всегда: чужой чарт плюс свои значения. Рабочих способов два.

Первый — multi-source Application. Чарт берётся из Helm- или OCI-реестра, values лежат в Git и подключаются через ref и $values. Учётные данные остаются в repository-Secret'ах Argo CD, где им и место.

spec:
  sources:
    - repoURL: https://prometheus-community.github.io/helm-charts
      chart: prometheus
      targetRevision: 15.7.1
      helm:
        valueFiles:
          - $values/charts/prometheus/values.yaml
    - repoURL: https://git.example.com/org/value-files.git
      targetRevision: dev
      ref: values

Второй способ — Kustomize с полем helmCharts: чарт инфлейтится внутри Kustomize, сверху ложатся патчи. Им ставят Argo Rollouts и половину CRD-операторов. Потолок у этого способа есть, и он задокументирован.

Флаг --enable-helm, без которого helmCharts не работает, включается в Argo CD только глобально — через kustomize.buildOptions: --enable-helm в argocd-cm, сразу на все Kustomize-приложения. Пер-апповой опции для него нет, альтернатива — писать CMP-плагин.

Аутентификации нет вообще. Генератор собирает команду helm pull без единого флага учётных данных: ни --username, ни --password, ни --registry-config, ни --plain-http. Repository-Secret'ы Argo CD до этого пути не доходят, они действуют только для native Helm source. Схему oci:// генератор поддерживает — для неё в pullCommand() отдельная ветка, — но приватный реестр или реестр без TLS декларативно настроить нечем. На внутреннем Harbor без TLS связка «Kustomize + helmCharts + Argo CD 3.5» не соберётся вообще.

Мейнтейнеры Kustomize этого не скрывают: генератор задуман ограниченным подмножеством Helm, в него принимают багфиксы, критические security-правки и флаги-аналоги helm template. Поддержку приватных реестров и аутентификации добавлять не планируют — следующая итерация должна стать KRM-функцией. Читать этот документ стоит как заявление о намерениях: в нём же обещано не добавлять OCI, а схема oci:// в коде генератора уже есть.

Хуки и детерминизм

Argo CD распознаёт helm.sh/hook и раскладывает хуки по своим фазам, но соответствие неполное, и различия меняют поведение:

  • pre-install и pre-upgrade схлопываются в PreSync. Argo CD не различает первую установку и обновление, для него всё — синк. Джоба миграции БД с "helm.sh/hook": pre-upgrade,pre-install пойдёт на каждом синке. Если она не идемпотентна, узнаете об этом по данным.
  • pre-rollback, post-rollback, test-success, test-failure и hook-delete-timeout игнорируются.
  • Один собственный argocd.argoproj.io/hook в приложении выключает все Helm-хуки.

Второй сюжет — детерминизм рендера. helm template перезапускается на каждом сравнении с кластером. Чарт, который генерирует пароль через randAlphaNum, даёт вечный OutOfSync: значение меняется на каждом рендере, diff не сходится никогда. Лечится фиксацией значения в values. У чистого Kustomize, без helmCharts, этого класса проблем нет по построению: вывод kustomize build детерминирован. Сильная сторона здесь именно детерминизм; «отсутствие шаблонов» само по себе ничего не гарантирует — заверните чарт в helmCharts, и отрендерится тот же шаблон с тем же randAlphaNum и тем же вечным OutOfSync.

За детерминизм Kustomize берёт свою цену: патч живёт отдельно от объекта, и чтобы увидеть итоговый манифест, оверлей надо собрать. Плюс накопленный техдолг в старых деревьях. С версии 5.0.0 объявлены устаревшими patchesStrategicMerge, patchesJson6902 и vars, а bases — ещё с 2.1.0; замена — patches, replacements и resources, конвертирует kustomize edit fix. Из kustomize.config.k8s.io/v1beta1 их не удалят, но в будущий v1 API не возьмут, так что чинить придётся. Автоконвертация vars → replacements способна перезаписать много файлов и изменить вывод сборки — делайте её в чистом git-дереве.

Что бы ни рендерило манифесты, проверять надо результат рендера, а не исходник. Политики на выходе helm template и kustomize build ловят то, что до кластера доезжать не должно, независимо от выбора движка.

Когда что выбирать

СитуацияВыборПочему
Чужой чарт из публичного или приватного реестраNative Helm source + values через multi-sourceУчётки, TLS и OCI-флаги живут в repository-Secret'ах
Свои манифесты, много окруженийKustomize: базы и оверлеиДетерминированный вывод, патчи вместо ветвления шаблонов
Чарт надо поправить сверху: аннотации, лимиты, sidecarKustomize + helmChartsПатч поверх рендера, но реестр обязан быть публичным и по TLS
Внутренний реестр без TLS или с авторизациейТолько native Helm sourceГенератор helmCharts не передаёт ни учётки, ни --plain-http
Чарт уходит внешнему потребителюHelmВерсионирование чарта, зависимости, OCI-дистрибуция

Что из этого следует

Выбор диктует происхождение артефакта. Там, где чарт надо достать из реестра, требующего учётные данные или работающего без TLS, рабочий путь остаётся один — native Helm source в Argo CD: только у него есть канал к repository-Secret'ам. Там, где манифесты свои и лежат в том же Git, Kustomize даёт детерминированную сборку и патчи вместо ветвящихся шаблонов. Вкус к Go-шаблонам в этом решении не участвует; участвует то, кому реестр откроет дверь.

© 2026 axyi.ru · CC BY 4.0