Тестирование Helm-чартов: lint, unit-тесты и ct в CI
Published: 2026-03-26
Непротестированные Helm-чарты ломаются в production способами, которые сложно отладить: отсутствующее поле required, несоответствие меток между Service и Deployment, значение рендерится как null из-за опечатки. Три инструмента покрывают большинство случаев: helm lint, helm-unittest и chart-testing.
helm lint
Встроенный линтер ловит синтаксические ошибки и нарушения required-полей:
bashhelm lint charts/my-service
helm lint charts/my-service -f charts/my-service/values-dev.yaml
Всегда линтуйте с values-файлами каждого окружения, а не только с defaults. Значение, опциональное в values.yaml, может быть обязательным в values-dev.yaml.
CI:
yamllint:
stage: test
image: alpine/helm:3.16
script:
- |
for chart in charts/*/; do
helm lint "$chart"
helm lint "$chart" -f "${chart}values-dev.yaml" || true
done
helm-unittest
helm-unittest рендерит шаблоны с заданными values и проверяет результат рендеринга. Ловит логические ошибки, которые lint не видит: неправильное число реплик, отсутствующие метки.
Установка:
bashhelm plugin install https://github.com/helm-unittest/helm-unittest
Тесты в charts/my-service/tests/:
yamlsuite: deployment
templates:
- deployment.yaml
tests:
- it: должно быть 3 реплики в production
asserts:
- equal:
path: spec.replicas
value: 3
- it: должна быть 1 реплика в dev
set:
replicaCount: 1
asserts:
- equal:
path: spec.replicas
value: 1
- it: должна быть запрещена эскалация привилегий
asserts:
- equal:
path: spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation
value: false
- it: должны быть заданы requests на CPU
asserts:
- isNotEmpty:
path: spec.template.spec.containers[0].resources.requests.cpu
- it: selector Service должен совпадать с Deployment
templates:
- service.yaml
asserts:
- equal:
path: spec.selector.app
value: my-service
Запуск:
bashhelm unittest charts/my-service
chart-testing (ct)
ct автоматизирует проверку чартов в монорепо: определяет изменённые чарты, линтует их и устанавливает в реальный кластер:
yaml# ct.yaml в корне репо
target-branch: main
chart-dirs:
- charts
check-version-increment: true
bashct lint --config ct.yaml
ct install --config ct.yaml
С check-version-increment: true проверка падает, если шаблоны чарта изменены, а версия в Chart.yaml — нет.
Values-заглушки для ct install
ct install использует values из поддиректории ci/:
charts/my-service/
ci/
default-values.yaml
dev-values.yaml
Несколько файлов — несколько установок. Тестируется чарт с разными наборами values.
Полный пайплайн GitLab CI
yamlstages:
- lint
- test
- package
helm-lint:
stage: lint
image: alpine/helm:3.16
script:
- helm lint charts/my-service
helm-unittest:
stage: test
image: alpine/helm:3.16
before_script:
- helm plugin install https://github.com/helm-unittest/helm-unittest
script:
- helm unittest charts/my-service
ct-lint:
stage: lint
image: quay.io/helmpack/chart-testing:latest
script:
- ct lint --config ct.yaml --charts charts/my-service
helm-package:
stage: package
image: alpine/helm:3.16
script:
- helm package charts/my-service --destination dist/
artifacts:
paths:
- dist/*.tgz
only:
- main
Что тестировать, а что нет
Тестировать:
- Правильное количество реплик по окружениям
- Обязательные secrets/configmaps указаны
- SecurityContext (allowPrivilegeEscalation, readOnlyRootFilesystem)
- Selector Service совпадает с метками Deployment
- Resources requests/limits заданы
Пропустить:
- Внутренности Kubernetes (это его задача)
- Runtime-поведение (для этого интеграционные тесты)
Частые проблемы
Плагин helm-unittest долго устанавливается: предустановите его в собственный образ на базе alpine/helm:3.16.
ct lint падает на неизменённых чартах: используйте флаг --charts, чтобы явно указать чарты.