Советы и приёмы при разработке чартов
В этом руководстве описаны советы и приёмы, которые разработчики чартов Helm освоили при создании чартов производственного качества.
Знайте свои функции шаблонов
Helm использует Go templates для шаблонизации ваших файлов ресурсов. Хотя Go включает несколько встроенных функций, мы добавили множество других.
Во-первых, мы добавили все функции из библиотеки Sprig,
за исключением env и expandenv по соображениям безопасности.
Также мы добавили две специальные функции шаблонов: include и required. Функция
include позволяет подключить другой шаблон, а затем передать результат другим
функциям шаблонов.
Например, этот фрагмент шаблона подключает шаблон mytpl, приводит результат
к нижнему регистру, а затем оборачивает его в двойные кавычки.
value: {{ include "mytpl" . | lower | quote }}
Функция required позволяет объявить определённое значение как обязательное для
рендеринга шаблона. Если значение пустое, рендеринг шаблона завершится с ошибкой,
содержащей сообщение, указанное пользователем.
Следующий пример функции required объявляет запись .Values.who как обязательную
и выводит сообщение об ошибке, если эта запись отсутствует:
value: {{ required "A valid .Values.who entry required!" .Values.who }}
Оборачивайте строки в кавычки, не оборачивайте целые числа
При работе со строковыми данными всегда безопаснее оборачивать строки в кавычки, чем оставлять их без кавычек:
name: {{ .Values.MyName | quote }}
Но при работе с целыми числами не оборачивайте значения в кавычки. Это может во многих случаях вызвать ошибки парсинга в Kubernetes.
port: {{ .Values.Port }}
Это замечание не относится к значениям переменных окружения, которые ожидаются как строки, даже если представляют собой числа:
env:
- name: HOST
value: "http://host"
- name: PORT
value: "1234"
Использование функции 'include'
Go предоставляет способ подключения одного шаблона в другой с помощью встроенной
директивы template. Однако встроенную функцию нельзя использовать в конвейерах
Go templates.
Чтобы сделать возможным подключение шаблона и последующее выполнение операции
над его выводом, Helm предоставляет специальную функцию include:
{{ include "toYaml" $value | indent 2 }}
Приведённый выше код подключает шаблон toYaml, передаёт ему $value, а затем
передаёт вывод этого шаблона функции indent.
Поскольку YAML придаёт значение уровням отступов и пробелам, это отличный способ подключать фрагменты кода с правильной обработкой отступов в нужном контексте.
Использование функции 'required'
Go позволяет устанавливать параметры шаблона для управления поведением при обращении
к map с ключом, который в ней отсутствует. Обычно это делается с помощью
template.Options("missingkey=option"), где option может быть default,
zero или error. Установка этого параметра в значение error остановит выполнение
с ошибкой, но это будет применяться к каждому отсутствующему ключу в map. Могут
возникать ситуации, когда разработчик чарта хочет применить такое поведение
к определённым значениям в файле values.yaml.
Функция required позволяет разработчикам объявлять значение как обязательное
для рендеринга шаблона. Если запись пуста в values.yaml, шаблон не будет
отрендерен и вернёт сообщение об ошибке, указанное разработчиком.
Пример:
{{ required "A valid foo is required!" .Values.foo }}
Приведённый выше код отрендерит шаблон, когда .Values.foo определена, но не
сможет отрендерить его и завершится с ошибкой, когда .Values.foo не определена.
Использование функции 'tpl'
Функция tpl позволяет разработчикам вычислять строки как шаблоны внутри шаблона.
Это полезно для передачи строки шаблона в качестве значения чарту или для рендеринга
внешних конфигурационных файлов. Синтаксис: {{ tpl TEMPLATE_STRING VALUES }}
Примеры:
# values
template: "{{ .Values.name }}"
name: "Tom"
# template
{{ tpl .Values.template . }}
# output
Tom
Рендеринг внешнего конфигурационного файла:
# external configuration file conf/app.conf
firstName={{ .Values.firstName }}
lastName={{ .Values.lastName }}
# values
firstName: Peter
lastName: Parker
# template
{{ tpl (.Files.Get "conf/app.conf") . }}
# output
firstName=Peter
lastName=Parker
Создание Image Pull Secrets
Image pull secrets — это комбинация registry, username и password.
Они могут понадобиться в развёртываемом приложении, но для их создания необходимо
несколько раз выполнить кодирование base64. Можно написать вспомогательный шаблон,
который создаст конфигурационный файл Docker для использования в Secret.
Вот пример:
Сначала предположим, что учётные данные определены в файле values.yaml
следующим образом:
imageCredentials:
registry: quay.io
username: someone
password: sillyness
email: someone@host.com
Затем мы определяем вспомогательный шаблон следующим образом:
{{- define "imagePullSecret" }}
{{- with .Values.imageCredentials }}
{{- printf "{\"auths\":{\"%s\":{\"username\":\"%s\",\"password\":%s,\"email\":\"%s\",\"auth\":\"%s\"}}}" .registry .username (.password | quote) .email (printf "%s:%s" .username .password | b64enc) | b64enc }}
{{- end }}
{{- end }}
Наконец, мы используем вспомогательный шаблон в более крупном шаблоне для создания манифеста Secret:
apiVersion: v1
kind: Secret
metadata:
name: myregistrykey
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: {{ template "imagePullSecret" . }}
Автоматический перезапуск Deployment
Часто ConfigMap или Secret внедряются как конфигурационные файлы в контейнеры,
или есть другие изменения внешних зависимостей, требующие перезапуска подов.
В зависимости от приложения при последующем helm upgrade может потребоваться
перезапуск, но если спецификация deployment не изменилась, приложение продолжит
работать со старой конфигурацией, что приведёт к несогласованному развёртыванию.
Функцию sha256sum можно использовать для обновления секции аннотаций deployment
при изменении другого файла:
kind: Deployment
spec:
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
[...]
ПРИМЕЧАНИЕ: Если вы добавляете это в библиотечный чарт, вы не сможете получить
доступ к вашему файлу через $.Template.BasePath. Вместо этого вы можете
сослаться на ваше определение с помощью {{ include ("mylibchart.configmap") . | sha256sum }}.
Если вы хотите, чтобы ваш deployment всегда перезапускался, можно использовать аналогичную аннотацию, заменив значение случайной строкой, чтобы оно всегда менялось и вызывало перезапуск deployment:
kind: Deployment
spec:
template:
metadata:
annotations:
rollme: {{ randAlphaNum 5 | quote }}
[...]
Каждый вызов функции шаблона будет генерировать уникальную случайную строку. Это означает, что если необходимо синхронизировать случайные строки, используемые файле шаблона.
Оба эти метода позволяют вашему Deployment использовать встроенную логику стратегии обновления для избежания простоев.
ПРИМЕЧАНИЕ: Ранее мы рекомендовали использовать флаг --recreate-pods как
альтернативный вариант. Этот флаг был помечен как устаревший в Helm 3 в пользу
более декларативного метода, описанного выше.
Запрет удаления ресурса при деинсталляции
Иногда есть ресурсы, которые не должны удаляться при выполнении helm uninstall.
Разработчики чартов могут добавить аннотацию к ресурсу, чтобы предотвратить его
удаление.
kind: Secret
metadata:
annotations:
helm.sh/resource-policy: keep
[...]
Аннотация helm.sh/resource-policy: keep указывает Helm пропустить удаление
этого ресурса при операции helm (например, helm uninstall, helm upgrade или
helm rollback), которая привела бы к его удалению. Однако этот ресурс становится
осиротевшим. Helm больше не будет им управлять. Это может привести к проблемам
при использовании helm install --replace для релиза, который был удалён,
но сохранил ресурсы.
Использование «Partials» и подключение шаблонов
Иногда требуется создать переиспользуемые части в чарте — блоки или частичные шаблоны. Часто удобнее хранить их в отдельных файлах.
В директории templates/ любой файл, начинающийся с подчёркивания (_), не будет
выводиться как манифест Kubernetes. По соглашению вспомогательные шаблоны
и partials размещаются в файле _helpers.tpl.
Сложные чарты с множеством зависимостей
Многие чарты в Artifact Hub CNCF являются «строительными блоками» для создания более сложных приложений. Но чарты также могут использоваться для создания экземпляров крупномасштабных приложений. В таких случаях один зонтичный чарт может иметь несколько подчартов, каждый из которых функционирует как часть целого.
Текущая лучшая практика для создания сложного приложения из отдельных частей —
создать зонтичный чарт верхнего уровня, который предоставляет глобальные
конфигурации, и использовать поддиректорию charts/ для встраивания каждого
из компонентов.
YAML — это надмножество JSON
Согласно спецификации YAML, YAML является надмножеством JSON. Это означает, что любая допустимая структура JSON должна быть допустимой в YAML.
Это даёт преимущество: иногда разработчикам шаблонов может быть проще выразить структуру данных с помощью JSON-подобного синтаксиса, чем работать с чувствительностью YAML к пробелам.
В качестве лучшей практики шаблоны должны следовать YAML-подобному синтаксису, если только синтаксис JSON существенно не снижает риск проблем с форматированием.
Будьте осторожны при генерации случайных значений
В Helm есть функции для генерации случайных данных, криптографических ключей и т.д. Их можно использовать. Но имейте в виду, что при обновлениях шаблоны выполняются повторно. Когда выполнение шаблона генерирует данные, отличающиеся от предыдущего выполнения, это вызовет обновление этого ресурса.
Установка или обновление релиза одной командой
Helm предоставляет возможность выполнить установку или обновление одной командой.
Используйте helm upgrade с флагом --install. Это заставит Helm проверить,
установлен ли уже релиз. Если нет — будет выполнена установка. Если да —
существующий релиз будет обновлён.
$ helm upgrade --install <release name> --values <values file> <chart directory>