====== 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 }}
:!: