Helm: что это и зачем
Когда у тебя один Deployment — kubectl apply -f deployment.yaml достаточно. Когда у тебя приложение из 20 YAML-файлов (Deployment, Service, Ingress, ConfigMap, Secret, ServiceAccount, RBAC, HPA, PDB, NetworkPolicy…) — и эти манифесты должны быть разные в dev, staging, prod — становится больно. Helm — это решение от Kubernetes community для упаковки, версионирования и установки таких “приложений” одной командой. Это apt / brew / npm мира Kubernetes.
Структура compose.yml: services, build, ports, volumes
Что такое Helm
Helm — package manager для Kubernetes. Его основная единица — Chart (пакет): директория с YAML-шаблонами и default-значениями, описывающая всё, что нужно для разворачивания приложения. Когда ты делаешь helm install, Helm:
- Рендерит templates с подставленными values в plain YAML.
- Отправляет результирующие manifests в apiserver через тот же протокол, что и
kubectl apply. - Сохраняет метаданные релиза (имя, ревизия, rendered manifests, history) в виде Secret в том же namespace.
Это означает: Helm — это client-side инструмент. В кластере ничего особенного нет. Любой манифест, который применил Helm, можно увидеть через обычный kubectl get.
Helm 2 vs Helm 3: почему Tiller убрали
В Helm 2 (до 2019) была серверная часть — Tiller, Deployment с cluster-admin правами в kube-system. Helm CLI отправлял шаблоны Tiller-у, тот рендерил и применял. Проблем было много:
- Security: Tiller с cluster-admin = эскалация привилегий. Любой, кто мог достучаться до Tiller (через port-forward или внутрикластерно) — получал admin на весь кластер.
- RBAC bypass: RBAC проверял права Tiller, а не пользователя.
- State management: история релизов хранилась в ConfigMaps в kube-system, конкурирующие helm-инстансы могли портить состояние.
Helm 3 (с 2019) — client-only:
- Никакого Tiller, никаких серверных компонентов.
- Helm использует ту же kubeconfig, что и kubectl — RBAC применяется к пользователю.
- Релизы хранятся как Secrets в том же namespace, что и приложение (по умолчанию). Secret имя —
sh.helm.release.v1.<release-name>.v<revision>. - Можно использовать
--driver=configmapили--driver=sqlдля альтернативных backend-ов.
В 2026 году Helm 2 EOL уже несколько лет. Если видишь legacy-проект с Tiller — это первая вещь, которую нужно мигрировать. helm 2to3 convert — официальный плагин для миграции релизов.
Структура Chart
Chart — это директория с фиксированной структурой:
mychart/
├── Chart.yaml # metadata: name, version, appVersion, dependencies
├── values.yaml # default config values
├── values.schema.json # JSON Schema для валидации values (опционально)
├── templates/ # YAML templates (Go templating)
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── _helpers.tpl # template helpers (define + include)
│ ├── NOTES.txt # вывод после install (как использовать релиз)
│ └── tests/ # helm test — манифесты Pod-ов для smoke-tests
│ └── test-connection.yaml
├── charts/ # dependencies (subcharts), unpacked
├── crds/ # CustomResourceDefinitions, installed first
├── README.md
└── LICENSE
Ключевые файлы:
Chart.yaml— метаданные:name,version(SemVer Chart-а),appVersion(версия приложения, которое чартом упаковано),dependencies(список subcharts).values.yaml— default-значения. Пользователь может переопределить через-f my-values.yamlили--set key=value.templates/*.yaml— Go-шаблоны манифестов. На рендере становятся обычными K8s манифестами.templates/_helpers.tpl— переиспользуемые шаблоны (как функции). Имя начинается с_— Helm не пытается рендерить как K8s манифест.crds/— CRD устанавливаются до templates, и не удаляются приhelm uninstall(специально, чтобы не сломать другие релизы).
Repository и Release: терминология
Три ключевых понятия:
- Chart — пакет (директория или
.tgzархив). Сам по себе ничего не делает. - Repository — HTTP-сервер с
index.yaml, в котором перечислены доступные charts и ссылки на.tgzфайлы. Примеры: Bitnami (https://charts.bitnami.com/bitnami), Artifact Hub (агрегатор). Repository — это просто статика на HTTP. - Release — инстанс установленного Chart-а в кластере. У одного Chart-а может быть много releases (например, два инстанса Redis:
cacheиqueue).
# Repo: добавили source charts
helm repo add bitnami https://charts.bitnami.com/bitnami
# Chart: skachali, посмотрели, что внутри
helm pull bitnami/nginx --untar
ls nginx/
# Release: устанавливаем chart как named instance
helm install web bitnami/nginx --namespace web
helm install api bitnami/nginx --namespace api
# Это два разных Release-а одного и того же Chart-а.
Go templating: первый взгляд
Helm использует Go templates (пакет text/template) с расширениями из Sprig (string/math функции). В templates ты обращаешься к четырём встроенным объектам:
.Values— данные из values.yaml + переопределения от пользователя..Release—.Release.Name,.Release.Namespace,.Release.Revision,.Release.IsInstall,.Release.IsUpgrade..Chart—.Chart.Name,.Chart.Version,.Chart.AppVersion(из Chart.yaml)..Capabilities—.Capabilities.KubeVersion,.Capabilities.APIVersions.Has— для conditional на основе версии K8s..Files— доступ к файлам внутри Chart-а (для ConfigMap из файлов конфига).
Пример templates/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-web
labels:
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}-web
template:
metadata:
labels:
app: {{ .Release.Name }}-web
spec:
containers:
- name: nginx
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: 80
И values.yaml:
replicaCount: 3
image:
repository: nginx
tag: "1.25"
После helm install web ./mychart Helm рендерит шаблон, подставляет значения, и применяет результат. Имя Deployment-а становится web-web, replicas=3, image=nginx:1.25.
Helm-шаблоны до рендеринга не являются валидным YAML. Поэтому kubectl apply на сырых template-файлах не работает. И редакторы YAML могут орать на синтаксис. Используй helm template ./mychart --debug для дебага рендера до apply.
Release как Secret: что значит для управления
После helm install web ./mychart в кластере появляется Secret типа helm.sh/release.v1:
kubectl get secret -l owner=helm -n web
# NAME TYPE DATA AGE
# sh.helm.release.v1.web.v1 helm.sh/release.v1 1 30s
Внутри (data[“release”] — gzip+base64+JSON):
- Все rendered manifests, которые Helm применил.
- Final values (после всех merges).
- Метаданные: timestamp, статус (deployed/failed/pending-upgrade), пользователь (если есть).
При helm upgrade web ./mychart создаётся новый Secret sh.helm.release.v1.web.v2, старый остаётся (история ревизий — по умолчанию хранятся последние 10, контролируется --history-max). При helm rollback web 1 создаётся v3 со state из v1. При helm uninstall web --keep-history — статус релиза меняется на uninstalled, но Secrets остаются. Без --keep-history — Secrets удаляются.
Если ты удалил Release Secret вручную (kubectl delete secret sh.helm.release.v1.web.v1) — Helm “забывает” про релиз, но manifests остаются в кластере. helm list его уже не видит. helm uninstall тоже не сможет — он же не знает, что чистить. Чинить: либо kubectl delete resources руками по labels, либо helm install web ... --replace (только в Helm 3).
Что отслеживает Helm, а что — нет
Это критически важный момент, который часто не понимают:
- Helm не tracks отдельные K8s resources. Он не знает в моменте, сколько у тебя Pod-ов в Deployment. Он знает только rendered manifests из последнего apply.
helm uninstallудаляет всё, что было в последних rendered manifests через меткуapp.kubernetes.io/managed-by=Helm+ ownership tracking.- Если ты вручную создал какой-то ресурс с тем же именем — Helm не возьмёт его в управление.
helm upgradeможет сломаться с conflict. - CRDs из
crds/директории НЕ удаляются приhelm uninstall— специально, чтобы не сломать другие релизы, использующие тот же CRD. - Helm hooks (Jobs для migrations) не tracked как часть release. Они создаются, выполняются — и
helm rollbackне вернёт их назад.
Killer-моменты
- Helm 3 client-only. Нет Tiller, нет server-side компонентов. RBAC проверяется на пользователя, а не на демона с cluster-admin.
- Release stored as Secret в namespace. Удалил Secret вручную — Helm “забыл” про release, но resources остались.
- CRDs не удаляются при
helm uninstall. По дизайну, чтобы не сломать другие релизы с теми же CRDs. - Один Chart → много Release-ов.
helm install web bitnami/redisиhelm install cache bitnami/redis— два независимых инстанса с разной историей. - Templates не валидный YAML. Линтеры YAML на template-файлы ругаются.
helm templateдля рендера локально,helm lintдля проверки. appVersionvsversionв Chart.yaml.version— версия Chart-а (увеличивается при изменении templates).appVersion— версия упакованного приложения (Redis 7.2 → 7.4 без изменения chart структуры).