Helm: чарты и values
Helm — пакетный менеджер Kubernetes. Chart шаблонизирует манифесты плейсхолдерами Go-template; values переопределяют их под окружение; а install/upgrade/rollback ведут именованный версионированный release, которого голый kubectl apply не даёт.
У тебя один сервис в трёх окружениях. Dev хочет 1 реплику и образ :dev; staging — 2 и :rc; prod — 6, другой бюджет ресурсов и запиненный тег :1.8.3. Всё остальное в манифесте идентично. Поэтому команда копирует deployment.yaml в три папки, и теперь изменение readiness-пробы надо вносить руками в трёх местах — и кто-то всегда один пропускает. Хуже: когда прод-раскатка падает в 2 ночи, у kubectl apply нет «отмены». Нет записи, каким был предыдущий рабочий манифест, нет одной команды, чтобы его вернуть. Ты диффишь YAML против истории своего шелла.
Проблема: голый YAML дублируется, а у kubectl apply нет отмены
У обычного YAML Kubernetes две структурные дыры, как только ты гоняешь одно приложение больше чем в одном месте. Первая — дублирование: окружения отличаются горсткой значений — число реплик, тег образа, лимиты ресурсов, hostname, — но у YAML нет параметров, поэтому ты копируешь весь набор манифестов на окружение и держишь их в синхроне руками. Дрейф неизбежен. Вторая — нет единицы релиза: kubectl apply -f отправляет текущие файлы в кластер и забывает. Он не записывает «это версия 4 my-api», не умеет связать Deployment + Service + ConfigMap + Ingress в один атомарный объект и не знает понятия отката к предыдущей версии. Хочешь отменить — реконструируй старый YAML сам.
Helm — де-факто пакетный менеджер Kubernetes — закрывает обе дыры. Он вводит единицу упаковки (chart), механизм параметризации (values) и stateful версионированную единицу деплоя (release).
Chart — это пакет: templates + значения по умолчанию + метаданные
Chart — это директория (или упакованный .tgz) с фиксированной раскладкой. Три части, важные с первого дня:
my-api/
Chart.yaml # метаданные: name, version, appVersion, dependencies
values.yaml # значения по умолчанию (публичный API чарта)
templates/
deployment.yaml # манифесты K8s, но с плейсхолдерами Go-template
service.yaml
_helpers.tpl # переиспользуемые сниппеты шаблоновtemplates/ держит обычные манифесты Kubernetes с плейсхолдерами Go-template, вбитыми в места, которые меняются. Helm рендерит эти шаблоны против дерева values, чтобы получить финальный плоский YAML, и шлёт его в кластер.
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-api
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: api
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
resources:
limits:
memory: {{ .Values.resources.limits.memory }}values.yaml поставляет значения по умолчанию — и заодно служит документированным публичным интерфейсом чарта:
# values.yaml (значения по умолчанию)
replicaCount: 1
image:
repository: registry.example.com/my-api
tag: dev
resources:
limits:
memory: 256MiValues переопределяют шаблоны под окружение
Один и тот же chart рендерит разные манифесты, потому что ты кормишь его разными values. Держишь по одному небольшому файлу на окружение и передаёшь его при install/upgrade через -f или переопределяешь отдельные ключи через --set. Приоритет слоистый: values.yaml чарта — база, каждый файл -f переопределяет её слева направо, а флаги --set бьют всё.
# values-prod.yaml — только то, что отличается от значений по умолчанию
replicaCount: 6
image:
tag: "1.8.3"
resources:
limits:
memory: 1Gi# один chart, три окружения — единственная разница в values
helm upgrade --install my-api ./my-api -f values-prod.yaml
helm upgrade --install my-api ./my-api -f values-staging.yaml --set image.tag=rc-42Это несущая идея: values — это граница окружения. Шаблон кодирует форму твоего деплоя один раз; values несут факты, специфичные для окружения. Определение readiness-пробы ровно одно на сопровождение, и dev/staging/prod больше не могут разъехаться в частях, которые должны быть идентичны.
▸Почему это работает
Держи файлы values под окружение тонкими — только ключи, которые реально отличаются от values.yaml. Если ты копируешь весь файл значений по умолчанию на окружение, ты только что заново изобрёл проблему дублирования, ради решения которой Helm и существует, — просто теперь она спрятана на уровень ниже. Дисциплина такая: values.yaml чарта — единственный источник формы и разумных значений по умолчанию; каждый файл values под окружение — короткий diff против него.
Releases: install, upgrade, rollback как одна версионированная единица
Когда ты helm install (или helm upgrade --install) chart, Helm создаёт именованный release и записывает пронумерованную ревизию ровно того, что задеплоил, храня её как Secret в кластере. Каждый helm upgrade поднимает номер ревизии. Поскольку Helm помнит каждую ревизию, helm rollback my-api 4 — это одна команда, которая заново применяет известно-хорошее прошлое состояние, — «отмена», которой у kubectl apply никогда не было. helm uninstall удаляет ресурсы всего release разом. Это превращает деплой из «кучи YAML, которую я впихнул в API-сервер» в атомарную, версионированную, обратимую транзакцию над именованным набором.
helm upgrade --install my-api ./my-api -f values-prod.yaml # -> ревизия 5
helm history my-api # все ревизии
helm rollback my-api 4 # атомарная отмена к рабочему состояниюkubectl apply vs Helm, кратко
| Возможность | Голый kubectl apply -f | Helm |
|---|---|---|
| Шаблонизация / параметры | Нет — статический YAML, копия на окружение | Плейсхолдеры Go-template, заполняемые из values |
| Много окружений | Дубли манифестов, ручной синхрон, дрейф | Один chart, тонкий файл values на окружение |
| Release / rollback | Нет единицы release, нет встроенной отмены | Именованный release, пронумерованные ревизии, helm rollback |
| Упаковка / шаринг | Разрозненные файлы в репозитории | Chart .tgz, версионированный, в chart-репозитории |
Шаринг и переиспользование: репозитории и subcharts
Зачем писать StatefulSet для Postgres с нуля, если production-grade чарты уже существуют, проверены на практике и несут разумные настройки по умолчанию? Именно здесь экосистема чартов окупается.
Чарты — распространяемые артефакты. Публичные chart-репозитории — самый известный Bitnami — публикуют production-grade чарты для готовой инфраструктуры (Postgres, Redis, nginx), так что тебе не надо писать руками stateful-set под базу. Можно и композировать: chart может объявить зависимости (subcharts) в Chart.yaml, а values родителя могут переопределить values сабчарта, так что зонтичный chart связывает твоё приложение с зависимостями как один release.
▸lesson.inset.warn
Флаг Helm --reuse-values переносит values предыдущего release во время upgrade, что звучит удобно, но молча маскирует изменения, которые ты внёс в values.yaml и в значения по умолчанию новой версии чарта. Лучше передавать свои файлы -f явно на каждый upgrade, чтобы отрендеренный вывод был чистой функцией от chart + видимых тебе values, а не от накопленного скрытого состояния прошлых ревизий.
Заметки сеньора: никогда не апгрейдь вслепую
Две практики отделяют безопасный Helm-воркфлоу от опасного. Предпросмотр до применения. helm upgrade --dry-run рендерит манифесты, не трогая кластер; широко используемый плагин helm diff upgrade показывает точное изменение против живого release, так что ты видишь «это удалит PVC» до того, как это случится, а не после. Версионируй всё. Chart несёт две версии в Chart.yaml: version (собственная версия чарта, поднимай при любом изменении чарта) и appVersion (приложение, которое он поставляет). Относись к чартам как к коду — пинни версию чарта, которую используют твои окружения, и поднимай её осознанно. И помни про ловушку upgrade-vs-recreate: правка неизменяемого поля (spec у Job, clusterIP у Service, некоторые поля volume) не патчится на месте; upgrade падает или, в зависимости от ресурса, форсит пересоздание, которое может означать даунтайм. Читай diff, знай, какие поля неизменяемы, и планируй раскатку — helm rollback это твоя страховка, а не стратегия.
Ты гоняешь один сервис в dev, staging и prod, отличающихся только числом реплик, тегом образа и лимитом памяти. Нужны безболезненная конфигурация под окружения и реальный путь отката. Выбери подход.
Прод-helm upgrade выкатил битый конфиг, и сервис начал отдавать ошибки. Какое самое быстрое корректное восстановление и почему оно возможно?
Где в чарте должна жить разница между dev и prod под окружение (реплики, тег образа)?
- 01Объясни, как один chart Helm выдаёт разные манифесты для dev, staging и prod, назвав три части чарта и как работает приоритет values.
- 02Почему Helm может откатить битый деплой, а kubectl apply нет, и что прогнать перед upgrade, чтобы откат не понадобился?
У голого YAML Kubernetes две дыры, как только приложение живёт больше чем в одном месте: он дублирует всё идентичное между окружениями, а у kubectl apply нет единицы release или отката. Теперь, когда унаследуешь кластер, где кто-то годами делал kubectl apply -f по трём окружениям со скопированными манифестами, ты знаешь путь: один chart, один тонкий файл values на окружение и журнал release, делающий helm rollback твоей спасательной командой в 2 ночи. Helm — пакетный менеджер Kubernetes — закрывает обе. Chart — это пакет: метаданные Chart.yaml, templates/ с манифестами с плейсхолдерами Go-template и values.yaml со значениями по умолчанию и публичным интерфейсом чарта. Один и тот же chart рендерит разные манифесты, потому что values переопределяют шаблоны под окружение — тонкий файл values на окружение, переданный через -f, или отдельные ключи через —set, наслаиваемые база-переопределение-флаг. Values — это граница окружения: форма живёт в шаблоне один раз, факты окружения — в маленьких файлах values, поэтому dev/staging/prod не могут разъехаться. Каждый деплой — именованный пронумерованный release, поэтому helm upgrade поднимает ревизию, которую Helm хранит, а helm rollback заново применяет известно-хорошую атомарно — отмену, которой не было у apply. Переиспользуй готовую инфраструктуру из публичных chart-репозиториев вроде Bitnami и композируй через зависимости-subcharts. И никогда не апгрейдь вслепую: helm —dry-run и helm diff предпросматривают изменение, версионируй чарты через version и appVersion в Chart.yaml и следи за ловушкой upgrade-vs-recreate, где неизменяемые поля форсят разрушительное пересоздание.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.