Helm
Пакетный менеджер для Kubernetes: шаблонизированные чарты, values для каждого окружения и релизы, которые можно откатить.
За 30 секунд
Helm упаковывает Kubernetes-приложения в чарты — папки с шаблонизированными манифестами и файлом values с разумными значениями по умолчанию. Вы устанавливаете чарт в кластер как релиз, переопределяете values для каждого окружения, обновляете его новыми values или версиями и одной командой откатываетесь к любой предыдущей ревизии. Это стандартный способ ставить стороннее ПО в Kubernetes и распространённый способ деплоить собственные сервисы.
Не в моём ежедневном стеке — я понимаю принципы и изучу детали, когда это понадобится проекту.
Почему это важно
- Kubernetes-манифесты одного приложения быстро разрастаются до сотен строк в Deployment, Service, Ingress и ConfigMap; Helm превращает их в один параметризованный пакет.
- Один и тот же чарт деплоится в dev, staging и production с разными файлами values вместо скопированного YAML, который со временем расходится.
- Релизы версионируются в кластере, поэтому обновления отслеживаются, а откат к проверенной ревизии — одна команда.
- Большинство open-source инфраструктуры (ingress-контроллеры, cert-manager, Prometheus, базы данных) поставляется с официальным Helm-чартом, так что знание Helm ожидают от любого, кто эксплуатирует Kubernetes.
Как это работает
01Чарты, values и релизы
Вся идея держится на трёх словах. Чарт — это пакет: папка с Chart.yaml (имя, версия, зависимости), values.yaml (настройки по умолчанию) и каталогом templates/ с манифестами Kubernetes, написанными на Go-шаблонах. Values — это входные данные: тег образа, число реплик, лимиты ресурсов, имена хостов. Релиз — это один установленный экземпляр чарта в кластере с именем и namespace; один и тот же чарт можно поставить несколько раз как разные релизы. Представьте чарт как рецепт с пропусками, values — как ваши ответы для пропусков, а релиз — как реально поданное блюдо с пронумерованной историей всех изменений. Helm рендерит шаблоны с values в обычные манифесты, отправляет их в Kubernetes API и записывает ревизию релиза как Secret в namespace релиза. Устанавливать серверный компонент не нужно.
02Установка и управление сторонними чартами
Чарты распространяются через OCI-registry (те же registry, где лежат контейнерные образы) или классические HTTP-репозитории чартов. Команды следуют простому жизненному циклу. helm install создаёт релиз; helm upgrade --install идемпотентен, и именно его используют пайплайны; helm list показывает релизы; helm history — ревизии; helm rollback возвращает к предыдущей ревизии; helm uninstall удаляет всё, что создал релиз. Перед установкой всегда изучайте values чарта по умолчанию и передавайте в своём файле только свои переопределения. Фиксируйте версию чарта через --version, чтобы новый upstream-релиз не стал сюрпризом. --wait заставляет Helm ждать, пока ресурсы будут готовы, а в Helm 4 флаг --rollback-on-failure (переименованный --atomic) автоматически откатывает изменения, если обновление не стало здоровым вовремя.
# Look before you install
helm show values oci://registry.example.com/charts/my-app --version 1.4.2 > defaults.yaml
# Install or upgrade, pinned, waiting for readiness
helm upgrade --install my-app oci://registry.example.com/charts/my-app \
--version 1.4.2 \
--namespace my-app --create-namespace \
-f values-production.yaml \
--wait --timeout 5m --rollback-on-failure
helm list -n my-app
helm history my-app -n my-app
helm rollback my-app 3 -n my-app
03Пишем собственный чарт
helm create my-app генерирует рабочий чарт для типичного веб-сервиса, который вы затем урезаете. Шаблоны читают values через {{ .Values.image.tag }}, встроенные объекты вроде {{ .Release.Name }} и {{ .Chart.AppVersion }} и функции вроде default, quote и toYaml. nindent ставит блоки YAML с правильным отступом — именно там прячется большинство багов в шаблонах. Общие фрагменты, например стандартные метки, кладут в _helpers.tpl как именованные шаблоны и подключают через include. Хорошо комментируйте values.yaml: это публичный интерфейс чарта. В Chart.yaml две версии: version — собственная семантическая версия чарта, которую повышают при каждом изменении чарта; appVersion — версия разворачиваемого приложения, по умолчанию чисто информационная.
# templates/deployment.yaml (excerpt)
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "my-app.fullname" . }}
labels:
{{- include "my-app.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "my-app.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "my-app.selectorLabels" . | nindent 8 }}
spec:
containers:
- name: app
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
resources: {{- toYaml .Values.resources | nindent 12 }}
04Values для каждого окружения
values.yaml в чарте хранит безопасные значения по умолчанию. Каждое окружение получает небольшой файл переопределений только с тем, что отличается: в production можно увеличить реплики, задать лимиты ресурсов, включить автомасштабирование и указать реальный хост. Передавайте несколько файлов через -f; более поздние файлы побеждают, а --set побеждает файлы, но оставьте --set для разовых значений вроде тега образа из CI, чтобы конфигурацию можно было ревьюить в Git. Никогда не кладите секреты в файлы values, закоммиченные в Git. Ссылайтесь на существующие Kubernetes Secret по имени или создавайте их инструментами вроде External Secrets Operator или Sealed Secrets, либо расшифровывайте во время деплоя плагином helm-secrets и SOPS. Файл values.schema.json в чарте проверяет values по JSON Schema при установке и ловит опечатки вроде replicaCont до того, как они попадут в кластер.
# values-production.yaml - only overrides
replicaCount: 3
image:
repository: registry.example.com/my-app
tag: "" # set by CI: --set image.tag=$GIT_SHA
ingress:
enabled: true
hosts:
- host: app.example.com
paths: [{ path: /, pathType: Prefix }]
resources:
requests: { cpu: 250m, memory: 256Mi }
limits: { memory: 512Mi }
existingSecret: my-app-env # created by External Secrets, not Helm
05Тестирование, упаковка и публикация
Относитесь к чарту как к коду с процессом релиза. helm lint проверяет структуру и типичные ошибки. helm template рендерит манифесты локально без кластера, так что можно посмотреть ровно то, что будет применено, или проверить это через kubeconform по схемам Kubernetes. helm upgrade --dry-run=server рендерит и валидирует на реальном API-сервере. Плагин helm diff показывает, что изменит обновление, — бесценно в pull request-ах. Тесты чарта — это поды в templates/tests с аннотацией хуков, которые запускаются через helm test после установки. Для публикации helm package создаёт версионированный .tgz, а helm push загружает его в OCI-registry. Подписывайте чарты и проверяйте подписи при установке ради целостности цепочки поставок, а в CI запускайте chart-testing (ct), чтобы линтить и устанавливать изменённые чарты во временный кластер, например kind.
helm lint ./charts/my-app
helm template my-app ./charts/my-app -f values-production.yaml | kubeconform -strict -summary
helm diff upgrade my-app ./charts/my-app -n my-app -f values-production.yaml
helm package ./charts/my-app # -> my-app-1.4.2.tgz
helm registry login registry.example.com
helm push my-app-1.4.2.tgz oci://registry.example.com/charts
Когда применять
- Установка стороннего ПО в Kubernetes, у которого есть официальный чарт.
- Деплой собственных сервисов в несколько окружений или кластеров из одного параметризованного пакета.
- Распространение приложения клиентам или другим командам, которые запускают его в своих кластерах.
- Нужны история релизов и простые откаты поверх обычного Kubernetes.
Когда не стоит
- Вы не на Kubernetes — для Docker Compose или PaaS Helm не нужен.
- Несколько простых манифестов с небольшими различиями между окружениями, где overlay-и Kustomize понятнее.
- Сложные day-two операции вроде failover и бэкапов базы данных, где оператор управляет жизненным циклом лучше шаблонов.
Альтернативы
- Kustomize
Overlay-и и патчи поверх обычного YAML без шаблонов, встроены в kubectl.
- Kubernetes-операторы
Контроллеры, которые постоянно управляют сложными stateful-приложениями, — не только установка и обновление.
- Argo CD / Flux
GitOps-контроллеры, которые часто рендерят Helm-чарты и держат кластер синхронизированным с Git.
- Timoni / CUE, jsonnet
Типизированные или программируемые языки конфигурации для команд, переросших текстовые шаблоны.
Лучшие практики
- Фиксируйте версии чартов и теги образов; никогда не деплойте в production плавающие latest-версии.
- В пайплайнах используйте helm upgrade --install --wait с --rollback-on-failure (или GitOps-контроллер).
- Держите переопределения окружений маленькими и в Git; --set — только для значений из CI вроде тега образа.
- Не кладите секреты в файлы values: ссылайтесь на Secret, созданные через External Secrets, Sealed Secrets или SOPS.
- Добавьте в CI values.schema.json, helm lint, kubeconform и helm diff для каждого изменения чарта.
- Повышайте версию чарта при каждом изменении и публикуйте в OCI-registry.
- Задавайте в своих чартах по умолчанию requests ресурсов, пробы и security context.
Частые ошибки
- Целиком копировать сторонний values.yaml в свой репозиторий, а потом пропускать изменения upstream-значений по умолчанию при обновлении.
- Ошибки отступов в шаблонах из-за забытого nindent или неправильной обрезки пробелов через {{-.
- Править ресурсы через kubectl после установки через Helm, создавая дрейф, который следующее обновление перезапишет.
- Хранить пароли в открытых файлах values в Git.
- Набивать шаблоны тяжёлой логикой, пока чарт не превратится в нечитаемый язык программирования.
Глубже
06Хуки, порядок и как они ломаются
Хуки — это ресурсы с аннотацией helm.sh/hook, которые Helm запускает в определённые моменты жизненного цикла: pre-install, pre-upgrade, post-upgrade, pre-delete, test и другие. Классический пример — Job миграции базы данных в pre-upgrade, чтобы миграции прошли до старта новых подов. helm.sh/hook-weight задаёт порядок хуков, а helm.sh/hook-delete-policy управляет очисткой, например before-hook-creation,hook-succeeded. Подводные камни: ресурсы хуков не управляются как часть релиза, поэтому uninstall их не удаляет, если этого не требует delete policy; упавший pre-upgrade хук блокирует обновление — и это хорошо, — но медленный может упереться в таймаут; хуки не идеально дружат с GitOps-инструментами, которые транслируют их в собственные фазы синхронизации. Делайте миграции идемпотентными и обратно совместимыми и задавайте Job-ам хуков backoffLimit и activeDeadlineSeconds.
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "my-app.fullname" . }}-migrate
annotations:
"helm.sh/hook": pre-upgrade,pre-install
"helm.sh/hook-weight": "0"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
backoffLimit: 1
activeDeadlineSeconds: 600
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
command: ["./migrate", "up"]
07Server-side apply, владение полями и дрейф
При client-side apply Helm вычислял трёхстороннее слияние между манифестом предыдущего релиза, новым манифестом и живым объектом. Изменения от других участников — например, HorizontalPodAutoscaler, меняющий число реплик, или чей-то kubectl edit — могли молча перезаписываться или, что хуже, теряться. Server-side apply, вариант по умолчанию для новых релизов в Helm 4, отправляет желаемый манифест и даёт API-серверу выполнить слияние, отслеживая в managedFields, какой менеджер владеет каждым полем. Если полем, которое Helm хочет задать, владеет другой менеджер, API сообщает о конфликте, а не молча воюет за него; --force-conflicts осознанно забирает владение. Практические следствия: не задавайте replicas в чарте, когда ими владеет автоскейлер; ждите явных ошибок конфликта там, где раньше были тихие перезаписи; проверяйте метод применения релиза через helm get metadata, прежде чем переводить старые релизы на server-side apply.
08Library-чарты, зависимости и паттерн umbrella
Зависимости объявляются в Chart.yaml: имя, диапазон версий, репозиторий и необязательное условие вроде postgresql.enabled; helm dependency update скачивает их в charts/ и фиксирует в Chart.lock. Umbrella-чарт объединяет несколько сабчартов в один релиз — удобно для платформенного набора, но рискованно для несвязанных сервисов, ведь тогда у них общий жизненный цикл релиза и общая зона отказа. Library-чарт (type: library) содержит только именованные шаблоны, без ресурсов, и позволяет многим чартам сервисов разделять одни и те же определения Deployment и Service, при этом у каждого остаются свои values. Это даёт стандарты на уровне организации для меток, проб и security context без копирования шаблонов. Родительские чарты могут переопределять values сабчарта под его именем, а global values видны каждому сабчарту.
# Chart.yaml
apiVersion: v2
name: my-app
version: 1.4.2
appVersion: "2.8.0"
dependencies:
- name: common-templates
version: ~0.6.0
repository: oci://registry.example.com/charts
- name: redis
version: ">=20.0.0 <21.0.0"
repository: oci://registry.example.com/charts
condition: redis.enabled
Реальные ситуации
Обновление оставило production наполовину задеплоенным
Проблема
Пайплайн запустил helm upgrade без ожидания. Новые поды падали из-за отсутствующей переменной окружения, релиз был помечен как deployed, а трафик частично попадал на падающие поды.
Как решить
Немедленно откатитесь: найдите последнюю рабочую ревизию через helm history и выполните helm rollback my-app <rev> --wait. Почините пайплайн: используйте helm upgrade --install --wait --timeout 5m --rollback-on-failure, чтобы нездоровое обновление откатывалось автоматически, а job заметно падал. Убедитесь, что у Deployment есть readiness-пробы, иначе Kubernetes считает падающие, но стартующие поды готовыми. Добавьте values.schema.json с этой переменной как обязательной, helm lint и шаг helm diff в pull request. Подумайте о деплое на staging с той же структурой values перед production.
Обновление стороннего чарта через мажорную версию
Проблема
Чарт ingress-контроллера отстаёт на две мажорные версии. Команда много лет назад скопировала его полный values.yaml и боится обновляться.
Как решить
Сократите скопированный файл до реальных переопределений, сравнив его с helm show values для текущей версии. Прочитайте changelog и заметки об обновлении для обеих мажорных версий, выписав переименованные values и изменения CRD. Отрендерите обе версии через helm template и сравните или используйте helm diff upgrade против живого кластера. Сначала обновите staging, по одной мажорной версии за раз, проверяя CRD, которые Helm не обновляет автоматически из папки crds/. Следите за метриками и уровнем ошибок, затем повторите в production в спокойное время с планом отката.
Десять микросервисов с десятью скопированными чартами
Проблема
У каждого сервиса свой чарт, созданный через helm create много лет назад. Метки, пробы и настройки безопасности различаются, и исправление безопасности приходится повторять десять раз.
Как решить
Создайте library-чарт с именованными шаблонами для Deployment, Service, Ingress, HPA и безопасного securityContext по умолчанию и опубликуйте его в OCI-registry с семантическими версиями. Переведите чарт одного сервиса на зависимость от него, оставив только values и небольшие шаблоны, специфичные для сервиса, и проверьте через helm template, что вывод эквивалентен. Остальные сервисы мигрируйте постепенно. Добавьте values.schema.json для стандартизации интерфейса. Теперь исправление — это релиз библиотеки плюс повышение версий, которые Renovate может автоматически предложить во всех репозиториях сервисов.
Шпаргалка
- Безопасная установка или обновление
helm upgrade --install my-app ./chart -n my-app --create-namespace --wait --rollback-on-failure- Показать значения чарта по умолчанию
helm show values oci://registry.example.com/charts/my-app --version 1.4.2- Отрендерить локально
helm template my-app ./chart -f values-production.yaml- Проверить на кластере
helm upgrade --install my-app ./chart --dry-run=server- Values живого релиза
helm get values my-app -n my-app --all- История релиза
helm history my-app -n my-app- Откат
helm rollback my-app 3 -n my-app --wait- Зафиксировать зависимости
helm dependency update ./chart- Упаковать и запушить
helm package ./chart && helm push my-app-1.4.2.tgz oci://registry.example.com/charts
Вопросы и ответы
Q1Чем отличаются чарт, релиз и ревизия?
Чарт — это пакет: шаблоны, values по умолчанию и метаданные, версионированные семантической версией. Релиз — это один установленный экземпляр этого чарта в кластере, определяемый именем и namespace; один и тот же чарт можно поставить дважды как два релиза. Ревизия — это пронумерованный снимок релиза: каждая установка, обновление или откат создаёт новую ревизию, в которой записаны отрендеренные манифесты и использованные values. Helm хранит ревизии как Secret в namespace релиза — на этом построены helm history и helm rollback. Откат к ревизии 3 на самом деле создаёт новую ревизию с содержимым ревизии 3, так что история никогда не переписывается.
Q2Helm или Kustomize?
Helm — это инструмент шаблонизации и упаковки: чарты с параметрами, версионированное распространение, зависимости и история релизов с откатом. Он хорош для стороннего ПО и для приложений, которые деплоятся во множестве вариаций. Kustomize работает с обычным YAML через overlay-и и патчи, без языка шаблонов, и встроен в kubectl; он отлично подходит, когда манифесты ваши, а окружения различаются слегка. Они не исключают друг друга. Многие команды ставят ПО вендоров через Helm, а свои сервисы ведут через Kustomize, и Kustomize даже умеет разворачивать Helm-чарты. Моё правило: если нужно распространять или сильно параметризовать — Helm; если хочется читать ровно тот YAML, который применяется, — Kustomize.
Q3Как работает helm rollback и что он не отменяет?
helm rollback my-app 3 берёт манифесты, сохранённые в ревизии 3, и снова применяет их как новую ревизию, так что Deployment, ConfigMap и другие ресурсы возвращаются в то состояние. Он не отменяет того, что лежит за пределами объектов Kubernetes: выполненные миграции базы данных, данные, записанные новой версией, или внешние ресурсы вроде DNS или облачных бакетов. Он также не может без проблем восстановить ресурсы, удалённые вручную в обход Helm, а PersistentVolumeClaim обычно сохраняют данные в любом случае. Поэтому изменения базы данных должны быть обратно совместимыми, а миграции — выполняться отдельными осознанными шагами. Откат надёжен для stateless-конфигурации и образов, но не для состояния.
Q4Как управлять секретами в Helm?
Правило такое: значения секретов не живут в открытых файлах values в Git. Варианты в порядке моего предпочтения: чарт ссылается на существующий Secret по имени, а этот Secret создаёт External Secrets Operator из облачного менеджера секретов или Vault; Sealed Secrets, который шифрует секреты ключом кластера, так что зашифрованная форма может лежать в Git; или SOPS с плагином helm-secrets, чтобы расшифровывать зашифрованные файлы values во время деплоя. Помните также, что Helm хранит отрендеренные манифесты в Secret релиза, так что любой, кто может читать Secret в namespace, может прочитать и values, переданные в Helm. Поэтому RBAC на namespace важен не меньше, чем способ передачи values.
Q5Что изменилось в Helm 4?
Helm 4, вышедший в конце 2025 года, — первая мажорная версия после Helm 3 в 2019 году. Главное изменение — server-side apply для новых установок: Kubernetes сам сливает изменения и отслеживает владение полями, заменяя клиентское трёхстороннее слияние Helm 3; существующие релизы сохраняют прежний метод, если вы явно не переключите их. Несколько флагов переименовали, например --atomic стал --rollback-on-failure, а --force — --force-replace. Появилась новая система плагинов с плагинами на WebAssembly, более точная проверка готовности через kstatus и улучшенная поддержка OCI, включая установку по digest. Чарты на API v2 продолжают работать, а хранилище релизов совместимо, так что существующими релизами Helm 3 можно управлять из Helm 4 без миграции. Helm 3 ограниченное время получает только исправления безопасности.
Вопросы уровня senior
S1Как Helm сочетается с GitOps-инструментами вроде Argo CD?
Argo CD и Flux умеют рендерить Helm-чарты и применять результат, постоянно сверяя кластер с Git. В этой модели Helm — в основном формат шаблонизации и упаковки: Argo CD рендерит через helm template и применяет собственным движком, поэтому helm list ничего не показывает, а история Argo заменяет ревизии Helm. Helm-controller во Flux, напротив, выполняет настоящие Helm-релизы. Плюсы GitOps: обнаружение дрейфа, отсутствие credentials кластера в CI и Git как аудит-лог. Минусы: хуки неидеально ложатся на фазы синхронизации, а отладка растягивается на два инструмента. Я бы держал чарты универсальными и версионированными в registry, values окружений — в GitOps-репозитории, а CI позволил бы только повышать там теги образов или версии чартов через pull request-ы.
S2Когда шаблоны Helm становятся неправильной абстракцией?
Текстовая шаблонизация YAML работает, пока в чартах не накопятся глубокие условия, циклы по вложенным map и десятки переключателей. Тогда никто не может предсказать вывод, values.yaml превращается в недокументированный API с сотнями ключей, а мелкие правки ломают отступы в малопонятных ветках. Признаки, что пора меняться: вы пишете логику, а не параметры; тестируете чарты в основном рендерингом и разглядыванием; один чарт пытается обслуживать принципиально разные формы деплоя. Варианты: разбить на чарты поменьше с общим library-чартом; вынести вариативность в overlay-и Kustomize поверх отрендеренного вывода; использовать типизированный язык конфигурации вроде CUE или инструмент вроде Timoni; а для сложной логики жизненного цикла — написать оператор. Компромисс — охват экосистемы: Helm понимают все, а альтернативы требуют новых навыков.
S3Как спроектировать чарт, который будут ставить другие команды или клиенты?
Относитесь к values.yaml как к публичному API с семантическим версионированием: переименование ключа — это breaking change и требует мажорной версии. Давайте безопасные рабочие значения по умолчанию: securityContext без root, requests ресурсов, пробы и никаких захардкоженных хостов. Валидируйте через values.schema.json и документируйте каждое значение. Поддерживайте паттерн existingSecret вместо требования класть секреты в values. Делайте необязательные компоненты условными и избегайте ресурсов уровня кластера без явной необходимости — у многих установщиков нет cluster-admin. Аккуратно обращайтесь с CRD: Helm устанавливает, но не обновляет те, что лежат в crds/; многие проекты поставляют их отдельным чартом. Подписывайте и публикуйте в OCI-registry, ведите changelog с заметками об обновлении и тестируйте пути установки и обновления в CI через chart-testing на kind.
Глоссарий
- Chart
- Пакет Helm: шаблоны, values по умолчанию и метаданные Kubernetes-приложения.
- Release
- Один установленный экземпляр чарта в кластере с именем и namespace.
- Revision
- Пронумерованный снимок релиза, который создаётся при каждой установке, обновлении или откате.
- Values
- Входные параметры конфигурации, заполняющие шаблоны чарта, из файлов или --set.
- Hook
- Ресурс, который Helm запускает в определённый момент жизненного цикла, например Job миграции перед обновлением.
- Library chart
- Чарт только с переиспользуемыми именованными шаблонами, которые используют другие чарты.
- OCI registry
- Container registry, который также хранит Helm-чарты как версионированные артефакты.
- Server-side apply
- Kubernetes сливает желаемое состояние на API-сервере и отслеживает, какой инструмент владеет каждым полем.
- values.schema.json
- JSON Schema в чарте, которая проверяет переданные values при установке.
Что изучить дальше
Есть идея продукта?Давайте создадим его.
Расскажите о своей идее, продукте или команде. Обычно отвечаю в течение одного рабочего дня.