Управление множеством релизов: Helmfile
Когда релизов в кластере десятки, командами helm install их не уследить. Helmfile описывает их все одним декларативным файлом.
Helmfile — отдельный инструмент (не часть Helm), который описывает желаемый набор релизов в файле
helmfile.yamlи приводит кластер к этому состоянию одной командой. Это «декларативный Helm поверх Helm».
Проблема, которую решает Helmfile
Реальный кластер — это не один релиз, а набор: ваше приложение, БД, ingress-контроллер, мониторинг, очередь. Держать в голове десяток helm upgrade --install с правильными флагами, версиями и values — источник ошибок. Helmfile превращает это в один версионируемый файл-манифест релизов.
Проблема не только в количестве команд, но и в повторяемости и порядке. Когда деплой живёт в виде bash-скрипта или, хуже, в голове дежурного, неизбежно расходятся окружения: на staging кто-то поставил версию чарта руками и забыл, на prod флаг --set отличается на один символ. Helmfile делает состояние кластера декларативным: то, что лежит в git, и есть то, что должно быть развёрнуто. К этому добавляется управление зависимостями между релизами — БД должна подняться раньше приложения, ingress-контроллер раньше Ingress-ресурсов; Helmfile умеет выстраивать релизы в нужной последовательности, чего голый набор helm install не гарантирует.
Стоит сразу очертить границу применимости. Helmfile — это императивный CLI-инструмент: кто-то (человек или CI-джоба) должен его запустить, чтобы изменения доехали. Он отлично ложится в pull-based пайплайны, но если вы строите полноценный GitOps с контроллером в кластере, который сам подтягивает изменения, то прямой конкурент Helmfile — это Argo CD или Flux. Выбор между ними — отдельный архитектурный вопрос; Helmfile проще и не требует ничего ставить в кластер, контроллеры — мощнее в части автосинхронизации и наблюдаемости дрейфа.
helmfile.yaml
repositories:
- name: bitnami
url: https://charts.bitnami.com/bitnami
releases:
- name: web
namespace: prod
chart: ./charts/webapp
version: 1.2.0
values:
- values/base.yaml
- values/prod.yaml
- name: orders-db
namespace: data
chart: bitnami/postgresql
version: 13.2.24
values:
- values/postgres-prod.yaml
- name: ingress
namespace: ingress
chart: bitnami/nginx-ingress-controller
version: 11.x.x
Основные команды
helmfile diff # показать разницу между файлом и кластером (как plan)
helmfile apply # привести кластер к желаемому состоянию (diff + sync)
helmfile sync # применить все релизы безусловно
helmfile destroy # удалить все описанные релизы
helmfile apply — рабочая лошадка: он сначала считает diff, и применяет только изменившиеся релизы. Это похоже на terraform apply, но для Helm-релизов.
Разница между apply и sync на практике важна. sync прогоняет helm upgrade --install по всем релизам безусловно — он надёжен, но медленный и шумный: даже неизменившиеся релизы переустанавливаются, ревизия инкрементируется, в истории появляется лишний шум. apply экономнее: он сначала просит у helm-diff разницу и трогает только то, что реально поменялось, — поэтому в CI обычно используют именно его. Однако у apply есть тонкость: если diff пуст из-за того, что helm-diff чего-то «не увидел» (например, изменения внутри хука или внешнего ресурса), apply может пропустить релиз. Поэтому периодический «выравнивающий» sync — здоровая практика.
Связку helmfile diff в pull request'е стоит выделить отдельно: она превращает ревью инфраструктуры в осмысленный процесс. Ревьюер видит не «поменялся values-файл на три строки», а конкретные изменения в манифестах кластера — какой Deployment получит новый образ, какому ресурсу поднимут лимиты. Это тот же план-перед-применением, что делает Terraform-воркфлоу безопасным, и одна из главных причин, по которой команды вообще берут Helmfile.
Окружения внутри Helmfile
Helmfile умеет окружения «из коробки» — один файл обслуживает dev/staging/prod:
environments:
dev:
values:
- envs/dev.yaml
prod:
values:
- envs/prod.yaml
releases:
- name: web
namespace: {{ .Environment.Name }}
chart: ./charts/webapp
values:
- values/{{ .Environment.Name }}.yaml
helmfile -e prod apply # применить набор релизов для prod
helmfile -e dev diff # посмотреть разницу для dev
DRY и шаблонизация helmfile
Helmfile сам поддерживает Go-шаблоны, что убирает дублирование между релизами: общие настройки выносят в якоря YAML или шаблонные переменные. Можно описать «базовый релиз» и переопределять лишь отличия — тот же принцип DRY, но на уровне набора релизов, а не одного чарта.
На практике это разворачивается в несколько приёмов. Когда файл с релизами разрастается, его дробят на части и подключают через helmfiles: — например, отдельный файл на инфраструктуру (ingress, мониторинг) и отдельный на продуктовые сервисы. Повторяющиеся куски — общий список values, лейблы, настройки таймаутов — выносят в YAML-якоря или в шаблонные значения из секции environments, чтобы поменять однажды и применить везде. Здесь же кроется и риск: легко увлечься и навертеть в helmfile.yaml столько Go-шаблонов и условий, что файл станет нечитаемым «вторым языком программирования» поверх чартов. Хорошее правило — держать helmfile тонким: он отвечает за то, какие релизы, где и с какими values, а вся логика рендера манифестов остаётся внутри чартов.
Отдельно стоит сказать про секреты в связке с Helmfile. Подходы из предыдущего урока никуда не деваются: Helmfile интегрируется с плагином helm-secrets и умеет подставлять зашифрованные sops-файлы как обычные values, поэтому единый декларативный файл может ссылаться и на открытые, и на зашифрованные значения, не нарушая GitOps-дисциплину.
Где Helmfile, а где Helm
| Уровень | Инструмент |
| Один чарт/релиз: шаблоны, values | Helm |
| Набор релизов кластера, окружения, порядок | Helmfile |
Как Helmfile работает под капотом
Helmfile — это оркестратор поверх CLI Helm. Он читает helmfile.yaml, рендерит его собственные шаблоны (подставляя окружение), и для каждого релиза формирует и вызывает соответствующую команду helm upgrade --install с нужными -f, --version, namespace. helmfile diff опирается на плагин helm-diff, сравнивая желаемые манифесты с текущими. То есть Helmfile ничего не делает с кластером напрямую — он лишь детерминированно генерирует и запускает правильные команды Helm в правильном порядке, обеспечивая, что состояние кластера соответствует декларации в git. Это делает его естественным звеном GitOps-пайплайна.
Частые ошибки
- Тащить логику чарта в Helmfile. Helmfile управляет набором релизов, а шаблоны манифестов — дело чарта.
- Забыть плагин
helm-diff. Без негоhelmfile diff/applyне покажет разницу. - Не фиксировать версии чартов. Как и в Helm, без
versionнабор «уплывёт».
Итог
- Helmfile декларативно описывает набор релизов кластера в одном файле;
applyприводит кластер к нему. - Встроенные окружения (
-e prod) и шаблонизация дают DRY на уровне набора релизов. - Под капотом — оркестратор, генерирующий правильные команды Helm; естественно ложится в GitOps.