Все уроки
DevOps и облакоПродвинутый10 мин чтенияПолезно знать

Helm

Пакетный менеджер для Kubernetes: шаблонизированные чарты, values для каждого окружения и релизы, которые можно откатить.

  • Charts
  • Values
  • Releases
  • Rollbacks
Шаг 1/4
Kubernetes — капитан. Вы говорите ему, чего хотите — «3 копии моего приложения», — и он это обеспечивает.

За 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) автоматически откатывает изменения, если обновление не стало здоровым вовремя.

bash
# 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 — версия разворачиваемого приложения, по умолчанию чисто информационная.

yaml
# 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 до того, как они попадут в кластер.

yaml
# 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.

bash
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

    Типизированные или программируемые языки конфигурации для команд, переросших текстовые шаблоны.

Лучшие практики

  1. Фиксируйте версии чартов и теги образов; никогда не деплойте в production плавающие latest-версии.
  2. В пайплайнах используйте helm upgrade --install --wait с --rollback-on-failure (или GitOps-контроллер).
  3. Держите переопределения окружений маленькими и в Git; --set — только для значений из CI вроде тега образа.
  4. Не кладите секреты в файлы values: ссылайтесь на Secret, созданные через External Secrets, Sealed Secrets или SOPS.
  5. Добавьте в CI values.schema.json, helm lint, kubeconform и helm diff для каждого изменения чарта.
  6. Повышайте версию чарта при каждом изменении и публикуйте в OCI-registry.
  7. Задавайте в своих чартах по умолчанию 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.

yaml
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 видны каждому сабчарту.

yaml
# 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

Реальные ситуации

01

Обновление оставило 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.

02

Обновление стороннего чарта через мажорную версию

Проблема

Чарт ingress-контроллера отстаёт на две мажорные версии. Команда много лет назад скопировала его полный values.yaml и боится обновляться.

Как решить

Сократите скопированный файл до реальных переопределений, сравнив его с helm show values для текущей версии. Прочитайте changelog и заметки об обновлении для обеих мажорных версий, выписав переименованные values и изменения CRD. Отрендерите обе версии через helm template и сравните или используйте helm diff upgrade против живого кластера. Сначала обновите staging, по одной мажорной версии за раз, проверяя CRD, которые Helm не обновляет автоматически из папки crds/. Следите за метриками и уровнем ошибок, затем повторите в production в спокойное время с планом отката.

03

Десять микросервисов с десятью скопированными чартами

Проблема

У каждого сервиса свой чарт, созданный через 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 при установке.

Есть идея продукта?Давайте создадим его.

Расскажите о своей идее, продукте или команде. Обычно отвечаю в течение одного рабочего дня.