Школа

← К программе

Helm: чарты, values, релизы

Урок курса «Курс подготовки DevOps-инженеров»

Хронометраж: занятие 60 минут — теория 30 минут, практика 30 минут. Домашняя работа: 1–2 часа.

Кратко о чём урок: Устройство чарта и шаблонов, values и их переопределение, install/upgrade/rollback и хранение состояния релиза, зависимости, hooks, отладка через template и diff-плагин.


1. Что изучаем

После урока вы сможете:

  • объяснять, из чего состоит чарт и почему шаблон — не главное в нём;
  • предсказывать, какое значение победит, когда values заданы в трёх местах;
  • объяснять, где Helm хранит состояние релиза, и читать его штатными командами;
  • различать ревизию релиза и ревизию объекта и откатываться осознанно;
  • проверять изменение до применения и понимать, чего эта проверка не увидит;
  • объяснять, почему правка кластера руками ломает следующий helm upgrade.

2. Зачем это нужно и где применяется

Блок 12 закончился на том, что у приложения в Kubernetes набирается десяток манифестов: Deployment, Service, ConfigMap, Secret, Ingress или Gateway, пробы, ресурсы, тома. Пока приложение одно и окружение одно, это просто каталог файлов. Проблемы начинаются, когда появляется второе окружение и второе приложение.

Три задачи, которые и создали инструменты вроде Helm:

  • **одно описание на несколько окружений.**Разница между dev и prod — это несколько значений (число реплик, домен, размер тома), а не другой набор манифестов. Копировать каталог и править руками — гарантированный дрейф между средами (10.01);
  • установка чужого приложения одной командой. Prometheus, Argo CD, Istio, база данных — все они поставляются чартами, и это единственный способ получить их, не собирая двадцать манифестов самому;
  • обратимость. У выката в Kubernetes есть откат (13.05), но он про один Deployment. Релиз — это набор объектов, и откатывать надо набор.

Отсюда рабочие ситуации, разобранные в уроке:

  • «на dev работает, на prod другие значения» — и надо понять, какое победило;
  • «после helm upgrade изменилось не то, что ожидали» — самая частая заявка по Helm, и причина обычно в одной строке команды;
  • «helm upgrade падает с конфликтом, хотя чарт не менялся» — следствие того, что кто-то правил кластер руками;
  • «релиз в состоянии failed, что теперь с приложением» — и надо знать, применилось ли хоть что-нибудь;
  • «проверили перед выкатом, а установка всё равно упала» — потому что проверяли не тем.

3. Теория (30 минут)

Файлы для работы: blocks/14-kubernetes-operations/examples/14.01-helm/

Стенд: kind, один узел, Kubernetes v1.37.0, Helm v4.3.0. Версия здесь важна как никогда: Helm 4 применяет объекты через server-side apply, и половина поведения в уроке следует именно из этого. Советы из статей про Helm 3 местами уже неверны. Все опыты идут с HELM_*_HOME во временном каталоге — настройки Helm на машине не затрагиваются (тот же приём, что DOCKER_CONFIG в 08.09).

3.1. Чарт: что в нём есть и что из этого важно

helm create demo разворачивает заготовку — 4 каталога, 12 файлов, 481 строка в файлах *.yaml и *.tpl, из которых 161 строка приходится на values.yaml. Соотношение показательно: треть заготовки — не шаблоны, а описание того, что в них можно менять.

Учебный чарт мы напишем сами и маленьким (chart/), чтобы видеть каждую строку:

Файл Что это
Chart.yaml паспорт: имя, version (версия чарта), appVersion (версия приложения)
values.yaml значения по умолчанию — интерфейс чарта для того, кто его ставит
templates/*.yaml манифесты с подстановками: deployment.yaml, service.yaml
templates/_helpers.tpl именованные фрагменты, файл не становится объектом (точка в начале имени)
charts/ зависимости, скачанные локально

Две версии в Chart.yaml путают чаще всего. version меняется при правке шаблонов, appVersion — когда обновилось само приложение. Они не связаны: можно трижды поправить шаблон при том же образе.

Шаблон превращается в манифест без кластера — это отдельная команда:

helm template web chart
  → kind: Service,  name: web-course-app
  → kind: Deployment, name: web-course-app, replicas: 2
объектов: 2

Имя web-course-app собрано из имени релиза и имени чарта — так один чарт ставится в один кластер многократно. И обратите внимание: шаблонизация целиком клиентская, кластер о чартах ничего не знает. Это первое, что надо понять про Helm: он не расширение Kubernetes, а генератор манифестов плюс учёт того, что сгенерировал.

3.2. Values: четыре источника и один победитель

Значения приходят из нескольких мест, и приоритет проверяется одной таблицей (измерено на одном чарте, меняется только команда):

Как задано replicas APP_VERSION
только values.yaml чарта 2 v1
-f prod.yaml 3 from-file
--set appVersion=from-set 2 from-set
-f prod.yaml и --set 3 from-set

Порядок такой: значения чарта — слабее файла -f, файл — слабее --set. При нескольких -f побеждает последний. Логика та же, что у приоритетов переменных Ansible (10.02), и ошибаются в ней так же часто.

А вот ловушка, которой нет в документации на первой странице и которая стоит простоя. Значения не «прилипают» к релизу. Ставим с тремя репликами, потом делаем upgrade, меняя только версию:

install --set replicaCount=3 --set appVersion=v1   → replicas 3
upgrade        --set appVersion=v2                 → replicas 2 (!), APP_VERSION v2

replicaCount вернулся к значению чарта, потому что в новой команде его не было. То есть helm upgrade — это не «изменить одно поле», а «применить чарт с теми значениями, что переданы сейчас». Отсюда два способа не наступить:

helm upgrade web chart --reuse-values --set appVersion=v3   → replicas 3, APP_VERSION v3
helm upgrade web chart -f prod.yaml                         → значения из файла в git

Второй правильнее: --reuse-values тащит состояние, которое нигде не записано, а файл значений лежит в репозитории и виден в истории изменений. Это ровно тот вывод, который мы получали про откат Deployment в 13.05: временная мера против записанного состояния.

3.3. Релиз: где живёт состояние

После helm install в кластере два объекта приложения и ещё один, служебный:

kubectl get secret -l owner=helm
  sh.helm.release.v1.web.v1   helm.sh/release.v1
  метки: {"name":"web","owner":"helm","status":"deployed","version":"1"}

Состояние релиза — это Secret особого типа в том же пространстве имён. Никакой серверной части у Helm нет (в Helm 2 была, и её убрали): весь учёт — в объектах кластера. Из этого следуют практические вещи. Кто имеет доступ к секретам namespace, тот видит все values релиза, включая пароли, — значит для секретов остаются те же соображения, что в 13.08. И чтобы посмотреть состояние, не нужны чужие инструменты:

Команда Отвечает на вопрос
helm list -o json какие релизы есть, какая ревизия, какой статус
helm get values web что переопределили при установке
helm get values web --all какие значения получились в итоге (18 строк против 8)
helm get manifest web какие манифесты применены (2 объекта)
helm get hooks web какие хуки есть у релиза

Про helm list отдельно: читать его колонки в скриптах нельзя. Я на этом наступил в этом же уроке — awk '{print $8}' выдал метку времени вместо статуса, потому что в поле даты пробелы. В скриптах только -o json.

3.4. Ревизии: две разные нумерации

Каждое изменение релиза создаёт новую ревизию и новый Secret:

helm history web
REVISION  STATUS      DESCRIPTION
1         superseded  Install complete
2         superseded  Upgrade complete
3         deployed    Upgrade complete

ревизия Deployment: 3        секретов состояния: 3

Числа совпали случайно: каждое изменение чарта здесь меняло шаблон пода. Если поправить то, что пода не касается (метку сервиса, размер ресурса другого объекта), ревизия релиза вырастет, а ревизия Deployment — нет. Ревизия релиза — про набор объектов, ревизия Deployment — про шаблон пода (13.05).

Откат устроен так же, как в Deployment, и это стоит запомнить:

helm rollback web 1
4  deployed  Rollback to 1
APP_VERSION в кластере: v1,  replicas: 2

Откат создаёт новую ревизию, а не возвращает старую. История только растёт; --history-max по умолчанию 10, дальше старые ревизии вытесняются. И тот же вывод, что в 13.05: откат — временная мера, дальше правится то, из чего собирается релиз.

3.5. Server-side apply: главное отличие Helm 4

Здесь начинается то, из-за чего к этому уроку нельзя применять советы про Helm 3. Посмотрим, кто владеет полями установленного объекта (механика из 13.03):

kubectl get deploy web-course-app --show-managed-fields
  менеджер: helm                     операция: Apply
  менеджер: kube-controller-manager  операция: Update

Операция Apply означает server-side apply: Helm 4 по умолчанию (--server-side auto) применяет объекты так же, как kubectl apply --server-side. Теперь трогаем кластер руками и пробуем обычный upgrade:

kubectl scale deploy web-course-app --replicas=5
helm upgrade web chart        → код 1
  conflict with "kubectl" with subresource "scale" using apps/v1: .spec.replicas
replicas после неудачи: 5,  статус релиза: failed

helm upgrade не «перетирает» ручную правку, а падает с конфликтом владения полем. Это то же поведение, что у двух менеджеров в 13.03, только здесь один из них — Helm. Выход — сказать явно, что вы забираете поле себе:

helm upgrade web chart --force-conflicts   → код 0, replicas 2, статус deployed
менеджеры после этого: helm (Apply), kube-controller-manager (Update)

Знакомый по статьям --force здесь не поможет и даже не запустится: invalid operation: cannot use server-side apply and force replace together. Он относится к другому механизму (--force-replace, замена объекта целиком) и с server-side apply несовместим.

Контрольный опыт, без которого вывод был бы неполным: тот же дрейф при клиентском применении.

helm install web chart --server-side=false   → менеджер: helm, операция Update
kubectl scale --replicas=5
helm upgrade web chart --server-side=false   → код 0, replicas 2

То есть при --server-side=false расхождение правится молча и без ошибки. Разница поведения — не в Helm как таковом, а в способе применения; по умолчанию в версии 4 он серверный. Мелочь, которую видно здесь же: аннотации last-applied-configuration Helm не ставит ни в одном режиме — прошлый манифест он хранит в своём Secret, а не в объекте.

3.6. Проверка до применения: что ловит каждый инструмент

Четыре способа «посмотреть, прежде чем ставить», и они не равноценны. Внесём в шаблон опечатку в имени поля (imagePullPolicyy) — YAML остаётся валидным, схема нарушена:

Команда Код Поймала?
helm lint chart 0 нет
helm template web chart 0 нет
helm install --dry-run 0 нет
helm install --dry-run=server 0 нет
helm template … | kubectl apply --dry-run=server -f - 1 да: unknown field
helm install по-настоящему 1 да: failed to create typed patch, объект не создан

Строка про --dry-run=server — главная в таблице и самая неожиданная. Серверный пробный прогон Helm опечатку не увидел, хотя kubectl apply --dry-run=server на том же отрисованном манифесте её ловит (в 13.03 мы это измеряли). Значит проверка, которой все доверяют, даёт ложную уверенность, а надёжный порядок — отрисовать и прогнать через kubectl:

helm template web chart | kubectl apply --dry-run=server -f -

Плагин helm diff отвечает на другой вопрос — «что изменится». Он ставится отдельно, и на стенде это потребовало --verify=false (plugin source does not support verification), что само по себе повод задуматься о доверии к плагинам:

helm diff upgrade web chart --set appVersion=v3
  -   value: "v2"
  +   value: "v3"

Но у него есть граница, которую надо знать. Правим кластер руками и снова смотрим diff:

kubectl scale deploy web-course-app --replicas=7
helm diff upgrade web chart --set replicaCount=3   → строк вывода 0

Ноль. helm diff по умолчанию сравнивает новый чарт с сохранённым релизом, а не с фактическим состоянием кластера, — то есть дрейф он не показывает. Сравнение с кластером включается --dry-run=server, и тогда разница появляется. Это ровно та же ловушка, что у kubectl diff в 13.03: инструмент отвечает на вопрос «что сделает команда», а не «чем кластер отличается от репозитория».

3.7. Хуки: как выполнить миграцию перед выкатом

Хук — обычный объект с аннотацией. Job с helm.sh/hook: pre-upgrade,pre-install выполняется до применения остальных объектов (chart/templates/migration-hook.yaml):

helm upgrade web chart --set migration.enabled=true   → код 0
job: web-course-app-migrate  Complete 1/1  7s
логи: миграция схемы

Полезнее посмотреть, что будет при неудаче. Заставим хук завершиться с кодом 1:

helm upgrade … --set migration.exitCode=1 --set appVersion=v9   → код 1
  pre-upgrade hooks failed: … Job Failed. failed: 1/1
статус релиза: failed
APP_VERSION в кластере: v1   (просили v9)

Объекты не применились вовсе — упавшая миграция остановила выкат, приложение осталось на прежней версии. Это и есть причина, по которой миграции вешают на pre-upgrade (сравните с 07.07, где мы разбирали сами миграции).

Флаг --atomic добавляет к этому автоматический откат:

helm upgrade web chart --atomic …   → код 1
5  failed    Upgrade "web" failed: pre-upgrade hooks failed…
6  deployed  Rollback to 4
статус релиза: deployed

Код возврата 1 — работа не сделана, но релиз снова deployed, а не failed. В пайплайне это важно: без --atomic неудачный выкат оставляет релиз в состоянии failed, и следующий запуск начинается из него.

И одна вещь, которая удивляет при уборке: helm uninstall снял приложение и своё состояние, но Job от хука остался:

объектов приложения: 0,  секретов состояния: 0,  Job от хука: 1

Так работает hook-delete-policy: before-hook-creation — объект удаляется перед следующим запуском хука, а не после успеха. Хотите чистоты — hook-succeeded.

3.8. Зависимости

Чарт может тянуть другие чарты. Объявление в Chart.yaml, и helm dependency update скачивает их в charts/ и пишет Chart.lock:

helm dependency update umbrella   → код 0, Saving 1 charts
в charts/: course-app-0.1.0.tgz,  Chart.lock: 6 строк

объектов при subchart.enabled=false:  0
объектов при subchart.enabled=true:   2

Чарт-обёртка своих шаблонов не имеет вовсе, поэтому при выключенном условии из него не рождается ни одного объекта, а при включённом появляются объекты подчарта.

Два наблюдения. condition позволяет выключать зависимость значением — так делают «поставь базу вместе с приложением или подключись к внешней». И Chart.lock — это тот же самый механизм, что package-lock.json из 07.04: без него version: "^1.0" однажды приведёт другую версию. Зависимость из локального каталога (file://) годится, чтобы проверить механизм без сети.

3.9. Сводка: что показали замеры

Что проверяли Результат
helm create 4 каталога, 12 файлов, 442 строки, из них 161 — values.yaml
приоритет values чарт < -f < --set; при двух -f — последний
values между upgrade не сохраняются: replicaCount вернулся с 3 на 2
состояние релиза Secret типа helm.sh/release.v1, по одному на ревизию
helm get values против --all 8 строк против 18 (зависит от числа переопределений)
откат создаёт новую ревизию (4 Rollback to 1), --history-max = 10
чем применяет Helm 4 server-side apply: менеджер helm, операция Apply
дрейф + helm upgrade код 1, конфликт владения полем, релиз failed
выход из конфликта --force-conflicts → код 0; --force не работает вовсе
то же при --server-side=false операция Update, дрейф правится молча, код 0
опечатка в имени поля lint, template, --dry-run, --dry-run=server — не поймали
та же опечатка kubectl apply --dry-run=server — поймал, настоящий install упал
helm diff при дрейфе 0 строк — сравнивает с релизом, не с кластером
провал хука pre-upgrade объекты не применились, релиз failed, версия прежняя
--atomic код 1, но автоматический откат и статус deployed
uninstall приложение и состояние удалены, Job хука остался
зависимости Chart.lock из 6 строк; condition включает подчарт (0 против 2 объектов)

3.10. Примеры и варианты реализации

Локальный стенд. Всё из урока воспроизводится полностью: Helm — клиентский инструмент, кластеру для него ничего не нужно. Единственное, чего стоит избегать на стенде, — привыкнуть к --set: локально это удобно, а в рабочем процессе даёт ту самую ловушку с «непослушными» значениями.

VPS / bare-metal. Добавляется вопрос хранения чартов и значений. Чарт лежит в git рядом с кодом либо в реестре (OCI-репозиторий — тот же реестр образов из 08.09), файлы значений по окружениям — в git, секреты не в них (13.08, 07.03). И отдельно: доступ к namespace означает доступ к values всех релизов в нём, потому что состояние — обычный Secret.

Yandex Cloud (Managed Service for Kubernetes). Helm работает так же — он не зависит от того, кто управляет кластером. Отличие в том, что многие дополнения (контроллеры, агенты) провайдер предлагает ставить своими средствами, и тогда важно не поставить то же самое чартом второй раз. Запуском не проверялось.

Что выбрать. Helm оправдан там, где есть параметризация и чужие приложения. Если манифесты не параметризуются, а просто различаются в двух-трёх местах, шаблонизатор может оказаться лишним — об этом весь следующий урок про Kustomize. Правило, которое переносится в любую среду: значения окружения живут в файле в репозитории, а не в команде; всё, что передано --set по ходу, существует только в памяти того, кто это набрал.

4. Практика на занятии (30 минут)

Опыты — в lab-helm.sh, чарт — в chart/, файл значений окружения — values-prod.yaml.

Курс проходят самостоятельно, поэтому каждый шаг говорит, что ввести, зачем, что должно получиться и что делать при неудаче.

Стенд — kind, один узел (kindest/node:v1.37.0), образ приложения course-k8s-app:1. Первый вызов любого режима сам поднимает кластер и грузит в него образ (kind load docker-image) — это самая долгая часть первого шага. Запускайте режимы с KEEP=1, чтобы кластер оставался между шагами, а не пересоздавался на каждую команду. KUBECONFIG и HELM_*_HOME лаборатория уводит во временный каталог — ваши настройки Helm и список репозиториев не затрагиваются. Уборка — последним шагом, режимом clean.

Шаг 1 (5 минут). Чарт и шаблон.

  • Что ввести:
cd blocks/14-kubernetes-operations/examples/14.01-helm
KEEP=1 bash lab-helm.sh chart
  • Зачем: увидеть, из чего состоит чарт, и убедиться, что шаблон превращается в манифест без обращения к кластеру.
  • Ожидаемый результат: команда поднимет kind-кластер (первый запуск — дольше остальных, дальше он остаётся благодаря KEEP=1) и напечатает состав заготовки helm create рядом с нашим маленьким чартом — Chart.yaml, values.yaml, templates/*.yaml, _helpers.tpl — и две разные версии в Chart.yaml: version чарта и appVersion приложения. В конце — вывод helm template: два объекта (Service, Deployment) с именем web-course-app и replicas: 2.
  • Если не так: сообщение сборка образа не удалась или кластер не создан значит, что не запущен Docker (проверьте docker ps); первый запуск ещё и качает образ узла kindest/node:v1.37.0, это может занять несколько минут.
  • Ответьте: почему helm template работает без кластера и что из этого следует?

Шаг 2 (8 минут). Values и главная ловушка.

  • Что ввести:
KEEP=1 bash lab-helm.sh values
  • Зачем: увидеть приоритет источников values и главную ловушку урока — значения не сохраняются между вызовами helm upgrade.
  • Ожидаемый результат: сначала таблица четырёх способов задать одно и то же значение (чарт слабее -f, -f слабее --set); затем install --set replicaCount=3 --set appVersion=v1 даёт replicas=3, а следующий upgrade только с --set appVersion=v2 возвращает replicas=2 — число реплик откатилось к значению чарта, хотя его никто не трогал. Прежде чем читать эту строку вывода, предскажите сами, чему будет равно replicas. Дальше — --reuse-values и -f values-prod.yaml возвращают три реплики двумя разными способами.
  • Если не так: если таблица или ловушка не совпадают с описанием из теории (3.2) — с прошлого прогона остался чужой релиз; выполните bash lab-helm.sh clean и повторите режим заново.
  • Ответьте: почему helm upgrade — это не «изменить одно поле», а «применить чарт с теми значениями, что переданы сейчас»?

Шаг 3 (7 минут). Состояние релиза и ревизии.

  • Что ввести:
KEEP=1 bash lab-helm.sh release
KEEP=1 bash lab-helm.sh revisions
  • Зачем: найти, где Helm хранит состояние релиза, и убедиться, что откат создаёт новую ревизию, а не возвращает старую.
  • Ожидаемый результат: режим release покажет Secret sh.helm.release.v1.web.v1 типа helm.sh/release.v1 с меткой status=deployed, разницу между helm get values (только переопределённое) и --all (итоговые значения), и предупреждение про колонки helm list — их не разбирать в скриптах, только -o json. Режим revisions покажет три ревизии в helm history, затем helm rollback web 1 создаст четвёртую ревизию с описанием Rollback to 1, а не вернёт первую.
  • Если не так: режим сам пересоздаёт релиз с нуля (wipe) перед измерением, поэтому чужое состояние из прошлых шагов ему не мешает; повторный запуск безопасен.
  • Ответьте: чем ревизия релиза отличается от ревизии Deployment и может ли одна вырасти без другой?

Шаг 4 (7 минут). Дрейф и server-side apply.

  • Что ввести:
KEEP=1 bash lab-helm.sh drift
  • Зачем: убедиться, что Helm 4 применяет объекты через server-side apply, и увидеть, что из этого следует при ручной правке кластера. Ключевой режим урока.
  • Ожидаемый результат: сначала kubectl get deploy --show-managed-fields покажет менеджера helm с операцией Apply. Затем kubectl scale --replicas=5 вручную, и обычный helm upgrade упадёт с кодом 1 и сообщением conflict with "kubectl" with subresource "scale" using apps/v1: .spec.replicas, а релиз перейдёт в статус failed. --force не сработает (cannot use server-side apply and force replace together), а --force-conflicts пройдёт с кодом 0. В конце — контрольный опыт с --server-side=false: тот же дрейф правится молча, кодом 0, без единого сообщения о конфликте.
  • Если не так: если helm upgrade не упал (код 0) — опыт не показал того, ради чего делался, лаба сама печатает это предупреждение; проверьте версию (helm version) — должна быть 4.x.
  • Ответьте: что произошло, какой механизм Kubernetes за этим стоит и какие два выхода из конфликта у вас есть?

Шаг 5 (3 минуты). Проверка и уборка.

  • Что ввести:
KEEP=1 bash lab-helm.sh verify
bash lab-helm.sh clean
  • Зачем: увидеть, что четыре способа «проверить перед установкой» не равноценны, и убрать за собой стенд.
  • Ожидаемый результат: таблица из пяти строк. helm lint, helm template, helm install --dry-run и даже helm install --dry-run=server вернут код 0 на опечатке в имени поля (imagePullPolicyy) — ни один её не поймает. Только helm template | kubectl apply --dry-run=server -f - вернёт код 1 с текстом unknown field. После clean — ноль кластеров с именем lab1301 и ноль контейнеров с этим префиксом.
  • Если не так: если код 1 не появился ни в одной строке — опечатка не внеслась в шаблон; лаба сама прерывается с сообщением опечатка не внеслась — опыт недействителен, проверьте права на запись в chart/templates/.
  • Ответьте: найдите строку, где код 0 означает не «всё хорошо», а «эта проверка такого не видит».

Режимы hooks и deps на занятие не выносятся — они в домашней работе.

Что обсудить в конце занятия. Инженер выкатил релиз командой helm upgrade app ./chart --set image.tag=1.4.2, всё поднялось. Через неделю другой инженер выкатил helm upgrade app ./chart --set replicaCount=6. Что стало с версией образа и почему? Как надо было?

5. Домашняя работа (1–2 часа)

Задание. Собрать чарт для своего приложения и доказать обратимость выката.

Часть 1. Свой чарт. Возьмите любое приложение (подойдёт учебное) и напишите чарт с нуля — без helm create, чтобы в нём не было лишнего. Обязательно: Deployment, Service, _helpers.tpl с именованным шаблоном имени, values.yaml минимум с пятью параметрами. Проверьте helm lint и отрисуйте helm template.

Часть 2. Два окружения одним чартом. Сделайте values-dev.yaml и values-prod.yaml, отличающиеся не меньше чем тремя значениями. Покажите выводом helm template, что из одного чарта получаются два разных набора манифестов. Ответьте: что из ваших значений нельзя держать в этих файлах и почему (13.08).

Часть 3. Хуки и провал миграции. Добавьте в чарт Job на pre-upgrade (пример) и прогоните режим:

KEEP=1 bash lab-helm.sh hooks

Зафиксируйте: что происходит с приложением, когда миграция падает; чем отличается поведение с --atomic; что остаётся в кластере после uninstall и почему. Затем измените hook-delete-policy так, чтобы остатков не было, и покажите, что помогло.

Часть 4. Дрейф своими руками. Поставьте свой чарт, измените что-нибудь в кластере через kubectl, попробуйте helm upgrade. Опишите: какой код возврата, что в сообщении, какое поле конфликтует и два способа выйти из положения. Затем повторите то же с --server-side=false и объясните разницу.

Часть 5. Проверка перед выкатом. Внесите в свой чарт ошибку, которую не видит helm lint, и постройте проверку, которая её ловит. Приведите обе команды и коды возврата. Ответьте: почему helm install --dry-run=server на эту ошибку не срабатывает, и что вы поставите в пайплайн (09.07).

Часть 6. Зависимости (по желанию). Прогоните режим deps и объясните, зачем нужен Chart.lock и что делает condition.

Результаты — в карточку homework/helm-workbook.md.

Критерии приёмки.

  • свой чарт написан без helm create, helm lint проходит, приведён вывод helm template;
  • два файла значений дают разные манифесты — показано выводом; сказано, чего в них держать нельзя;
  • поведение при провале миграции зафиксировано, разница с --atomic объяснена, остатки хука убраны сменой политики и это подтверждено выводом;
  • дрейф воспроизведён: приведены код возврата, текст конфликта, конфликтующее поле и два способа выхода;
  • найдена ошибка, которую helm lint не видит, и команда, которая её ловит — с кодами возврата обеих;
  • в ответах нет утверждений «должно работать»: каждое подтверждено выводом команды.

Файлы для работы: blocks/14-kubernetes-operations/examples/14.01-helm/

6. Частые ошибки и подводные камни

Выкат через --set без файла значений. Симптом: после чужого upgrade у сервиса съехало число реплик или вернулся старый образ. Причина: значения не сохраняются между командами — helm upgrade применяет чарт с тем, что передано сейчас. Что делать: значения окружения держать в файле в git и выкатывать -f; --reuse-values — только как разовая мера, потому что тащит состояние, которого нет в репозитории.

Правка кластера руками при Helm 4. Симптом: helm upgrade падает с conflict with "kubectl" … .spec.replicas, релиз уходит в failed. Причина: Helm 4 применяет объекты через server-side apply, и поле принадлежит другому менеджеру (13.03). Что делать: --force-conflicts, если поле действительно ваше; и не править то, чем управляет Helm, — иначе следующий выкат встанет в неподходящий момент.

Ждут, что --force решит конфликт. Симптом: invalid operation: cannot use server-side apply and force replace together. Причина: --force (он же --force-replace) — про замену объекта целиком, это другой механизм. Что делать: при server-side apply нужен --force-conflicts.

Доверяют helm install --dry-run=server. Симптом: проверка прошла, установка упала. Причина: измерено — опечатку в имени поля этот пробный прогон не видит, хотя kubectl apply --dry-run=server на том же манифесте видит. Что делать: в пайплайн ставить helm template … | kubectl apply --dry-run=server -f -.

Считают, что helm diff показывает дрейф. Симптом: в кластере руками изменили реплики, helm diff upgrade печатает пустоту. Причина: он сравнивает новый чарт с сохранённым релизом, а не с кластером. Что делать: --dry-run=server для сравнения с кластером; для поиска дрейфа вообще — инструменты GitOps (14.10).

Ожидают, что откат вернёт прежнюю ревизию. Симптом: история релиза растёт после каждого отката, и в ней не разобраться. Причина: helm rollback создаёт новую ревизию с описанием Rollback to N. Что делать: помнить, что это временная мера, и сразу приводить в порядок то, из чего собирается релиз (тот же вывод, что в 13.05).

Неудачный выкат оставляют в состоянии failed. Симптом: следующий upgrade начинается из непонятного состояния, часть объектов новая, часть старая. Причина: без --atomic Helm не откатывает то, что успел применить. Что делать: --atomic в пайплайне; помнить, что код возврата всё равно 1 — успех надо проверять по коду, а не по статусу релиза.

Забывают про остатки хуков. Симптом: после uninstall в namespace остались Job и поды от миграций. Причина: hook-delete-policy: before-hook-creation удаляет объект перед следующим запуском, а не после успеха. Что делать: hook-succeeded для того, что не нужно хранить, и уборка старых Job по расписанию (13.12).

Секреты в values. Симптом: пароль виден любому, кто может читать секреты namespace. Причина: состояние релиза — обычный Secret, и values лежат в нём целиком. Что делать: то же, что в 13.08 и 07.03: секреты передавать отдельно, а в чарте ссылаться на имя существующего Secret.

7. Проверка усвоения

  1. Чем version отличается от appVersion в Chart.yaml? Приведите случай, когда меняется одно и не меняется другое.
  2. Значение задано в values.yaml чарта, в файле -f и в --set. Какое победит? Что будет, если передать два файла -f?
  3. Вы поставили релиз с --set replicaCount=5, затем сделали helm upgrade с одним --set image.tag=2.0. Сколько будет реплик и почему?
  4. Где Helm хранит состояние релиза и какие два практических следствия из этого вытекают?
  5. Чем ревизия релиза отличается от ревизии Deployment? Может ли одна вырасти без другой?
  6. helm upgrade вернул conflict with "kubectl" … .spec.replicas. Что произошло, какой механизм Kubernetes за этим стоит и какие два выхода у вас есть?
  7. helm lint, helm template и helm install --dry-run=server вернули 0, а установка упала. Как такое возможно и какую проверку надо было выполнить?
  8. Миграция на pre-upgrade упала. Что стало с приложением? Что изменится, если добавить --atomic, и каким будет код возврата?

8. Материалы

  • Helm Docs — Charts — устройство чарта: Chart.yaml, структура каталогов, зависимости, condition и tags. Основной документ, с которого стоит начинать.
  • Values Files — как устроены values и в каком порядке они переопределяются; читать до первого --set в пайплайне.
  • helm upgrade — справочник — все флаги, включая --server-side (по умолчанию auto), --force-conflicts, --take-ownership, --atomic, --history-max. Именно здесь видно, что в Helm 4 применение серверное.
  • Chart Hooks — список событий, hook-weight и политики удаления; раздел про hook-delete-policy объясняет остатки, которые мы намерили.
  • Helm 4 release notes — перечень изменений относительно Helm 3; полезно, когда натыкаетесь на совет из статьи, который «почему-то не работает».
  • helm-diff — плагин для просмотра изменений до применения; в README же оговорены его границы (сравнение с сохранённым релизом, а не с кластером).

← Предыдущий: 13.14. Отладка в кластере: события, логи, типовые сбои · Блок 14 · Следующий: 14.02. Kustomize, наложения и выбор между Helm и Kustomize →

Материалы для практики

Скрипты, шпаргалки и заготовки к заданиям урока. Текст урока — на этой странице.