Тестирование 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, чтобы явно указать чарты.