Зависимости чарта и subcharts

Приложению часто нужна база или кеш. Вместо отдельной установки их подключают как зависимости чарта.

Subchart — это другой чарт, объявленный в dependencies родителя. Helm устанавливает родителя и его subchart-ы как единый релиз.

Зачем подключать чужие чарты, а не ставить их отдельно

Можно установить PostgreSQL отдельным релизом, а приложение — своим, и связать их вручную через адреса сервисов. Так делают, и это нормально. Но как только число компонентов растёт, ручная связка превращается в проблему: их надо устанавливать в правильном порядке, обновлять согласованно, откатывать вместе, передавать друг другу пароли и хосты. Subchart-ы решают это композицией: вы описываете «моё приложение состоит из веб-сервиса, базы и кеша» в одном чарте, и весь набор живёт как единое целое — один helm install, один helm upgrade, один helm rollback, одна точка истины для значений. Это особенно ценно для эфемерных окружений: чтобы поднять полную копию системы под pull request, достаточно одной команды.

У этого удобства есть цена, о которой стоит думать заранее. Чужой subchart (тот же bitnami/postgresql) — это код, который вы не писали и не контролируете: у него своя политика версионирования, свои значения по умолчанию, свои представления о безопасности и ресурсах. Подключив его, вы берёте на себя ответственность следить за его обновлениями (там бывают CVE) и понимать его values.yaml достаточно, чтобы настроить под себя. Для продакшена многие команды сознательно не используют встроенный stateful-subchart базы данных, предпочитая управляемый сервис (RDS, Cloud SQL), и оставляют subchart только для dev — ровно тот сценарий, ради которого существует condition.

Объявление зависимости

# Chart.yaml
dependencies:
  - name: postgresql
    version: "13.2.24"
    repository: https://charts.bitnami.com/bitnami
    condition: postgresql.enabled
  - name: redis
    version: "18.x.x"
    repository: https://charts.bitnami.com/bitnami
    condition: redis.enabled
    tags:
      - cache

Загрузка зависимостей

Объявить мало — нужно скачать чарты в папку charts/:

helm dependency update ./webapp    # скачать по Chart.yaml -> charts/*.tgz
helm dependency list ./webapp      # показать статус зависимостей

Это создаёт файл Chart.lock с зафиксированными версиями — аналог package-lock.json. Коммитьте его для воспроизводимости.

Различие между helm dependency update и helm dependency build стоит запомнить, потому что в CI путают именно его. update идёт в репозитории, разрешает диапазоны версий вроде 18.x.x в конкретную последнюю подходящую версию и перезаписывает Chart.lock. build, наоборот, читает уже существующий Chart.lock и скачивает ровно те версии, что в нём зафиксированы, ничего не разрешая заново. Правило простое: на машине разработчика, когда вы осознанно поднимаете версию зависимости, — update; в пайплайне сборки, где нужна воспроизводимость один-в-один, — build по закоммиченному локу. Если в CI звать update, вы рискуете молча подтянуть новую минорную версию subchart-а, которую никто не тестировал.

Передача values в subchart

Значения subchart-а живут в родительском values.yaml под ключом с именем subchart-а:

# values.yaml родителя
postgresql:                # всё внутри уходит в subchart postgresql
  enabled: true
  auth:
    username: app
    database: webapp
  primary:
    persistence:
      size: 8Gi
redis:
  enabled: false           # выключен через condition

Ключ postgresql совпадает с name зависимости — так Helm понимает, кому адресованы значения.

condition: включить/выключить зависимость

condition: postgresql.enabled означает: subchart разворачивается, только если .Values.postgresql.enabled истинно. Это позволяет в проде использовать внешнюю управляемую БД (выключив встроенный postgresql), а в dev — встроенный:

# values/prod.yaml — внешняя БД, встроенную не поднимаем
postgresql:
  enabled: false
externalDatabase:
  host: prod-db.rds.amazonaws.com

tags: групповое управление

Если несколько зависимостей помечены тегом (например, cache), их можно включать/выключать пачкой:

tags:
  cache: false   # выключит все зависимости с тегом cache

Глобальные значения для всех subchart

Секция global в родительских values видна всем subchart-ам — удобно для общих параметров (домен, imagePullSecrets):

global:
  imageRegistry: registry.example.com
  storageClass: fast-ssd

Важно понимать, чем global отличается от обычной передачи values по ключу-имени. Значения под ключом postgresql видит только subchart postgresql; значения под global видят все — и родитель, и каждый subchart, причём по одному и тому же пути .Values.global.*. Это единственный штатный канал, по которому родитель может «сверху» продиктовать общий параметр всем потомкам сразу. Но именно поэтому global легко превратить в свалку: туда тянет сложить всё подряд, и тогда непонятно, кто на что влияет. Здравое правило — держать в global только по-настоящему сквозные вещи: registry образов, storageClass, общий домен, секреты вытягивания образов. Всё, что специфично для одного компонента, должно жить под его собственным ключом.

Алиасы и переопределение значений зависимостей

Иногда один и тот же subchart нужен дважды — например, два независимых Redis: один под кеш, другой под очередь. Поле alias в зависимости даёт subchart-у новое имя, под которым он и рендерится, и принимает values. Тогда в Chart.yaml будут две записи с одним name: redis, но разными alias: redis-cache и alias: redis-queue, а в values появятся два независимых блока redis-cache: и redis-queue:. Это снимает конфликт имён, который иначе сделал бы двойное подключение невозможным. Рядом стоит упомянуть import-values — механизм, позволяющий «поднять» часть значений subchart-а в родителя, чтобы пользователь чарта не лез в детали вложенной структуры; в простых чартах он не нужен, но в больших платформенных — экономит много путаницы.

Как зависимости работают под капотом

При рендере Helm строит дерево чартов: родитель + subchart-ы (и их subchart-ы рекурсивно). Каждый subchart рендерится в своём контексте: его .Values — это поддерево из родителя под ключом-именем плюс global. Все отрендеренные манифесты родителя и subchart-ов собираются в один релиз и применяются вместе; ревизия тоже общая. Поэтому helm upgrade родителя обновляет и subchart-ы, а helm rollback откатывает всё дерево разом. Chart.lock фиксирует точные версии скачанных subchart-ов, чтобы сборка была воспроизводимой между машинами и временем.

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

  • Забыть helm dependency update. Объявил зависимость в Chart.yaml, но charts/ пуст — install падает с «found in Chart.yaml, but missing in charts/».
  • Неверный ключ values. Значения под ключом, не совпадающим с name subchart-а, до него не дойдут.
  • Не коммитить Chart.lock. Версии subchart-ов «уплывут» у коллеги.

Итог

  • Зависимости объявляют в Chart.yaml, скачивают через helm dependency update (фиксируется в Chart.lock).
  • Values subchart-а — под ключом с его именем; condition/tags включают и выключают зависимости.
  • Родитель и subchart-ы — единый релиз: общий upgrade/rollback; global делит значения между всеми.
Проверьте себя
1. Где задаются значения для subchart postgresql?
AВ Chart.lock
BВ родительском values.yaml под ключом postgresql
CВ отдельном репозитории
DВ templates/
2. Что делает condition: postgresql.enabled?
AВсегда ставит postgresql
BРазворачивает subchart только если .Values.postgresql.enabled истинно
CШифрует БД
DФиксирует версию
3. Почему install падает с «missing in charts/» после объявления зависимости?
AНеверная версия Helm
BНе выполнен helm dependency update — subchart не скачан в charts/
CНет интернета всегда
DОшибка в kubeconfig