Анатомия чарта: из чего состоит пакет
Создаём первый чарт командой helm create и разбираем каждый файл и папку внутри.
Чарт — это директория с предсказуемой структурой. Helm узнаёт чарт по наличию
Chart.yamlв корне; всё остальное — соглашения, которым он следует.
Полезная аналогия: чарт — это как deb- или rpm-пакет в мире Linux, только для Kubernetes. У него есть метаданные (что за пакет, какая версия), есть «настройки по умолчанию» и есть содержимое, которое разворачивается в систему. Разница в том, что чарт не хранит готовые манифесты, а генерирует их при установке — поэтому один и тот же чарт ставит и dev-стенд с одной репликой, и прод-кластер с десятью, просто на разных значениях. Эта способность подстраиваться и делает чарт переиспользуемой единицей, а не одноразовым YAML-файлом.
Почему структура именно такая и почему её важно соблюдать? Helm не сканирует директорию «как получится» — у него жёсткие ожидания: дефолты он ищет в values.yaml, шаблоны — только в templates/, зависимости — только в charts/. Положите шаблон не в ту папку, и он либо не отрендерится, либо отрендерится не вовремя. Поэтому новичку выгоднее принять конвенцию как данность, а не выдумывать свою раскладку: вся экосистема (линтеры, IDE-плагины, CI-проверки, тысячи чужих чартов) рассчитана ровно на эту структуру.
Самый быстрый способ увидеть структуру — сгенерировать заготовку:
helm create webapp
tree webapp
Вывод:
webapp/ ├── Chart.yaml ├── values.yaml ├── charts/ ├── templates/ │ ├── _helpers.tpl │ ├── deployment.yaml │ ├── service.yaml │ ├── ingress.yaml │ ├── serviceaccount.yaml │ ├── hpa.yaml │ ├── NOTES.txt │ └── tests/ │ └── test-connection.yaml └── .helmignore
Chart.yaml — паспорт чарта
Обязательный файл с метаданными. Минимальный набор:
apiVersion: v2 # v2 = Helm 3
name: webapp
description: Веб-приложение
type: application # или library
version: 0.1.0 # версия ЧАРТА (SemVer)
appVersion: "1.0.0" # версия приложения внутри (строка!)
Запомните различие: version — версия упаковки (растёт при любой правке чарта), appVersion — версия вашего приложения (растёт при релизе кода). apiVersion: v2 отличает чарты Helm 3 от старых v1.
Почему два разных поля версии, а не одно? Потому что чарт и приложение живут своими жизнями. Вы можете поправить отступ в шаблоне, добавить новый параметр или исправить опечатку в комментарии — код приложения при этом ни на байт не изменился, но упаковка стала другой, и потребители должны это заметить. Тогда вы поднимаете version, оставив appVersion прежним. И наоборот: вышел новый образ приложения 2.5.0 — вы меняете appVersion и, как правило, тоже бампаете version чарта (раз изменился дефолтный тег). Эта пара версий — то, на что смотрят системы вроде ArgoCD и репозитории чартов, чтобы понять, что именно поменялось и нужно ли катить обновление.
Поле description и необязательные keywords, home, icon кажутся косметикой, но именно они формируют карточку чарта в репозитории и в выводе helm search. Если вы публикуете чарт для команды или сообщества, осмысленное описание экономит коллегам минуты разбирательств «а что это вообще ставит».
values.yaml — значения по умолчанию
Здесь живут все настраиваемые параметры с дефолтными значениями. Шаблоны обращаются к ним через .Values. Это «панель управления» чарта:
replicaCount: 1
image:
repository: nginx
tag: "1.25.3"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
Ключевая мысль: values.yaml — это не «конфиг для разработчика чарта», а интерфейс для того, кто чарт устанавливает. Автор шаблонов и установщик — часто разные люди. Установщик не обязан читать ваши шаблоны; он смотрит в values.yaml, видит доступные ручки и крутит их под свой стенд через -f my-values.yaml или --set. Поэтому к нему относятся как к документированному API: понятные имена, разумная вложенность, дефолты, при которых чарт хотя бы запустится без единой правки.
templates/ — шаблоны манифестов
Сердце чарта. Каждый .yaml здесь — шаблон Kubernetes-манифеста с вставками на языке Go templates. При install Helm рендерит их, подставляя значения, и применяет результат. Файлы, имена которых начинаются с подчёркивания (_helpers.tpl), — особые: они не дают манифестов сами по себе, а хранят переиспользуемые куски.
Стоит сразу принять важную вещь: имя файла в templates/ для Helm почти ничего не значит. Helm не смотрит, что файл называется deployment.yaml — он просто рендерит его содержимое и склеивает все результаты вместе, разделяя их по YAML-разделителю ---. Можно описать три ресурса в одном файле или раскидать по трём — итог в кластере будет одинаковым. Разбиение по файлам — это удобство для человека, а не требование движка. На практике придерживаются правила «один файл — один тип ресурса» (deployment.yaml, service.yaml, ingress.yaml): так чарт легче читать и ревьюить.
Подпапка templates/tests/ — отдельная история. Манифесты в ней помечены аннотацией helm.sh/hook: test и не разворачиваются при обычной установке. Они запускаются командой helm test уже после релиза и проверяют, что приложение действительно живо (например, что сервис отвечает на запрос). Это встроенный способ smoke-теста релиза, и сгенерированный test-connection.yaml — его минимальный пример.
charts/ — вложенные чарты (зависимости)
Сюда складываются subcharts — другие чарты, от которых зависит ваш (например, ваш webapp зависит от postgresql). Helm устанавливает их вместе с родителем. Подробно — в разделе про зависимости.
_helpers.tpl и NOTES.txt
_helpers.tpl — библиотека именованных шаблонов (например, генерация полного имени релиза), чтобы не повторять логику в каждом манифесте. NOTES.txt — текст, который Helm печатает после установки (как достучаться до приложения); тоже шаблонизируется.
.helmignore
Как .gitignore, но для упаковки чарта: перечисляет, что не класть в .tgz (тесты, README-черновики, мусор IDE).
Как Helm читает чарт под капотом
При любой операции Helm загружает чарт в память как дерево: метаданные из Chart.yaml, дефолты из values.yaml, все файлы templates/ и рекурсивно все subchart-ы из charts/. Затем он формирует объект контекста (.Values, .Release, .Chart, .Capabilities) и прогоняет каждый шаблон из templates/ через движок Go template. Файлы _*.tpl и NOTES.txt в финальные манифесты не превращаются. Всё, что отрендерилось в непустой YAML, отправляется в кластер.
Тонкость, которая экономит часы отладки: рендеринг и применение — это две раздельные фазы. Сначала Helm целиком собирает текст всех манифестов на своей стороне, ничего ещё не отправляя в кластер. И только если весь набор собрался без ошибок шаблонизатора, он передаёт результат в Kubernetes. Поэтому ошибку в шаблоне можно поймать заранее, без риска что-либо сломать, командой helm template — она проходит ровно первую фазу и печатает готовый YAML в терминал. Это ваш главный инструмент при разработке чарта: правите шаблон, гоняете helm template, смотрите вывод, повторяете.
Ещё одна деталь про пустоту. Если шаблон после рендера дал пустую строку или только комментарии — Helm не создаст из него ресурс, а просто пропустит. Это используют намеренно: оборачивают весь манифест в if, и при выключенном параметре файл «исчезает». Так один чарт умеет включать и выключать целые ресурсы (ingress, hpa, serviceaccount) без удаления файлов — достаточно флага в values.
Частые ошибки
- Файл в
templates/, который не должен рендериться. Любой не-_файл там Helm попытается отрендерить как манифест. Вспомогательные куски называйте с подчёркиванием. - Число вместо строки в
appVersion. Версии вроде1.0в YAML станут числом1; берите в кавычки. - Путать
versionиappVersion. При правке шаблона поднимать нужноversionчарта, даже если код приложения не менялся.
Итог
helm createдаёт каноническую структуру; чарт опознаётся поChart.yaml.Chart.yaml— метаданные,values.yaml— дефолты,templates/— шаблоны,charts/— зависимости.- Файлы
_*.tplиNOTES.txtне становятся манифестами — это вспомогательные.