Введение в Go templates: первые вставки
Превращаем статичный YAML в шаблон: первые подстановки значений через двойные фигурные скобки.
Helm использует движок Go templates. Всё, что заключено в
{{ ... }}, — это инструкция движку: подставить значение, вызвать функцию или выполнить условие. Остальной текст копируется как есть.
Прежде чем писать первую вставку, полезно понять, что мы вообще делаем. У нас есть статичный YAML-манифест, который работает только для одного конкретного случая: одно имя, один образ, одно число реплик. Шаблонизация — это операция «вынеси изменчивое наружу»: всё, что от установки к установке меняется, мы заменяем на вставку, которая будет подставлять значение из values.yaml. В итоге один файл-шаблон обслуживает бесконечное число конфигураций. Это ровно тот же приём, что параметр функции вместо захардкоженной константы — только применённый к тексту манифеста.
Важно сразу зафиксировать: Go templates — это не «язык Helm», а стандартный шаблонизатор из языка Go, который Helm взял и дополнил своими функциями и объектами. Поэтому базовый синтаксис (скобки, if, range, конвейеры |) одинаков с другими инструментами на Go. Знание переносимо, и при поиске в документации стоит держать в голове это разделение: часть конструкций — общие для Go, часть — добавки Helm и библиотеки Sprig.
Возьмём кусок Deployment и параметризуем его. Слева — то, что вы пишете в templates/, справа — что получится после рендера.
Подстановка значения
Шаблон (язык — смесь YAML и Go template, поэтому подсветка без запуска):
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-web
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: web
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
При values.yaml с replicaCount: 3, image.repository: nginx, image.tag: "1.25.3" и релизе с именем my получится:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-web
spec:
replicas: 3
template:
spec:
containers:
- name: web
image: "nginx:1.25.3"
Откуда берутся точки: контекст
Ведущая точка . — это корневой контекст, в котором живут встроенные объекты. .Values — ваши значения, .Release — данные о релизе (имя, namespace), .Chart — поля Chart.yaml. Точка-разделитель проходит по вложенности: .Values.image.tag = поле tag внутри image внутри values.
Стоит привыкнуть читать точку как «здесь и сейчас» — это указатель на текущий контекст, и поначалу он всегда корневой. Запись .Values.image.tag разбирается слева направо именно как навигация по дереву: «от текущего контекста возьми Values, в нём image, в нём tag». Если на любом шаге элемента нет, движок по умолчанию подставит пустое значение, а не упадёт — и это коварно: опечатка .Values.imgae.tag не вызовет ошибку, а просто даст пустоту, и манифест соберётся «успешно», но неправильно. Поэтому проверять вывод helm template глазами на ранней стадии — не паранойя, а норма.
Зачем вообще нужна эта пустота-по-умолчанию? Она удобна для необязательных полей: написали annotations: {{ .Values.podAnnotations }}, пользователь ничего не задал — получили пусто, и ничего не сломалось. Но та же черта прячет опечатки. Баланс между «не падать на отсутствующем» и «ловить ошибки» решается позже функциями вроде required и default — пока достаточно знать, что молчаливая пустота существует и за ней нужен глаз.
Обрезка пробелов: дефис в скобках
Шаблонные конструкции оставляют после себя пустые строки и пробелы, что ломает отступы YAML. Дефис - у скобки убирает пробелы рядом: {{- срезает пробелы слева, -}} — справа.
spec:
{{- if .Values.ingress.enabled }}
rules:
- host: {{ .Values.ingress.host }}
{{- end }}
Без {{- на месте if и end остались бы пустые строки, и YAML мог бы стать невалидным. Управление пробелами — едва ли не главная «боль новичка» в Helm.
Корень боли в том, что управляющие конструкции (if, range, end) сами занимают строку. После рендера выражение исчезает, но перенос строки и отступ перед ним остаются — и в YAML, где значимы именно отступы и переносы, этот «мусор» ломает структуру. В большинстве языков лишняя пустая строка безвредна, в YAML — нет, и в этом вся ловушка. Дефис у скобки — это явная команда «съешь соседние пробельные символы, включая перенос строки». Запомнить направление просто: дефис «смотрит» в ту сторону, где режет пробелы, — {{- режет слева от себя, -}} справа.
Практический совет: не сыпьте дефисами наугад «чтобы наверняка». Перебор тоже вреден — можно случайно склеить две строки, которые должны были остаться раздельными. Рабочий цикл такой: написали конструкцию, прогнали helm template, посмотрели на реальные отступы в выводе и точечно добавили дефис там, где зияет лишняя пустая строка. Глаз на отступы тренируется за пару дней, и потом эта «боль» становится механической привычкой.
Комментарии в шаблонах
{{/* Это комментарий шаблона: в вывод не попадёт */}}
# А это обычный YAML-комментарий: попадёт в манифест
Как Helm рендерит под капотом
Движок проходит шаблон как текст, ищет {{ }}, вычисляет выражения в контексте (где . = корневой объект) и подставляет результат как строку. Важно: Go template ничего не знает про YAML — для него это просто текст. Поэтому корректность отступов и кавычек целиком на вас. Сначала текст полностью собирается, и лишь потом готовая строка парсится как YAML и уходит в кластер. Если на этапе YAML-парсинга отступ поехал — Helm выдаст ошибку вроде error converting YAML to JSON.
Частые ошибки
- Забыть, что
.может меняться. Внутриrange/withточка переопределяется на текущий элемент — об этом отдельный урок. - Неэкранированные спецсимволы YAML. Значение с двоеточием или
#без кавычек сломает YAML; оборачивайте черезquote. - Лишние пробелы от конструкций. Без
{{-/-}}отступы съезжают — частая причина невалидного манифеста.
Итог
- Всё в
{{ }}вычисляется движком; остальной текст копируется как есть. .— корневой контекст:.Values,.Release,.Chart.- Дефис
{{-/-}}обрезает пробелы; движок не знает про YAML — отступы на вас.