Отладка и тестирование чарта: 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.