====== Helm Charts ======
[[https://habr.com/ru/companies/otus/articles/790710/|habr]]\\
[[https://habr.com/ru/articles/769046/#p13|habr2]]\\
**Helm** - менеджер пакетов для k8s. Позволяет обернуть приложения в пакеты и работать с ними\\
**Charts** - собсна пакеты, включают в себя все что нужно приложению для запуска в k8s. Позволяет работать с приложением как с цельным пакетом а не набором отдельных манифестов\\
* **Chart.yaml** основной файл, собсна описание приложения
* **Values.yaml** файл с переменными, распарсенные данные и прочие переменные нужно сюда передать очевидно
* **Template/** - директория содержит шаблоны манифестов, использует данные из Chart.yaml и Values.yaml
* **Template/NOTES.txt** - readme, тоже шаблонизируется из файлов выше
* **Templates/*.tpl** - именованные шаблоны
* **Charts/** - директория подчартов, если требуются другие чарты, они размещаются здесь
* **.helmignore** - файл для исключения файлов из собираемого пакета
=== Post-Renderers ===
Есть функционал для добавления логики после подготовки манифестов но перед применением\\
По сути сводится к баш скрипту, через который прогоняется текстовый поток всех манифестов, в нем с-но можно делать правки\\
Вызывается параметр в команде запуска хельма\\
helm install my-chart ./mychart --post-renderer ./post-render.sh
=== Helm Dependencies ===
Зависимости можно описать в "Chart.yaml" или "requirements.yaml", указав название, версию, репозиторий нужных чартов. Тогда хельм будет автоматически их все тянуть\\
Для работы с ними есть команды "helm dependency [list, update, build]"\\
=== Порядок запуска (деплоя) ресурсов ===
Есть стандартный порядок, можно им управлять с помощью хуков\\
Задается аннотацией прямо в манифесте, так же у хуков есть "вес", в рамках одного и того же шага\\
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
...
annotations:
"helm.sh/hook": pre-install
"helm.sh/hook-weight": "-2"
==== To use ====
[[https://helm.sh/docs/chart_template_guide|Офф дока]]\\
:!: Команды
**Чарт** - предварительно настроенный шаблон ресурсов\\
**Релиз** - чарт, развернутый с помощью Helm в k8s\\
* **create** - создает новый чарт с указанным именем
* **package** - упаковать каталог чарта в архив
* **show [chart|values|all] ** - показать информацию о чарте
* **get** - расширенная информация о релизе
* **verify** - верификация чарта
* **search** - поиск в чарте
* **pull** - загрузить чарт из репы
* **repo** - работа с репами
* **install** - установить чарт
* **upgrade** - обновить релиз
* **list** - список релизов
* **status** - статус релиза
* **rollback** - откат релиза на предыдущую версию
* **history** - история релизов
* **test** - запустить тесты релиза
* **uninstall** - деинсталлировать релиз
* **completion** - создает сценарий автозаполнения
* **dependency** - управление зависимостями
* **env** - информация о среде
* **lint** - проверить чарт на возможные проблемы
* **plugin** - работа с плагинами
* **template** - локальное отображение шаблонов (?)
* **version** -
helm install
# Тест итогового манифеста
helm install --dry-run test-release ./helm-chart
# Переопределение параметров при запуске (можно указать файлом)
helm install -n kubectl-ns --set certificateSecretStore.enabled=false my-app ./helm-chart
helm install -n kubectl-ns -f ./my-app-values.yaml my-app ./helm-chart
:!: Шаблонизация
# Просто переменная
metadata:
name: {{ .Release.Name }}-certificate-secret-store
---
# Вставляем шаблон "helm-chart.labels" и передаем вывод через шаблонную ф-ю "nident"
# в данном случае она добавляет пробелы (4шт)
metadata:
labels:
{{- include "helm-chart.labels" . | nindent 4 }}
---
#
Есть ф-я "toYaml", пока неясно как но видимо в чем то помогает\\
spec:
...
template:
...
spec:
containers:
- name: {{ .Release.Name }}-app
image: {{ .Values.deployment.image }}
ports:
- name: {{ .Values.clusterip.targetPort }}
containerPort: {{ .Values.deployment.containerPort }}
resources:
{{- toYaml .Values.deployment.resources | nindent 10 }}
{{- if .Values.lockboxSecretStore.enabled }}
env:
{{- range .Values.lockboxSecretStore.externalSecret.data }}
- name: {{ .secretKey }}
valueFrom:
secretKeyRef:
name: {{ $.Release.Name }}-lockbox-secret
key: {{ .secretKey }}
{{- end }}
{{- end }}
values.yaml в данном случае такой
clusterip:
port: 80
targetPort: http
deployment:
replicaCount: 1
image: ""
containerPort: 8080
resources:
requests:
cpu: "150m"
memory: "400Mi"
limits:
cpu: "250m"
memory: "600Mi"
:!: IF
Включение/Отключение частей кода с помощью переменных\\
Объявляем нужные переменные в "values.yaml", значение по умолчанию "true"\\
lockboxSecretStore:
enabled: true
certificateSecretStore:
enabled: true
Затем в манифесте проверка переменных, ветвление
{{- if .Values.certificateSecretStore.enabled -}}
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
...
{{- end }}
При запуске, переменную можно переопределить
helm install --dry-run --set certificateSecretStore.enabled=false test-release ./helm-chart
Пример с IF. Переменные в шаблоне\\
В двух циклах проверяем наличие чего либо во вложенном списке, если есть присваиваем контрольной переменной значение и на основании создаем или не создаем манифест\\
{{ $my_var := false }}
{{- range $svc := .Values.list_sys_services }}
{{- range $port := $svc.listen_ports }}
{{ $my_var = true }}
{{- end }}
{{- end }}
{{- range $svc := .Values.list_plugin_services }}
{{- range $port := $svc.listen_ports }}
{{ $my_var = true }}
{{- end }}
{{- end -}}
{{- if $my_var }}
manifest: "exist"
var_before: {{ $my_var }}
{{- end -}}
:!: FOR
Для циклов используется оператор "range"\\
lockboxSecretStore:
enabled: true
externalSecret:
secretId: ""
data:
- secretKey: JDBC_URL
property: JDBC_URL
- secretKey: DB_USERNAME
property: DB_USERNAME
- secretKey: DB_PASSWORD
property: DB_PASSWORD
spec:
...
data:
{{- range .Values.lockboxSecretStore.externalSecret.data }}
- secretKey: {{ .secretKey }}
remoteRef:
key: {{ $.Values.lockboxSecretStore.externalSecret.secretId }}
property: {{ .property }}
{{- end }}
:!: Знак $ - операторы "range" и "with" создают свою область видимости (точка указывает на текущую область). Чтобы получить значения из "values.yaml" нужно задать корневую область видимости, что и делает этот параметр\\
:!: Циклы
Values:
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions
- pineapple
Temlate:
toppings: |-
{{- range .Values.pizzaToppings }}
- {{ . | title | quote }}
{{- end }}
Цикл по кол-ву указанному в переменной, конкатенация со строкой, приведение типа плюс обращение к элементу списка по индексу
{{- range $index, $count := until (int .Values.jpa.dataSources.count) }}
db_{{ $index }}:
url: {{ index $.Values.jpa.dataSources.url $index }}
username: {{ index $.Values.jpa.dataSources.username $index }}
name: {{ index $.Values.jpa.dataSources.name $index }}
description: {{ index $.Values.jpa.dataSources.description $index }}
{{- end }}
Еще примеры циклов - перечень указанных значений (tuple)
sizes: |-
{{- range tuple "small" "medium" "large" }}
- {{ . }}
{{- end }}
Цикл по словарю
toppings: |-
{{- range $index, $topping := .Values.pizzaToppings }}
{{ $index }}: {{ $topping }}
{{- end }}
Цикл по списку плюс числовой индекс
Values:
ssl:
sslcert_keyname: ["cert1", "cert2", "cert3"]
-----
Templ:
{{- range $index, $item := .Values.back_conf.app.jpa.dataSources.ssl.sslcert_keyname }}
{{ . }}
{{ $index }}
{{ $item }}
###############
{{- end }}
------
Result:
cert1
0
cert1
###############
cert2
1
cert2
###############
=== Шаблонизация ===
★ Если файл yaml то все должно быть четко yaml, иначе шаблонизатор падает\\
прям душнила .......\\
★ "удалятры" (-) удаляют пустые строки\\
**helm template . --debug**\\
:!: Валидация переменных при шаблонизации
# Values
my_string: "this string"
my_list: ["this", "list"]
my_empty_list: []
# В шаблоне
string: {{ .Values.my_string }}
list: {{ .Values.my_list }}
empty_list: {{ .Values.my_empty_list }}
none_list: {{ .Values.my_none_list }}
# Вывод
string: this string
list: [this list]
empty_list: []
none_list:
В случае несуществующей переменной, в блоке IF можно дополнительно не приседать, если переменной нет то блок IF не отработает\\
{{- if .Values.no_exist_var }}
no_exist_var: exist
{{ end }}
Цикл при несуществующей переменной тоже нормально отрабатывает\\
Еще пример по работе с циклом. Обращение к элементу\\
{{- range $item := .Values.my_list }}
item: {{ $item }}
{{- end }}
# Альтернатива
{{- range .Values.my_list }}
item: {{ . }}
{{- end }}
:!: Работа с файлами
Вставка содержимого файла в шаблоне\\
Переменные
config_service_version: v1
config_service_dir: config
service: test
Шаблон
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Values.service }}-{{ .Values.config_service_version }}
data:
config.json: |-
{{ .Files.Get (printf "%s/%s-%s.json" .Values.config_service_dir .Values.service .Values.config_service_version ) | indent 4 }}
config_base64.json: |-
{{ .Files.Get "my-file.json" | indent 4 | b64enc }}
Специально для манифестов\\
Эти функции сами подставляются в формате "имя-файла: значение", секреты сразу кодируются\\
apiVersion: v1
kind: ConfigMap
metadata:
name: conf
data:
{{ (.Files.Glob "foo/*").AsConfig | indent 2 }}
---
apiVersion: v1
kind: Secret
metadata:
name: very-secret
type: Opaque
data:
{{ (.Files.Glob "bar/*").AsSecrets | indent 2 }}
Проверка существования файла
{{ $res := .Files.Glob "input_files/resources.zip" }}
{{- if $res }}
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: my-secrets
labels:
app: my-app
data:
{{ ($res).AsSecrets | indent 2 }}
{{ end }}
:!: Еще про переменные
★ Область видимости, если объявлена в блоке, будет доступна только в блоке, глобальные так же доступны в блоке, имена могут пересекаться\\
★ ":=" объявляет новую переменную, "=" присваивает значение существующей\\
# Объявили переменную
{{ $my_var := "first" }}
port1: {{ $my_var }}
# Присвоили новое значение
{{- $my_var = "second" }}
port2: {{ $my_var }}
# Внутри блока (цикла) присвоили новое значение
{{ range $svc := .Values.list_sys_services }}
{{ $my_var = "three" }}
var_in_range_after: {{ $my_var }}
# Если использовать := то будет новая, локальная переменная, имя может пересекаться с глобальной
{{ $my_var = "this_is_local" }}
# здесь уже применяется локальная
var_in_range_after: {{ $my_var }}
{{- end }}
# здесь глобальная, с обновленным значением, "three"
var_after_range: {{ $my_var }}
:!: Примеры в шаблонизации
Условие, дефолтное значение, кавычки
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}
{{- if eq .Values.favorite.drink "coffee" }}
mug: "true"
{{- end }}
Оператор With
Values:
favorite:
drink: coffee
food: pizza
----------
Temlate:
myvalue: "Hello World"
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}
Приведение типа переменной
{{- range $index, $count := until (int .Values.jpa.dataSources.count) }}
Арифметика\\
addf, subf, mulf, divf\\
minAvailable: {{ mulf .Values.autoscaling.minReplicas 0.75 }}
:!: Работа со списками
until\\
Функция until создает диапазон целых чисел\\
# [0, 1, 2, 3, 4]
range $i, $e := until 5
seq\\
Работает аналогично команде bash seq.\\
Параметр 1 (end) - генерирует все счетные числа от 1 до end включительно.\\
Параметр 2 (start, end) - генерирует все счетные числа от start до end включительно, увеличивая или уменьшая их на 1.\\
3 параметра (start, step, end) - будут сгенерированы все счетные целые числа от начала до конца, включая пошаговое увеличение или уменьшение.\\
len\\
Длинна строки либо списка\\
{{ len .Values.jpa.dataSources.url }}
:!: Проход по списку, уникальные значения
# Конкатенация (дополнение) списков
{{ $list_ports = concat $list_ports (list ($item.port | default 5432)) }}
{{ $list_ports = append $list_ports ($item.port | default 5432) }}
# Формирование списка из словаря в цикле, затем удление дубликатов
{{ $list_ports := list }}
{{- range $index, $item := .Values.egress_route.postgresql }}
{{- $list_ports = append $list_ports ($item.port | default 5432) }}
{{- end }}
result: {{ $list_ports | sortAlpha | uniq }}
# вариант со словарем
{{- $used_port := dict }}
{{- range $index, $item := .Values.egress_route.postgresql }}
{{- $var_port := (($item.port | default 5432) | toString) }}
{{- if not (hasKey $used_port $var_port) }}
port: {{ $item.port }}
{{- $_ := set $used_port $var_port $item.port }}
{{- end }}
{{- end }}
:!: