Отладка и тестирование чарта: lint, template, test

Прежде чем катить чарт в прод, его проверяют на трёх уровнях: синтаксис, отрендеренный результат и работоспособность в кластере.

helm lint ловит ошибки структуры, helm template показывает, что отрендерится (главный инструмент дебага), а helm test проверяет уже установленный релиз через test-хуки.

helm lint — статическая проверка

helm lint ./webapp
helm lint ./webapp -f values/prod.yaml   # с конкретными значениями

Вывод:

==> Linting ./webapp
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed

lint проверяет наличие обязательных полей Chart.yaml, валидность YAML после рендера, типичные ошибки. Это первый рубеж — гоняйте его в CI на каждый коммит.

Важно понимать, что у lint два режима строгости. По умолчанию он различает [ERROR] (чарт реально сломан и установить его нельзя) и [INFO]/[WARNING] (косметика вроде отсутствующей иконки или нерекомендуемого поля). В CI разумно запускать его с флагом --strict, тогда любое предупреждение тоже валит сборку — это дисциплинирует команду и не даёт мелким огрехам накапливаться. Отдельно стоит помнить: lint по умолчанию рендерит чарт со значениями из values.yaml, а не с вашими прод-оверрайдами. Поэтому условие required или ветка if, которая срабатывает только в проде, может остаться непокрытой — для этого и передают -f values/prod.yaml, чтобы линтер прошёл по реальному пути рендера.

Ещё один нюанс «под капотом»: lint сначала рендерит шаблоны (как template), а потом парсит результат как YAML и сверяет с набором правил. Это значит, что ошибка в Go-шаблоне (незакрытая директива, обращение к nil-полю) всплывёт уже на этапе lint — отдельно запускать template, чтобы поймать синтаксис шаблона, не обязательно. Зато lint не покажет вам, как именно выглядит итоговый манифест, — для глазной проверки нужен именно template.

helm template — рендер без кластера

Главный инструмент отладки шаблонов. Рендерит чарт локально, не обращаясь к кластеру, и печатает готовые манифесты:

# увидеть весь результат
helm template web ./webapp -f values/prod.yaml

# только один манифест
helm template web ./webapp --show-only templates/deployment.yaml

# с отладкой ошибок рендера
helm template web ./webapp -f values/prod.yaml --debug

Когда чарт падает с «error converting YAML to JSON» — именно helm template покажет проблемную строку в отрендеренном виде. Привыкайте читать вывод глазами: отступы, кавычки, пустые блоки.

Почему template — главный инструмент дебага, а не вспомогательный? Потому что 90% ошибок в чартах — это не логика, а текст: съехавший на пробел отступ внутри range, число, которое YAML принял за булево (on, yes), многострочная строка без |, отсутствующие кавычки вокруг значения, начинающегося с * или &. Все эти проблемы видны только в отрендеренном виде, потому что в исходном шаблоне на их месте стоит {{ .Values.foo }}. Helm рендерит шаблон, и лишь затем отдаёт результат YAML-парсеру — поэтому ваша ментальная модель должна быть «сначала текст, потом YAML».

Полезные флаги при отладке

Флаг --show-only незаменим, когда чарт генерирует двадцать манифестов, а сломан один: вы сужаете вывод до конкретного файла и не утопаете в простыне. Флаг --debug добавляет к выводу комментарии о том, какие значения были подставлены, и — что важнее — печатает частично отрендеренный результат даже при ошибке, тогда как без него Helm иногда отдаёт лишь сухое сообщение. А чтобы проверить, как поведут себя именованные шаблоны (_helpers.tpl), удобно временно вынести проблемный include в отдельный фиктивный манифест и посмотреть его через --show-only.

helm install --dry-run — рендер с проверкой кластером

Отличие от template: --dry-run обращается к API кластера (валидирует схемы ресурсов, проверяет .Capabilities по-настоящему), но ничего не применяет:

helm install web ./webapp -f values/prod.yaml --dry-run --debug
ИнструментКластерДля чего
helm templateне нуженбыстрый дебаг шаблонов локально/в CI
--dry-runнуженпроверка с реальными Capabilities и валидацией

helm test — проверка живого релиза

В templates/tests/ кладут Pod/Job с хуком helm.sh/hook: test, который проверяет работоспособность развёрнутого приложения (например, делает HTTP-запрос к сервису):

apiVersion: v1
kind: Pod
metadata:
  name: "{{ .Release.Name }}-test"
  annotations:
    "helm.sh/hook": test
spec:
  restartPolicy: Never
  containers:
    - name: curl
      image: curlimages/curl
      command: ["curl"]
      args: ["--fail", "http://{{ .Release.Name }}-webapp:80"]
helm install web ./webapp -n demo
helm test web -n demo            # запустить тест-поды
helm test web -n demo --logs     # с выводом логов

Если тест-под завершился с кодом 0 — тест пройден. Это smoke-проверка «приложение реально отвечает», а не модульный тест.

Зачем вообще нужен отдельный механизм тестов, если есть liveness/readiness-пробы? Пробы проверяют один Pod изнутри, а helm test смотрит на релиз снаружи и целиком: дошёл ли трафик через Service, отвечает ли миграция БД, видна ли новая фича по реальному эндпоинту. Это проверка интеграции, а не отдельного контейнера. Хороший приём — складывать в тест несколько разных проверок (доступность сервиса, миграция, конфиг) как отдельные тест-Pod'ы; Helm запустит их все и покажет, какой именно упал.

Под капотом тест — это обычный хук жизненного цикла. Когда вы вызываете helm test, Helm находит манифесты с аннотацией helm.sh/hook: test, создаёт их в кластере, ждёт завершения и судит по коду выхода контейнера. По умолчанию тест-Pod'ы остаются после прогона, чтобы можно было посмотреть логи; убрать их сразу помогает аннотация helm.sh/hook-delete-policy со значением hook-succeeded или before-hook-creation. Учтите и обратную сторону: тест запускается вручную и в кластере — он не подходит для pre-merge CI, где кластера ещё нет. Поэтому в пайплайне тест ставят после деплоя на staging, а не вместо статических проверок.

Как уровни дополняют друг друга под капотом

Три инструмента покрывают три разных момента. lint и template работают со статикой чарта (до кластера): первый судит о структуре, второй материализует текст манифестов — оба ловят 90% ошибок шаблонизации и отступов. --dry-run добавляет валидацию схемами кластера: неверное поле в Deployment, недоступный apiVersion. helm test работает после установки и проверяет рантайм: поднялось ли, отвечает ли. В CI обычно ставят их конвейером: lint → template (или dry-run на тестовом кластере) → install → test.

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

  • Полагаться только на lint. Он не рендерит со всеми вашими values и не ловит логические дыры; всегда смотрите helm template.
  • Путать template и --dry-run. Первый не видит кластер и подставляет стабы Capabilities; для проверки версий API нужен --dry-run.
  • Считать helm test юнит-тестом. Это smoke-проверка живого релиза, а не тест шаблонов.

Итог

  • lint — структура; template — рендер без кластера (главный дебаг); --dry-run — рендер с валидацией кластером; test — smoke живого релиза.
  • «error converting YAML to JSON» отлаживают через helm template ... --debug.
  • В CI выстраивают конвейер lint → template/dry-run → install → test.
Проверьте себя
1. Чем helm template отличается от helm install --dry-run?
AНичем
Btemplate рендерит локально без кластера; --dry-run обращается к API для валидации, но не применяет
Ctemplate применяет манифесты
D--dry-run удаляет релиз
2. Что проверяет helm test?
AСинтаксис шаблонов
BРаботоспособность уже установленного релиза через test-хуки (smoke)
CВерсии зависимостей
DОтступы YAML
3. Каким инструментом удобнее всего отлаживать «error converting YAML to JSON»?
Ahelm lint
Bhelm template ... --debug — он показывает отрендеренный манифест со съехавшей строкой
Chelm test
Dhelm rollback