Установка Helm и первый готовый чарт

От пустого терминала до работающего nginx в кластере за несколько команд.

Helm CLI — единственный бинарник, который вы ставите локально. Начиная с Helm 3, серверного компонента (Tiller) больше нет: Helm работает напрямую с API Kubernetes, используя ваш kubeconfig.

Важная историческая деталь: в Helm 2 в кластере жил привилегированный сервис Tiller, и это была головная боль с точки зрения безопасности. В Helm 3 его убрали. Теперь Helm — это просто клиент, который читает тот же ~/.kube/config, что и kubectl, и обращается к кластеру от вашего имени. Поэтому правило: если kubectl видит кластер — Helm тоже увидит.

Стоит на секунду задержаться на том, почему Tiller был проблемой — это объясняет всю архитектуру Helm 3. Tiller жил в кластере как сервис с широкими правами и сам применял изменения от имени этих прав. Получалось, что любой, кто мог достучаться до Tiller, фактически получал его полномочия в обход вашей собственной модели доступа. В Helm 3 это исчезло вместе с Tiller: теперь Helm действует строго от вашего имени и ограничен ровно теми правами, которые даёт ваш kubeconfig. Если у вашей учётной записи нет права создавать Deployment в namespace, то и helm install туда не сможет — права берутся из RBAC кластера, а не из самого Helm. Это делает поведение Helm предсказуемым и безопасным: он не более могуществен, чем вы сами.

Установка

Способ зависит от ОС. На macOS проще через Homebrew, на Linux — официальным скриптом, в CI — скачиванием бинарника фиксированной версии.

# macOS
brew install helm

# Linux (официальный скрипт)
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# CI: фиксируем версию явно (воспроизводимость!)
HELM_VERSION=v3.14.0
curl -fsSL https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz   | tar -xz --strip-components=1 -C /usr/local/bin linux-amd64/helm

В CI всегда фиксируйте версию. «Установить последний Helm» — путь к нестабильным сборкам, когда вышел новый Helm и поведение шаблонов поменялось.

Звучит как перестраховка, но это реальный класс инцидентов. Между минорными версиями Helm иногда меняются дефолты функций шаблонизатора, поведение при слиянии values или строгость валидации. Пайплайн, который годами зелёный, в одно утро падает на стадии деплоя — и не потому, что кто-то правил код, а потому что на раннере подтянулся свежий Helm с чуть иным поведением. Отладка таких «само сломалось» съедает часы, потому что в голову не приходит подозревать инструмент, который вы не трогали. Закреплённая версия (HELM_VERSION=v3.14.0) превращает обновление Helm в осознанный коммит, который можно отдельно протестировать и при необходимости откатить.

Проверка установки

helm version

Вывод:

version.BuildInfo{Version:"v3.14.0", GitCommit:"...", GitTreeState:"clean", GoVersion:"go1.21.5"}

Если видите Version:"v3.x" — всё в порядке. Major-версия 3 принципиальна: материалы и команды Helm 2 несовместимы.

Добавляем репозиторий

Чистый Helm 3 идёт без предустановленных репозиториев. Добавим публичный Bitnami и обновим индекс.

helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm repo list

Вывод:

NAME   	URL
bitnami	https://charts.bitnami.com/bitnami

Первый релиз

Поставим nginx одной командой. Флаг --namespace с --create-namespace создаст namespace, если его нет.

helm install my-web bitnami/nginx   --namespace demo --create-namespace

Обратите внимание на форму команды: helm install <имя-релиза> <репозиторий/чарт>. Имя my-web придумываете вы — под ним релиз будет жить в namespace, и именно его вы потом укажете в upgrade, rollback и uninstall. Часть bitnami/nginx — это координата чарта в добавленном ранее репозитории. Эти две вещи постоянно путают новички из Helm 2, где имя задавалось флагом --name; в Helm 3 имя релиза — обязательный позиционный аргумент, и без него команда просто не выполнится.

Helm отрендерит шаблоны чарта в манифесты и применит их. Проверим, что релиз создан и поды поднимаются:

helm list -n demo
kubectl get pods -n demo

Вывод:

NAME  	NAMESPACE	REVISION	STATUS  	CHART       	APP VERSION
my-web	demo     	1       	deployed	nginx-15.4.2	1.25.3

NAME                      READY   STATUS    RESTARTS   AGE
my-web-nginx-7c9d...      1/1     Running   0          25s

Обратите внимание на REVISION 1 — это первая ревизия релиза. Дальше каждый upgrade будет её увеличивать.

Колонки этого вывода стоит прочитать вдумчиво — это карта релиза. STATUS: deployed означает, что Helm успешно применил манифесты; другие частые статусы — failed (применение сорвалось) и pending-upgrade (апгрейд завис, обычно потому что поды не дошли до готовности). Колонки CHART и APP VERSION показывают те самые две версии из прошлого урока: nginx-15.4.2 — это версия чарта-рецепта, а 1.25.3 — версия самого nginx внутри. Привычка читать эти поля экономит время при разборе инцидентов: по одному helm list видно, какой рецепт какой версии приложения сейчас раскатан в каждом namespace.

Важно различать «релиз создан» и «приложение работает». Команда helm install по умолчанию завершается, как только Helm отправил манифесты в кластер, — она не ждёт, пока поды реально станут готовы. Поэтому сразу после установки под может быть ещё в статусе ContainerCreating или Pending (тянется образ, нет места на узле, не примонтировался том). Если важно дождаться готовности именно в момент команды — для этого есть флаг --wait, который заставит Helm подождать, пока ресурсы релиза не придут в готовое состояние, и только тогда вернуть управление.

Как Helm нашёл кластер «под капотом»

Helm не хранит адрес кластера. При каждой команде он читает переменную KUBECONFIG (или ~/.kube/config), берёт текущий контекст и обращается к API-серверу так же, как kubectl. Поэтому переключение контекста kubectl config use-context меняет и «куда смотрит» Helm. Это объясняет частую путаницу: «Helm поставил не в тот кластер» почти всегда означает «был активен не тот контекст».

Та же логика распространяется и на namespace. Если вы не передали --namespace, Helm возьмёт namespace по умолчанию из текущего контекста (а не безусловный default, как часто думают). Поэтому полезная привычка — всегда явно указывать -n <namespace> в командах install, list, upgrade и uninstall. Многих «куда пропал мой релиз» можно избежать, просто не полагаясь на неявный namespace: вы делаете helm list без флага, видите пусто и пугаетесь, хотя релиз жив-здоров в соседнем namespace, который был активен в момент установки.

Перед опасными операциями в незнакомом окружении выработайте рефлекс — сначала спросить кластер, кто он. Две короткие команды экономят нервы.

kubectl config current-context   # в какой кластер я сейчас смотрю
kubectl config view --minify -o jsonpath='{..namespace}'   # и в какой namespace по умолчанию

Уборка за собой

helm uninstall my-web -n demo

Частые ошибки

  • Команды из Helm 2. Старые гайды используют helm init, --name, Tiller. В Helm 3 этого нет: helm install ИМЯ ЧАРТ — имя позиционным аргументом.
  • Нет репозиториев. «helm search ничего не находит» — потому что вы не добавили ни одного repo или не сделали helm repo update.
  • Не тот контекст. Проверяйте kubectl config current-context перед install в незнакомом окружении.

Итог

  • Helm 3 — это один CLI без серверного Tiller; он использует ваш kubeconfig.
  • В CI фиксируйте версию Helm для воспроизводимости.
  • Минимальный путь: repo addrepo updateinstalllist.
Проверьте себя
1. Чем Helm 3 отличается от Helm 2 по архитектуре?
AВ Helm 3 добавили Tiller
BВ Helm 3 убрали серверный Tiller — это чистый клиент к API кластера
CHelm 3 работает без kubeconfig
DHelm 3 требует отдельный сервер
2. Почему «helm search» может ничего не находить на свежей установке?
AHelm сломан
BНе добавлен ни один репозиторий / не сделан repo update
CНужен root
DSQL-индекс пуст
3. Как Helm определяет, в какой кластер ставить релиз?
AПо флагу --cluster
BПо текущему контексту kubeconfig, как kubectl
CСлучайно
DПо имени чарта