Функциональность Delivery доступна только если у вас есть лицензия на любую коммерческую версию Deckhouse Kubernetes Platform.
Основы
При организации доставки приложения в Kubernetes необходимо определиться с тем, какой формат выбрать для управления конфигурацией развёртывания (параметризации, управления зависимостями, конфигурации под различные окружения и т.д.), а также способом применения этой конфигурации – непосредственно механизмом развёртывания.
В Delivery встроен Helm, и именно он используется для решения перечисленных задач. Разработка и сопровождение конфигурации реализуется с помощью Helm-чарта, а для процесса развёртывания предлагается Helm c дополнительными возможностями:
- отслеживание состояния выкатываемых ресурсов (с возможностью изменения поведения для каждого ресурса):
- умное ожидание готовности ресурсов;
- мгновенное завершение проблемного развертывания без необходимости ожидания таймаута;
- прогресс развёртывания, логи, системные события и ошибки приложения.
- использование произвольного порядка развертывания для любых ресурсов, а не только для хуков;
- ожидание создания и готовности ресурсов, не принадлежащих релизу;
- интеграция сборки и развертывания и многое другое.
Delivery стремится сделать работу с Helm более простой, удобной и гибкой, при этом не ломая обратную совместимость с Helm-чартами, Helm-шаблонами и Helm-релизами.
Простой пример развертывания
Для развертывания простого приложения достаточно двух файлов и команды d8 d converge
, запущенной в Git-репозитории приложения:
# .helm/templates/hello.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
spec:
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- image: nginxdemos/hello:plain-text
# werf.yaml:
configVersion: 1
project: hello
d8 d converge --repo registry.example.org/repo --env production
Результат: Deployment hello
развёрнут в Namespace’е hello-production
.
Расширенный пример развертывания
Более сложный пример развертывания со сборкой образов и внешними Helm-чартами:
# werf.yaml:
configVersion: 1
project: myapp
---
image: backend
dockerfile: Dockerfile
# Dockerfile:
FROM node
WORKDIR /app
COPY . .
RUN npm ci
CMD ["node", "server.js"]
# .helm/Chart.yaml:
dependencies:
- name: postgresql
version: "~12.1.9"
repository: https://charts.bitnami.com/bitnami
# .helm/values.yaml:
backend:
replicas: 1
# .helm/templates/backend.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
replicas: {{ $.Values.backend.replicas }}
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- image: {{ $.Values.werf.image.backend }}
d8 d converge --repo registry.example.org/repo --env production
Результат: собран образ backend
, а затем Deployment backend
и ресурсы чарта postgresql
развёрнуты в Namespace’е myapp-production
.
Чарты и зависимости
О чартах
Чарты в Delivery – это Helm-чарты с некоторыми дополнительными возможностями. А Helm-чарты – это распространяемые пакеты с Helm-шаблонами, values-файлами и некоторыми метаданными. Из чартов формируются конечные Kubernetes-манифесты для дальнейшего развертывания.
Создание нового чарта
При развертывании c d8 d converge
или публикации c d8 d bundle publish
используется чарт, лежащий в директории .helm
в корне Git-репозитория. Этот чарт называется основным. Директорию основного чарта можно изменить директивой deploy.helmChartDir
файла werf.yaml
.
Для обычного чарта требуется создание файла Chart.yaml
и указание в нём имени и версии чарта:
# Chart.yaml:
apiVersion: v2
name: mychart
version: 1.2.3
А вот для основного чарта это не обязательно, т. к. при отсутствии файла Chart.yaml
или отсутствии в нём имени или версии чарта будет использована следующая конфигурация:
# .helm/Chart.yaml:
apiVersion: v2
name: <имя проекта Delivery>
version: 1.0.0
Если такая конфигурация основного чарта вас не устраивает, то создайте файл .helm/Chart.yaml
самостоятельно и переопределите вышеупомянутые директивы.
В случае, если ваш чарт будет содержать только именованные шаблоны для использования в других чартах, добавьте в Chart.yaml
директиву type: library
:
# Chart.yaml:
type: library
Если ваш чарт совместим только с частью версий Kubernetes, то ограничьте версии кластера Kubernetes, в которые чарт можно развернуть, директивой kubeVersion
, например:
# Chart.yaml:
kubeVersion: "~1.20.3"
При желании можно добавить следующие информационные директивы:
# Chart.yaml:
appVersion: "1.0"
deprecated: false
icon: https://example.org/mychart-icon.svg
description: This is My Chart
home: https://example.org
sources:
- https://github.com/my/chart
keywords:
- apps
annotations:
anyAdditionalInfo: here
maintainters:
- name: John Doe
email: john@example.org
url: https://john.example.org
Полученный чарт уже можно развернуть или опубликовать, хотя в таком виде от него мало пользы. Теперь вам понадобится, по меньшей мере, либо добавить шаблоны в директорию templates
, либо подключить зависимые чарты.
Добавление файлов в чарт
По мере необходимости добавьте в чарт шаблоны, параметры, зависимые чарты и прочее. Содержимое основного чарта может выглядеть так:
.helm/
charts/
dependent-chart/
# ...
templates/
deployment.yaml
_helpers.tpl
NOTES.txt
crds/
crd.yaml
secret/ # Только в Delivery
some-secret-file
values.yaml
values.schema.json
secret-values.yaml # Только в Delivery
Chart.yaml
Chart.lock
README.md
LICENSE
.helmignore
Подробнее:
-
charts/*
— зависимые чарты, чьи Helm-шаблоны/values-файлы используются для формирования манифестов наравне с Helm-шаблонами/values-файлами родительского чарта; -
templates/*.yaml
— Helm-шаблоны, из которых формируются Kubernetes-манифесты; -
templates/*.tpl
— файлы с Helm-шаблонами для использования в других Helm-шаблонах. Результат шаблонизации этих файлов игнорируется; -
templates/NOTES.txt
— Delivery отображает содержимое этого файла в терминале в конце каждого удачного развертывания; -
crds/*.yaml
— Custom Resource Definitions, которые развертываются до развертывания манифестов вtemplates
; -
secret/*
— (только в Delivery) зашифрованные файлы, расшифрованное содержимое которых можно подставлять в Helm-шаблоны; -
values.yaml
— файлы с декларативной конфигурацией для использования в Helm-шаблонах. Конфигурация в них может переопределяться переменными окружения, аргументами командной строки или другими values-файлами; -
values.schema.json
— JSON-схема для валидацииvalues.yaml
; -
secret-values.yaml
— (только в Delivery) зашифрованный файл с декларативной конфигурацией, аналогичныйvalues.yaml
. Его расшифрованное содержимое объединяется сvalues.yaml
во время формирования манифестов; -
Chart.yaml
— основная конфигурация и метаданные чарта; -
Chart.lock
— lock-файл, защищающий от нежелательного изменения/обновления зависимых чартов; -
README.md
— документация чарта; -
LICENSE
— лицензия чарта; -
.helmignore
— список файлов в директории чарта, которые не нужно включать в чарт при его публикации.
Подключение дополнительных чартов
Подключение зависимых локальных чартов
Подключить зависимые локальные чарты можно, положив их в директорию charts
родительского чарта. В таком случае манифесты сформируются и для родительского, и для зависимых чартов, после чего объединятся вместе для дальнейшего развертывания. Дополнительная конфигурации не обязательна.
Пример содержимого основного чарта, имеющего локальные зависимые чарты:
.helm/
charts/
postgresql/
templates/
postgresql.yaml
Chart.yaml
redis/
templates/
redis.yaml
Chart.yaml
templates/
backend.yaml
Обратите внимание, что у локальных зависимых чартов имя должно обязательно совпадать с именем их директории.
Если локальному зависимому чарту требуется дополнительная конфигурация, то в файле Chart.yaml
родительского чарта укажите имя зависимого чарта без указания dependencies[].repository
и добавьте интересующие директивы таким образом:
# .helm/Chart.yaml:
apiVersion: v2
dependencies:
- name: redis
condition: redis.enabled
При необходимости подключить локальный чарт не из директории charts
, а из другого места, используйте директиву dependencies[].repository
так:
# .helm/Chart.yaml:
apiVersion: v2
dependencies:
- name: redis
repository: file://../redis
Подключение зависимых чартов из репозитория
Подключить дополнительные чарты из OCI/HTTP-репозитория можно, указав их как зависимые в директиве dependencies
файла Chart.yaml
родительского чарта. В таком случае манифесты сформируются и для родительского, и для зависимых чартов, после чего объединятся вместе для дальнейшего развертывания.
Пример конфигурации чарта из репозитория, зависимого от основного чарта:
# .helm/Chart.yaml:
apiVersion: v2
dependencies:
- name: database
version: "~1.2.3"
repository: https://example.com/charts
После каждого добавления/обновления удалённых зависимых чартов или изменения их конфигурации требуется:
-
(Если используется приватный OCI или HTTP-репозиторий c зависимым чартом) Добавить OCI или HTTP-репозиторий вручную с
d8 d helm repo add
, указав нужные опции для доступа к репозиторию. -
Вызвать
d8 d helm dependency update
, который обновитChart.lock
. -
Закоммитить обновлённые
Chart.yaml
иChart.lock
в Git.
Также при использовании чартов из репозитория рекомендуется добавить .helm/charts/**.tgz
в .gitignore
.
Указание имени подключаемого чарта
В директиве dependencies[].name
родительского чарта указывается оригинальное имя зависимого чарта, установленное его разработчиком, например:
# .helm/Chart.yaml:
apiVersion: v2
dependencies:
- name: backend
Если нужно подключить несколько зависимых чартов с одинаковым именем или подключить один и тот же зависимый чарт несколько раз, то используйте директиву dependencies[].alias
родительского чарта, чтобы поменять имена подключаемых чартов, например:
# .helm/Chart.yaml:
apiVersion: v2
dependencies:
- name: backend
alias: main-backend
- name: backend
alias: secondary-backend
Указание версии подключаемого чарта
Ограничить подходящие версии зависимого чарта, из которых будет выбрана наиболее свежая, можно директивой dependencies[].version
родительского чарта, например:
# .helm/Chart.yaml:
apiVersion: v2
dependencies:
- name: backend
version: "~1.2.3"
Результат: будет использована самая свежая версия 1.2.x, но как минимум 1.2.3.
Указание источника подключаемого чарта
Указать путь к источнику чартов, в котором можно найти указанный зависимый чарт, можно директивой dependencies[].repository
родительского чарта, например:
# .helm/Chart.yaml:
apiVersion: v2
dependencies:
- name: mychart
repository: oci://example.org/myrepo
Результат: будет использован чарт mychart
из OCI-репозитория example.org/myrepo
.
Включение/отключение зависимых чартов
По умолчанию все зависимые чарты включены. Для произвольного включения/отключения зависимых чартов можно использовать директиву dependencies[].condition
родительского чарта, например:
# .helm/Chart.yaml:
apiVersion: v2
dependencies:
- name: backend
condition: backend.enabled
Результат: зависимый чарт backend
будет включен, только если параметр $.Values.backend.enabled
имеет значение true
(по умолчанию — true
).
Также можно использовать директиву dependencies[].tags
родительского чарта для включения/отключения целых групп зависимых чартов сразу, например:
# .helm/Chart.yaml:
dependencies:
- name: backend
tags: ["app"]
- name: frontend
tags: ["app"]
Результат: зависимые чарты backend
и frontend
будут включены, только если параметр $.Values.tags.app
имеет значение true
(по умолчанию — true
).
Шаблоны
Шаблонизация
Механизм шаблонизации в Deckhouse Delivery ничем не отличается от Helm. Используется движок шаблонов Go text/template, расширенный готовым набором функций Sprig и Helm.
Файлы шаблонов
В директории templates
чарта находятся файлы шаблонов.
Файлы шаблонов templates/*.yaml
формируют конечные Kubernetes-манифесты для развертывания. Каждый из этих файлов может формировать сразу несколько манифестов Kubernetes-ресурсов. Для этого манифесты должны быть разделены строкой ---
.
Файлы шаблонов templates/_*.tpl
содержат только именованные шаблоны для использования в других файлах. Файлы *.tpl
не формируют Kubernetes-манифесты сами по себе.
Действия
Главный элемент шаблонизации — действие. Действие может возвращать только строки. Действие заключается в двойные фигурные скобки:
{{ print "hello" }}
Результат:
hello
Переменные
Переменные используются для хранения или указания на данные любого типа.
Объявление и присваивание переменной:
{{ $myvar := "hello" }}
Присваивание нового значения существующей переменной:
{{ $myvar = "helloworld" }}
Использование переменной:
{{ $myvar }}
Результат:
helloworld
Использование предопределенных переменных:
{{ $.Values.werf.env }}
Данные можно подставлять и без объявления переменных:
labels:
app: {{ "myapp" }}
Результат:
labels:
app: myapp
Также в переменные можно сохранять результат выполнения функций или конвейеров:
{{ $myvar := 1 | add 1 1 }}
{{ $myvar }}
Результат:
3
Области видимости переменных
Область видимости ограничивает видимость переменных. По умолчанию область видимости переменных ограничена файлом-шаблоном.
Область видимости может меняться при использовании некоторых блоков и функций. К примеру, блок if
создаёт новую область видимости, а переменные, объявленные в блоке if
, будут недоступны снаружи:
{{ if true }}
{{ $myvar := "hello" }}
{{ end }}
{{ $myvar }}
Результат:
Error: ... undefined variable "$myvar"
Чтобы обойти это ограничение, объявите переменную за пределами блока, а значение присвойте ей внутри блока:
{{ $myvar := "" }}
{{ if true }}
{{ $myvar = "hello" }}
{{ end }}
{{ $myvar }}
Результат:
hello
Типы данных
Доступные типы данных:
Тип данных | Пример |
---|---|
Логический | {{ true }} |
Строка | {{ "hello" }} |
Целое число | {{ 1 }} |
Число с плавающей точкой | {{ 1.1 }} |
Список с элементами любого типа, упорядоченный | {{ list 1 2 3 }} |
Словарь с ключами-строками и значениями любого типа, неупорядоченный | {{ dict "key1" 1 "key2" 2 }} |
Специальные объекты | {{ $.Files }} |
Нуль | {{ nil }} |
Функции
В Deckhouse Delivery встроена обширная библиотека функций для использования в шаблонах. Основная их часть — функции Helm.
Функции можно использовать только в действиях. Функции могут иметь аргументы и могут возвращать данные любого типа. Например, приведенная ниже функция сложения принимает три аргумента-числа и возвращает число:
{{ add 3 2 1 }}
Результат:
6
Обратите внимание, что результат выполнения действия всегда конвертируется в строку независимо от возвращаемого функцией типа данных.
Аргументами функций могут быть:
-
простые значения:
1
; -
вызовы других функций:
add 1 1
; -
конвейеры:
1 | add 1
; -
комбинации вышеперечисленных типов:
1 | add (add 1 1)
.
Если аргумент — не простое значение, а вызов другой функции или конвейер, заключите его в круглые скобки ()
:
{{ add 3 (add 1 1) (1 | add 1) }}
Чтобы игнорировать возвращаемый функцией результат, просто сохраните его в переменную $_
:
{{ $_ := set $myDict "mykey" "myvalue"}}
Конвейеры
Конвейеры позволяют передать результат выполнения первой функции как последний аргумент во вторую функцию, а результат второй функции — как последний аргумент в третью и так далее:
{{ now | unixEpoch | quote }}
Здесь результат выполнения функции now
(получить текущую дату) передаётся как аргумент в функцию unixEpoch
(преобразует дату в Unix time), после чего полученное значение передаётся в функцию quote
(оборачивает в кавычки).
Итоговый результат:
"1671466310"
Использование конвейеров не обязательно, и при желании их можно переписать следующим образом:
{{ quote (unixEpoch (now)) }}
… однако рекомендуется использовать именно конвейеры.
Логические операции и сравнения
Логические операции реализуются следующими функциями:
Операция | Функция | Пример |
---|---|---|
НЕ | not <arg> |
{{ not false }} |
И | and <arg> <arg> [<arg>, ...] |
{{ and true true }} |
ИЛИ | or <arg> <arg> [<arg>, ...] |
{{ or false true }} |
Сравнения реализуются следующими функциями:
Сравнение | Функция | Пример |
---|---|---|
Эквивалентно | eq <arg> <arg> [<arg>, ...] |
{{ eq "hello" "hello" }} |
Не эквивалентно | neq <arg> <arg> [<arg>, ...] |
{{ neq "hello" "world" }} |
Меньше | lt <arg> <arg> |
{{ lt 1 2 }} |
Больше | gt <arg> <arg> |
{{ gt 2 1 }} |
Меньше или эквивалентно | le <arg> <arg> |
{{ le 1 2 }} |
Больше или эквивалентно | ge <arg> <arg> |
{{ ge 2 1 }} |
Пример комбинирования:
{{ and (eq true true) (neq true false) (not (empty "hello")) }}
Ветвления
Ветвления if/else
позволяют выполнять шаблонизацию только при выполнении/невыполнении определенных условий. Пример:
{{ if $.Values.app.enabled }}
# ...
{{ end }}
Условие считается невыполненным, если результатом его вычисления является:
-
логическое
false
; -
число
0
; -
пустая строка
""
; -
пустой список
[]
; -
пустой словарь
{}
; -
нуль:
nil
.
В остальных случаях условие считается выполненным. Условием могут быть данные, переменная, функция или конвейер.
Полный пример:
{{ if eq $appName "backend" }}
app: mybackend
{{ else if eq $appName "frontend" }}
app: myfrontend
{{ else }}
app: {{ $appName }}
{{ end }}
Простые ветвления можно реализовывать не только с if/else
, но и с функцией ternary
. Например, следующее выражение с ternary
:
{{ ternary "mybackend" $appName (eq $appName "backend") }}
… аналогично приведенной ниже конструкции if/else
:
{{ if eq $appName "backend" }}
app: mybackend
{{ else }}
app: {{ $appName }}
{{ end }}
Циклы
Циклы по спискам
Циклы range
позволяют перебирать элементы списка и выполнять нужную шаблонизацию на каждой итерации:
{{ range $urls }}
{{ . }}
{{ end }}
Результат:
https://example.org
https://sub.example.org
Относительный контекст .
всегда указывает на элемент списка, соответствующий текущей итерации, хотя указатель можно сохранить и в произвольную переменную:
{{ range $elem := $urls }}
{{ $elem }}
{{ end }}
Результат будет таким же:
https://example.org
https://sub.example.org
Получить индекс элемента в списке можно следующим образом:
{{ range $i, $elem := $urls }}
{{ $elem }} имеет индекс {{ $i }}
{{ end }}
Результат:
https://example.org имеет индекс 0
https://sub.example.org имеет индекс 1
Циклы по словарям
Циклы range
позволяют перебирать ключи и значения словарей и выполнять нужную шаблонизацию на каждой итерации:
# values.yaml:
apps:
backend:
image: openjdk
frontend:
image: node
# templates/app.yaml:
{{ range $.Values.apps }}
{{ .image }}
{{ end }}
Результат:
openjdk
node
Относительный контекст .
всегда указывает на значение элемента словаря, соответствующего текущей итерации, при этом указатель можно сохранить и в произвольную переменную:
{{ range $app := $.Values.apps }}
{{ $app.image }}
{{ end }}
Результат будет таким же:
openjdk
node
Получить ключ элемента словаря можно так:
{{ range $appName, $app := $.Values.apps }}
{{ $appName }}: {{ $app.image }}
{{ end }}
Результат:
backend: openjdk
frontend: node
Контроль выполнения цикла
Специальное действие continue
позволяет пропустить текущую итерацию цикла. В качестве примера пропустим итерацию для элемента https://example.org
:
{{ range $url := $urls }}
{{ if eq $url "https://example.org" }}{{ continue }}{{ end }}
{{ $url }}
{{ end }}
Специальное действие break
позволяет не только пропустить текущую итерацию, но и прервать весь цикл:
{{ range $url := $urls }}
{{ if eq $url "https://example.org" }}{{ break }}{{ end }}
{{ $url }}
{{ end }}
Контекст
Корневой контекст ($)
Корневой контекст — словарь, на который ссылается переменная $
. Через него доступны values и некоторые специальные объекты. Корневой контекст имеет глобальную видимость в пределах файла-шаблона (исключение — блок define
и некоторые функции).
Пример использования:
{{ $.Values.mykey }}
Результат:
myvalue
К корневому контексту можно добавлять произвольные ключи/значения, которые также станут доступны из любого места файла-шаблона:
{{ $_ := set $ "mykey" "myvalue"}}
{{ $.mykey }}
Результат:
myvalue
Корневой контекст остаётся неизменным даже в блоках, изменяющих относительный контекст (исключение — define
):
{{ with $.Values.backend }}
- command: {{ .command }}
image: {{ $.Values.werf.image.backend }}
{{ end }}
Некоторые функции вроде tpl
или include
могут терять корневой контекст. Для сохранения доступа к корневому контексту многим из них можно передать корневой контекст аргументом:
{{ tpl "{{ .Values.mykey }}" $ }}
Результат:
myvalue
Относительный контекст (.)
Относительный контекст — данные любого типа, на которые ссылается переменная .
. По умолчанию относительный контекст указывает на корневой контекст.
Некоторые блоки и функции могут менять относительный контекст. В примере ниже в первой строке относительный контекст указывает на корневой контекст $
, а во второй строке — уже на $.Values.containers
:
{{ range .Values.containers }}
{{ . }}
{{ end }}
Для смены относительного контекста можно использовать блок with
:
{{ with $.Values.app }}
image: {{ .image }}
{{ end }}
Переиспользование шаблонов
Именованные шаблоны
Для переиспользования шаблонизации объявите именованные шаблоны в блоках define
в файлах templates/_*.tpl
:
# templates/_helpers.tpl:
{{ define "labels" }}
app: myapp
team: alpha
{{ end }}
Далее подставляйте именованные шаблоны в файлы templates/*.(yaml|tpl)
функцией include
:
# templates/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
selector:
matchLabels: {{ include "labels" nil | nindent 6 }}
template:
metadata:
labels: {{ include "labels" nil | nindent 8 }}
Результат:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
selector:
matchLabels:
app: myapp
team: alpha
template:
metadata:
labels:
app: myapp
team: alpha
Имя именованного шаблона для функции include
может быть динамическим:
{{ include (printf "%s.labels" $prefix) nil }}
Именованные шаблоны обладают глобальной видимостью — единожды объявленный в родительском или любом дочернем чарте именованный шаблон становится доступен сразу во всех чартах — и в родительском, и в дочерних. Убедитесь, что в подключенных родительском и дочерних чартах нет именованных шаблонов с одинаковыми именами.
Параметризация именованных шаблонов
Функция include
, подставляющая именованные шаблоны, принимает один произвольный аргумент. Этот аргумент можно использовать для параметризации именованного шаблона, где этот аргумент станет относительным контекстом .
:
{{ include "labels" "myapp" }}
{{ define "labels" }}
app: {{ . }}
{{ end }}
Результат:
app: myapp
Для передачи сразу нескольких аргументов используйте список с несколькими аргументами:
{{ include "labels" (list "myapp" "alpha") }}
{{ define "labels" }}
app: {{ index . 0 }}
team: {{ index . 1 }}
{{ end }}
… или словарь:
{{ include "labels" (dict "app" "myapp" "team" "alpha") }}
{{ define "labels" }}
app: {{ .app }}
team: {{ .team }}
{{ end }}
Необязательные позиционные аргументы можно реализовать так:
{{ include "labels" (list "myapp") }}
{{ include "labels" (list "myapp" "alpha") }}
{{ define "labels" }}
app: {{ index . 0 }}
{{ if gt (len .) 1 }}
team: {{ index . 1 }}
{{ end }}
{{ end }}
А необязательные непозиционные аргументы — так:
{{ include "labels" (dict "app" "myapp") }}
{{ include "labels" (dict "team" "alpha" "app" "myapp") }}
{{ define "labels" }}
app: {{ .app }}
{{ if hasKey . "team" }}
team: {{ .team }}
{{ end }}
{{ end }}
Именованному шаблону, не требующему параметризации, просто передайте nil
:
{{ include "labels" nil }}
Результат выполнения include
Функция include
, подставляющая именованный шаблон, всегда возвращает только текст. Для возврата структурированных данных нужно десериализовать результат выполнения include
с помощью функции fromYaml
:
{{ define "commonLabels" }}
app: myapp
{{ end }}
{{ $labels := include "commonLabels" nil | fromYaml }}
{{ $labels.app }}
Результат:
myapp
Обратите внимание, что
fromYaml
не работает для списков. Специально для них (и только для них) предназначена функцияfromYamlArray
.
Для явной сериализации данных можно воспользоваться функциями toYaml
и toJson
, для десериализации — функциями fromYaml/fromYamlArray
и fromJson/fromJsonArray
.
Контекст именованных шаблонов
Объявленные в templates/_*.tpl
именованные шаблоны теряют доступ к корневому и относительному контекстам файла, в который они включаются функцией include
. Исправить это можно, передав корневой и/или относительный контекст в виде аргументов include
:
{{ include "labels" $ }}
{{ include "labels" . }}
{{ include "labels" (list $ .) }}
{{ include "labels" (list $ . "myapp") }}
include в include
В блоках define
тоже можно использовать функцию include
для включения именованных шаблонов:
{{ define "doSomething" }}
{{ include "doSomethingElse" . }}
{{ end }}
Через include
можно вызвать даже тот именованный шаблон, из которого и происходит вызов, т. е. вызвать его рекурсивно:
{{ define "doRecursively" }}
{{ if ... }}
{{ include "doRecursively" . }}
{{ end }}
{{ end }}
Шаблонизация с tpl
Функция tpl
позволяет выполнить шаблонизацию любой строки и тут же получить результат. Она принимает один аргумент, который должен быть корневым контекстом.
Пример шаблонизации values:
# values.yaml:
appName: "myapp"
deploymentName: "{{ .Values.appName }}-deployment"
# templates/app.yaml:
{{ tpl $.Values.deploymentName $ }}
Результат:
myapp-deployment
Пример шаблонизации произвольных файлов, которые сами по себе не поддерживают Helm-шаблонизацию:
{{ tpl ($.Files.Get "nginx.conf") $ }}
Для передачи дополнительных аргументов в функцию tpl
можно добавить аргументы как новые ключи корневого контекста:
{{ $_ := set $ "myarg" "myvalue"}}
{{ tpl "{{ $.myarg }}" $ }}
Контроль отступов
Используйте функцию nindent
для выставления отступов:
containers: {{ .Values.app.containers | nindent 6 }}
Результат:
containers:
- name: backend
image: openjdk
Пример комбинации с другими данными:
containers:
{{ .Values.app.containers | nindent 6 }}
- name: frontend
image: node
Результат:
containers:
- name: backend
image: openjdk
- name: frontend
image: node
Используйте -
после {{
и/или до }}
для удаления лишних пробелов до и/или после результата выполнения действия, например:
{{- "hello" -}} {{ "world" }}
Результат:
helloworld
Комментарии
Поддерживаются два типа комментариев — комментарии шаблонизации {{ /* */ }}
и комментарии манифестов #
.
Комментарии шаблонизации
Комментарии шаблонизации скрываются при формировании манифестов:
{{ /* Этот комментарий пропадёт */ }}
app: myApp
Комментарии могут быть многострочными:
{{ /*
Hello
World
/* }}
Шаблоны в них игнорируются:
{{ /*
{{ print "Эта шаблонизация игнорируется" }}
/* }}
Комментарии манифестов
Комментарии манифестов сохраняются при формировании манифестов:
# Этот комментарий сохранится
app: myApp
Комментарии могут быть только однострочнными:
# Для многострочных комментариев используйте
# несколько однострочных комментариев подряд
Шаблоны в них выполняются:
# {{ print "Эта шаблонизация выполняется" }}
Отладка
Используйте d8 d render
, чтобы полностью сформировать и отобразить конечные Kubernetes-манифесты. Укажите опцию --debug
, чтобы увидеть манифесты, даже если они не являются корректным YAML.
Отобразить содержимое переменной:
output: {{ $appName | toYaml }}
Отобразить содержимое переменной-списка или словаря:
output: {{ $dictOrList | toYaml | nindent 2 }}
Отобразить тип данных у переменной:
output: {{ kindOf $myvar }}
Отобразить произвольную строку, остановив дальнейшее формирование шаблонов:
{{ fail (printf "Тип данных: %s" (kindOf $myvar)) }}
Параметризация шаблонов
Основы параметризации
Содержимое словаря $.Values
можно использовать для параметризации шаблонов. Каждый чарт имеет свой словарь $.Values
. Словарь формируется слиянием параметров, полученных из файлов параметров, опций командной строки и других источников.
Простой пример параметризации через values.yaml
:
# values.yaml:
myparam: myvalue
# templates/example.yaml:
{{ $.Values.myparam }}
Результат:
myvalue
Более сложный пример:
# values.yaml:
myparams:
- value: original
# templates/example.yaml:
{{ (index $.Values.myparams 0).value }}
d8 d render --set myparams[0].value=overriden
Результат:
overriden
Источники параметров и их приоритет
Словарь $.Values
формируется объединением параметров из источников параметров в указанном порядке:
values.yaml
текущего чарта.secret-values.yaml
текущего чарта (только в Deckhouse Delivery).- Словарь в
values.yaml
родительского чарта, у которого ключ — алиас или имя текущего чарта. - Словарь в
secret-values.yaml
родительского чарта (только в Deckhouse Delivery), у которого ключ — алиас или имя текущего чарта. - Файлы параметров из переменной
WERF_VALUES_*
. - Файлы параметров из опции
--values
. - Файлы секретных параметров из переменной
WERF_SECRET_VALUES_*
. - Файлы секретных параметров из опции
--secret-values
. - Параметры в set-файлах из переменной
WERF_SET_FILE_*
. - Параметры в set-файлах из опции
--set-file
. - Параметры из переменной
WERF_SET_STRING_*
. - Параметры из опции
--set-string
. - Параметры из переменной
WERF_SET_*
. - Параметры из опции
--set
. - Служебные параметры Deckhouse Delivery.
- Параметры из директивы
export-values
родительского чарта (только в Deckhouse Delivery). - Параметры из директивы
import-values
дочерних чартов.
Правила объединения параметров:
-
простые типы данных перезаписываются;
-
списки перезаписываются;
-
словари объединяются;
-
при конфликтах параметры из источников выше по списку перезаписываются параметрами из источников ниже по списку.
Параметризация чарта
Чарт можно параметризовать через его файл параметров:
# values.yaml:
myparam: myvalue
# templates/example.yaml:
{{ $.Values.myparam }}
Результат:
myvalue
Также добавить/переопределить параметры чарта можно и аргументами командной строки:
d8 d render --set myparam=overriden # или WERF_SET_MYPARAM=myparam=overriden d8 d render
d8 d render --set-string myparam=overriden # или WERF_SET_STRING_MYPARAM=myparam=overriden d8 d render
… или дополнительными файлами параметров:
# .helm/values-production.yaml:
myparam: overriden
d8 d render --values .helm/values-production.yaml # или WERF_VALUES_PROD=.helm/values-production.yaml d8 d render
… или файлом секретных параметров основного чарта (только в Deckhouse Delivery):
# .helm/secret-values.yaml:
myparam: <encrypted>
d8 d render
… или дополнительными файлами секретных параметров основного чарта (только в Deckhouse Delivery):
# .helm/secret-values-production.yaml:
myparam: <encrypted>
d8 d render --secret-values .helm/secret-values-production.yaml # или WERF_SECRET_VALUES_PROD=.helm/secret-values-production.yaml d8 d render
… или set-файлами:
# myparam.txt:
overriden
d8 d render --set-file myparam=myparam.txt # или WERF_SET_FILE_PROD=myparam=myparam.txt d8 d render
Результат везде тот же:
overriden
Параметризация зависимых чартов
Зависимый чарт можно параметризовать как через его собственный файл параметров, так и через файл параметров родительского чарта.
К примеру, здесь параметры из словаря mychild
в файле values.yaml
чарта myparent
перезаписывают параметры в файле values.yaml
чарта mychild
:
# Chart.yaml:
name: myparent
dependencies:
- name: mychild
# values.yaml:
mychild:
myparam: overriden
# charts/mychild/values.yaml:
myparam: original
# charts/mychild/templates/example.yaml:
{{ $.Values.myparam }}
Результат:
overriden
Обратите внимание, что словарь, находящийся в values.yaml
родительского чарта и содержащий параметры для зависимого чарта, должен иметь в качестве имени alias
(если есть) или name
зависимого чарта.
Также добавить/переопределить параметры зависимого чарта можно и аргументами командной строки:
d8 d render --set mychild.myparam=overriden # или WERF_SET_MYPARAM=mychild.myparam=overriden d8 d render
d8 d render --set-string mychild.myparam=overriden # или WERF_SET_STRING_MYPARAM=mychild.myparam=overriden d8 d render
… или дополнительными файлами параметров:
# .helm/values-production.yaml:
mychild:
myparam: overriden
d8 d render --values .helm/values-production.yaml # или WERF_VALUES_PROD=.helm/values-production.yaml d8 d render
… или файлом секретных параметров основного чарта (только в Deckhouse Delivery):
# .helm/secret-values.yaml:
mychild:
myparam: <encrypted>
d8 d render
… или дополнительными файлами секретных параметров основного чарта (только в Deckhouse Delivery):
# .helm/secret-values-production.yaml:
mychild:
myparam: <encrypted>
d8 d render --secret-values .helm/secret-values-production.yaml # или WERF_SECRET_VALUES_PROD=.helm/secret-values-production.yaml d8 d render
… или set-файлами:
# mychild-myparam.txt:
overriden
d8 d render --set-file mychild.myparam=mychild-myparam.txt # или WERF_SET_FILE_PROD=mychild.myparam=mychild-myparam.txt d8 d render
… или директивой export-values
(только в Deckhouse Delivery):
# Chart.yaml:
name: myparent
dependencies:
- name: mychild
export-values:
- parent: myparam
child: myparam
# values.yaml:
myparam: overriden
d8 d render
Результат везде тот же:
overriden
Использование параметров зависимого чарта в родительском
Для передачи параметров зависимого чарта в родительский можно использовать директиву import-values
в родительском чарте:
# Chart.yaml:
name: myparent
dependencies:
- name: mychild
import-values:
- child: myparam
parent: myparam
# values.yaml:
myparam: original
# charts/mychild/values.yaml:
myparam: overriden
# templates/example.yaml:
{{ $.Values.myparam }}
Результат:
overriden
Глобальные параметры
Параметры чарта доступны только в этом же чарте (и ограниченно доступны в зависимых от него). Один из простых способов получить доступ к параметрам одного чарта в других подключенных чартах — использование глобальных параметров.
Глобальный параметр имеет глобальную область видимости — параметр, объявленный в родительском, дочернем или другом подключенном чарте становится доступен во всех подключенных чартах по одному и тому же пути:
# Chart.yaml:
name: myparent
dependencies:
- name: mychild1
- name: mychild2
# charts/mychild1/values.yaml:
global:
myparam: myvalue
# templates/example.yaml:
myparent: {{ $.Values.global.myparam }}
# charts/mychild1/templates/example.yaml:
mychild1: {{ $.Values.global.myparam }}
# charts/mychild2/templates/example.yaml:
mychild2: {{ $.Values.global.myparam }}
Результат:
myparent: myvalue
---
mychild1: myvalue
---
mychild2: myvalue
Секретные параметры (только в d8 d)
Для хранения секретных параметров можно использовать файлы секретных параметров, хранящиеся в зашифрованном виде в Git-репозитории.
По умолчанию Deckhouse Delivery пытается найти файл .helm/secret-values.yaml
, содержащий зашифрованные параметры, и при нахождении файла расшифровывает его и объединяет расшифрованные параметры с остальными:
# .helm/values.yaml:
plainParam: plainValue
# .helm/secret-values.yaml:
secretParam: 1000625c4f1d874f0ab853bf1db4e438ad6f054526e5dcf4fc8c10e551174904e6d0
{{ $.Values.plainParam }}
{{ $.Values.secretParam }}
Результат:
plainValue
secretValue
Работа с файлами секретных параметров
Порядок работы с файлами секретных параметров:
-
Возьмите существующий секретный ключ или создайте новый командой
d8 d helm secret generate-secret-key
. -
Сохраните секретный ключ в переменную окружения
WERF_SECRET_KEY
, либо в файлы<корень Git-репозитория>/.werf_secret_key
или<домашняя директория>/.werf/global_secret_key
. -
Командой
d8 d helm secret values edit .helm/secret-values.yaml
откройте файл секретных параметров и добавьте/измените в нём расшифрованные параметры. -
Сохраните файл — файл зашифруется и сохранится в зашифрованном виде.
-
Закоммитите в Git добавленный/изменённый файл
.helm/secret-values.yaml
; -
При дальнейших вызовах Deckhouse Delivery секретный ключ должен быть установлен в вышеупомянутых переменной окружения или файлах, иначе файл секретных параметров не сможет быть расшифрован.
Имеющий доступ к секретному ключу может расшифровать содержимое файла секретных параметров, поэтому держите секретный ключ в безопасном месте!
При использовании файла <корень Git-репозитория>/.werf_secret_key
обязательно добавьте его в .gitignore
, чтобы случайно не сохранить его в Git-репозитории.
Многие команды Deckhouse Delivery можно запускать и без указания секретного ключа благодаря опции --ignore-secret-key
, но в таком случае параметры будут доступны для использования не в расшифрованной форме, а в зашифрованной.
Дополнительные файлы секретных параметров
В дополнение к файлу .helm/secret-values.yaml
можно создавать и использовать дополнительные секретные файлы:
# .helm/secret-values-production.yaml:
secret: 1000625c4f1d874f0ab853bf1db4e438ad6f054526e5dcf4fc8c10e551174904e6d0
d8 d --secret-values .helm/secret-values-production.yaml
Информация о собранных образах (только в Deckhouse Delivery)
Deckhouse Delivery хранит информацию о собранных образах в параметрах $.Values.werf
основного чарта:
werf:
image:
# Полный путь к собранному Docker-образу для Deckhouse Delivery-образа "backend":
backend: example.org/apps/myapp:a243949601ddc3d4133c4d5269ba23ed58cb8b18bf2b64047f35abd2-1598024377816
# Адрес container registry для собранных образов:
repo: example.org/apps/myapp
tag:
# Тег собранного Docker-образа для Deckhouse Delivery-образа "backend":
backend: a243949601ddc3d4133c4d5269ba23ed58cb8b18bf2b64047f35abd2-1598024377816
Пример использования:
image: {{ $.Values.werf.image.backend }}
Результат:
image: example.org/apps/myapp:a243949601ddc3d4133c4d5269ba23ed58cb8b18bf2b64047f35abd2-1598024377816
Для использования $.Values.werf
в зависимых чартах воспользуйтесь директивой export-values
(только в Deckhouse Delivery):
# .helm/Chart.yaml:
dependencies:
- name: backend
export-values:
- parent: werf
child: werf
# .helm/charts/backend/templates/example.yaml:
image: {{ $.Values.werf.image.backend }}
Результат:
image: example.org/apps/myapp:a243949601ddc3d4133c4d5269ba23ed58cb8b18bf2b64047f35abd2-1598024377816
Информация о релизе
Deckhouse Delivery хранит информацию о релизе в свойствах объекта $.Release
:
# Устанавливается ли релиз в первый раз:
IsInstall: true
# Обновляется ли уже существующий релиз:
IsUpgrade: false
# Имя релиза:
Name: myapp-production
# Имя Kubernetes Namespace:
Namespace: myapp-production
# Номер ревизии релиза:
Revision: 1
… и в параметрах $.Values.werf
основного чарта (только в Deckhouse Delivery):
werf:
# Имя Deckhouse Delivery-проекта:
name: myapp
# Окружение:
env: production
Пример использования:
{{ $.Release.Namespace }}
{{ $.Values.werf.env }}
Результат:
myapp-production
production
Для использования $.Values.werf
в зависимых чартах воспользуйтесь директивой export-values
(только в Deckhouse Delivery):
# .helm/Chart.yaml:
dependencies:
- name: backend
export-values:
- parent: werf
child: werf
# .helm/charts/backend/templates/example.yaml:
{{ $.Values.werf.env }}
Результат:
production
Информация о чарте
Deckhouse Delivery хранит информацию о текущем чарте в объекте $.Chart
:
# Является ли чарт основным:
IsRoot: true
# Содержимое Chart.yaml:
Name: mychart
Version: 1.0.0
Type: library
KubeVersion: "~1.20.3"
AppVersion: "1.0"
Deprecated: false
Icon: https://example.org/mychart-icon.svg
Description: This is My Chart
Home: https://example.org
Sources:
- https://github.com/my/chart
Keywords:
- apps
Annotations:
anyAdditionalInfo: here
Dependencies:
- Name: redis
Condition: redis.enabled
Пример использования:
{{ $.Chart.Name }}
Результат:
mychart
Информация о шаблоне
Deckhouse Delivery хранит информацию о текущем шаблоне в свойствах объекта $.Template
:
# Относительный путь к директории templates чарта:
BasePath: mychart/templates
# Относительный путь к текущему файлу шаблона:
Name: mychart/templates/example.yaml
Пример использования:
{{ $.Template.Name }}
Результат:
mychart/templates/example.yaml
Информация о Git-коммите (только в Deckhouse Delivery)
Deckhouse Delivery хранит информацию о Git-коммите, на котором он был запущен, в параметрах $.Values.werf.commit
основного чарта:
werf:
commit:
date:
# Дата Git-коммита, на котором был запущен Deckhouse Delivery (человекочитаемая форма):
human: 2022-01-21 18:51:39 +0300 +0300
# Дата Git-коммита, на котором был запущен Deckhouse Delivery (Unix time):
unix: 1642780299
# Хэш Git-коммита, на котором был запущен Deckhouse Delivery:
hash: 1b28e6843a963c5bdb3579f6fc93317cc028051c
Пример использования:
{{ $.Values.werf.commit.hash }}
Результат:
1b28e6843a963c5bdb3579f6fc93317cc028051c
Для использования $.Values.werf.commit
в зависимых чартах воспользуйтесь директивой export-values
(только в Deckhouse Delivery):
# .helm/Chart.yaml:
dependencies:
- name: backend
export-values:
- parent: werf
child: werf
# .helm/charts/backend/templates/example.yaml:
{{ $.Values.werf.commit.hash }}
Результат:
1b28e6843a963c5bdb3579f6fc93317cc028051c
Информация о возможностях кластера Kubernetes
Deckhouse Delivery предоставляет информацию о возможностях кластера Kubernetes, в который Deckhouse Delivery стал бы применять Kubernetes-манифесты, через свойства объекта $.Capabilities
:
KubeVersion:
# Полная версия кластера Kubernetes:
Version: v1.20.0
# Мажорная версия кластера Kubernetes:
Major: "1"
# Минорная версия кластера Kubernetes:
Minor: "20"
# API, поддерживаемые кластером Kubernetes:
APIVersions:
- apps/v1
- batch/v1
- # ...
… и методы объекта $.Capabilities
:
APIVersions.Has <arg>
— поддерживается ли кластером Kubernetes указанное аргументом API (например,apps/v1
) или ресурс (например,apps/v1/Deployment
).
Пример использования:
{{ $.Capabilities.KubeVersion.Version }}
{{ $.Capabilities.APIVersions.Has "apps/v1" }}
Результат:
v1.20.0
true
Разные окружения
Параметризация шаблонов в зависимости от окружения (только в Deckhouse Delivery)
Окружение Deckhouse Delivery указывается опцией --env
($WERF_ENV
), либо автоматически выставляется командой d8 d ci-env
. Текущее окружение доступно в параметре $.Values.werf.env
основного чарта.
Окружение Deckhouse Delivery используется при формировании имени релиза и имени Namespace’а, а также может использоваться для параметризации шаблонов:
# .helm/values.yaml:
memory:
staging: 1G
production: 2G
# .helm/templates/example.yaml:
memory: {{ index $.Values.memory $.Values.werf.env }}
d8 d render --env production
Результат:
memory: 2G
Для использования $.Values.werf.env
в зависимых чартах воспользуйтесь директивой export-values
(только в Deckhouse Delivery):
# .helm/Chart.yaml:
dependencies:
- name: child
export-values:
- parent: werf
child: werf
# .helm/charts/child/templates/example.yaml:
{{ $.Values.werf.env }}
Результат:
production
Развертывание в разные Kubernetes Namespace
Имя Kubernetes Namespace для развертываемых ресурсов формируется автоматически (только в Deckhouse Delivery) по специальному шаблону [[ project ]]-[[ env ]]
, где [[ project ]]
— имя проекта Deckhouse Delivery, а [[ env ]]
— имя окружения.
Достаточно изменить окружение Deckhouse Delivery и вместе с ним изменится и Namespace:
# werf.yaml:
project: myapp
d8 d converge --env staging
d8 d converge --env production
Результат: один экземпляр приложения развёрнут в Namespace myapp-staging
, а второй — в myapp-production
.
Обратите внимание, что если в манифесте Kubernetes-ресурса явно указан Namespace, то для этого ресурса будет использован именно указанный в нём Namespace.
Изменение шаблона имени Namespace (только в Deckhouse Delivery)
Если вас не устраивает специальный шаблон, из которого формируется имя Namespace, вы можете его изменить:
# werf.yaml:
project: myapp
deploy:
namespace: "backend-[[ env ]]"
d8 d converge --env production
Результат: приложение развёрнуто в Namespace backend-production
.
Прямое указание имени Namespace
Вместо формирования имени Namespace по специальному шаблону можно указывать Namespace явно для каждой команды (рекомендуется также изменять и имя релиза):
d8 d converge --namespace backend-production --release backend-production
Результат: приложение развёрнуто в Namespace backend-production
.
Форматирование имени Namespace
Namespace, сформированный по специальному шаблону или указанный опцией --namespace
, приводится к формату RFC 1123 Label Names автоматически. Отключить автоматическое форматирование можно директивой deploy.namespaceSlug
файла werf.yaml
.
Вручную отформатировать любую строку согласно формату RFC 1123 Label Names можно командой d8 d slugify -f kubernetes-namespace
.
Развертывание в разные кластеры Kubernetes
По умолчанию Deckhouse Delivery развертывает Kubernetes-ресурсы в кластер, на который настроена команда d8 k
. Для развертывания в разные кластеры можно использовать разные kube-контексты единого kube-config файла (по умолчанию — $HOME/.kube/config
):
d8 d converge --kube-context staging # или $WERF_KUBE_CONTEXT=...
d8 d converge --kube-context production
… или использовать разные kube-config файлы:
d8 d converge --kube-config "$HOME/.kube/staging.config" # или $WERF_KUBE_CONFIG=...
d8 d converge --kube-config-base64 "$KUBE_PRODUCTION_CONFIG_IN_BASE64" # или $WERF_KUBE_CONFIG_BASE64=...
Развертывание из-под разных пользователей Kubernetes
По умолчанию Deckhouse Delivery для развертывания использует пользователя Kubernetes, через которого работает команда d8 k
. Для развертывания из-под разных пользователей используйте разные kube-контексты:
d8 d converge --kube-context admin # или $WERF_KUBE_CONTEXT=...
d8 d converge --kube-context regular-user
Порядок развертывания
Стадии развертывания
Развертывание Kubernetes-ресурсов происходит в следующей последовательности:
-
Развертывание
CustomResourceDefinitions
из директорийcrds
подключенных чартов. -
Развертывание хуков
pre-install
,pre-upgrade
илиpre-rollback
по одному хуку за раз, от хуков с меньшим весом к большему. Если хук имеет зависимость от внешнего ресурса, то он развернётся только после его готовности. -
Развертывание основных ресурсов: объединение ресурсов с одинаковым весом в группы (ресурсы без указанного веса имеют вес 0) и развертывание по одной группе за раз, от групп с ресурсами меньшего веса к группам с ресурсами большего веса. Если ресурс в группе имеет зависимость от внешнего ресурса, то она начнёт развертывание только после его готовности.
-
Развертывание хуков
post-install
,post-upgrade
илиpost-rollback
по одному хуку за раз, от хуков с меньшим весом к большему. Если хук имеет зависимость от внешнего ресурса, то он развернётся только после его готовности.
Развертывание CustomResourceDefinitions
Для развертывания CustomResourceDefinitions поместите CRD-манифесты в нешаблонизируемые файлы crds/*.yaml
в любом из подключенных чартов. При следующем развертывании эти CRD будут развернуты первыми, а хуки и основные ресурсы будут развернуты только после них.
Пример:
# .helm/crds/crontab.yaml:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
# ...
spec:
names:
kind: CronTab
# .helm/templates/crontab.yaml:
apiVersion: example.org/v1
kind: CronTab
# ...
d8 d converge
Результат: сначала развернут CRD для CronTab-ресурса, а затем развернут сам CronTab-ресурс.
Изменение порядка развертывания ресурсов (только в Deckhouse Delivery)
По умолчанию Deckhouse Delivery объединяет все основные ресурсы (основные — не являющиеся хуками или CRDs из crds/*.yaml
) в одну группу, создаёт ресурсы этой группы, а затем отслеживает их готовность.
Создание ресурсов группы происходит в следующем порядке:
- Namespace;
- NetworkPolicy;
- ResourceQuota;
- LimitRange;
- PodSecurityPolicy;
- PodDisruptionBudget;
- ServiceAccount;
- Secret;
- SecretList;
- ConfigMap;
- StorageClass;
- PersistentVolume;
- PersistentVolumeClaim;
- CustomResourceDefinition;
- ClusterRole;
- ClusterRoleList;
- ClusterRoleBinding;
- ClusterRoleBindingList;
- Role;
- RoleList;
- RoleBinding;
- RoleBindingList;
- Service;
- DaemonSet;
- Pod;
- ReplicationController;
- ReplicaSet;
- Deployment;
- HorizontalPodAutoscaler;
- StatefulSet;
- Job;
- CronJob;
- Ingress;
- APIService.
Отслеживание готовности включается для всех ресурсов группы одновременно сразу после создания всех ресурсов группы.
Для изменения порядка развертывания ресурсов можно создать новые группы ресурсов через задание ресурсам веса, отличного от веса по умолчанию 0
. Все ресурсы с одинаковым весом объединяются в группы, а затем группы ресурсов развертываются по очереди, от группы с меньшим весом к большему, например:
# .helm/templates/example.yaml:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: database
annotations:
werf.io/weight: "-1"
# ...
---
apiVersion: batch/v1
kind: Job
metadata:
name: database-migrations
# ...
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app1
annotations:
werf.io/weight: "1"
# ...
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app2
annotations:
werf.io/weight: "1"
# ...
d8 d converge
Результат: сначала был развернут ресурс database
, затем — database-migrations
, а затем параллельно развернулись app1
и app2
.
Запуск задач перед/после установки, обновления, отката или удаления релиза
Для развертывания определенных ресурсов только перед или после установки, обновления, отката или удаления релиза преобразуйте ресурс в хук аннотацией helm.sh/hook
, например:
# .helm/templates/example.yaml:
apiVersion: batch/v1
kind: Job
metadata:
name: database-initialization
annotations:
helm.sh/hook: pre-install
# ...
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
d8 d converge
Результат: ресурс database-initialization
будет развернут только при первой установке релиза, а ресурс myapp
будет развертываться и при установке, и при обновлении, и при откате релиза.
Аннотация helm.sh/hook
объявляет ресурс-хуком и указывает, при каких условиях этот ресурс должен развертываться (можно указать несколько условий через запятую). Возможные условия для развертывания хука:
-
pre-install
— при установке релиза до установки основных ресурсов; -
pre-upgrade
— при обновлении релиза до обновления основных ресурсов; -
pre-rollback
— при откате релиза до отката основных ресурсов; -
pre-delete
— при удалении релиза до удаления основных ресурсов; -
post-install
— при установке релиза после установки основных ресурсов; -
post-upgrade
— при обновлении релиза после обновления основных ресурсов; -
post-rollback
— при откате релиза после отката основных ресурсов; -
post-delete
— при удалении релиза после удаления основных ресурсов.
Для задания хукам порядка развертывания присвойте им разные веса (по умолчанию — 0
), чтобы хуки развертывались по очереди, от хука с меньшим весом к большему, например:
# .helm/templates/example.yaml:
apiVersion: batch/v1
kind: Job
metadata:
name: first
annotations:
helm.sh/hook: pre-install
helm.sh/hook-weight: "-1"
# ...
---
apiVersion: batch/v1
kind: Job
metadata:
name: second
annotations:
helm.sh/hook: pre-install
# ...
---
apiVersion: batch/v1
kind: Job
metadata:
name: third
annotations:
helm.sh/hook: pre-install
helm.sh/hook-weight: "1"
# ...
d8 d converge
Результат: сначала будет развернут хук first
, затем хук second
, затем хук third
.
По умолчанию при повторных развертываниях того же самого хука старый хук в кластере удаляется прямо перед развертыванием нового хука. Этап удаления старого хука можно изменить аннотацией helm.sh/hook-delete-policy
, которая принимает следующие значения:
-
hook-succeeded
— удалять новый хук сразу после его удачного развертывания, при неудачном развертывании не удалять совсем; -
hook-failed
— удалять новый хук сразу после его неудачного развертывания, при удачном развертывании не удалять совсем; -
before-hook-creation
— (по умолчанию) удалять старый хук сразу перед созданием нового.
Ожидание готовности ресурсов, не принадлежащих релизу (только в Deckhouse Delivery)
Развертываемым в текущем релизе ресурсам могут требоваться ресурсы, которые не принадлежат текущему релизу. Deckhouse Delivery может дожидаться готовности этих внешних ресурсов благодаря аннотации <name>.external-dependency.werf.io/resource
, например:
# .helm/templates/example.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
annotations:
secret.external-dependency.werf.io/resource: secret/my-dynamic-vault-secret
# ...
d8 d converge
Результат: Deployment myapp
начнёт развертывание только после того, как Secret my-dynamic-vault-secret
, создаваемый автоматически оператором в кластере, будет создан и готов.
А так можно ожидать готовности сразу нескольких внешних ресурсов:
# .helm/templates/example.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
annotations:
secret.external-dependency.werf.io/resource: secret/my-dynamic-vault-secret
database.external-dependency.werf.io/resource: statefulset/my-database
# ...
По умолчанию Deckhouse Delivery ищет внешний ресурс в Namespace релиза (если, конечно, ресурс не кластерный). Namespace внешнего ресурса можно изменить аннотацией <name>.external-dependency.werf.io/namespace
:
# .helm/templates/example.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
annotations:
secret.external-dependency.werf.io/resource: secret/my-dynamic-vault-secret
secret.external-dependency.werf.io/namespace: my-namespace
Обратите внимание, что ожидать готовность внешнего ресурса будут и все другие ресурсы релиза с тем же весом, так как ресурсы объединяются по весу в группы и развертываются именно группами.
Сценарии развертывания
Обычное развертывание
Обычно развертывание осуществляется командой d8 d converge
, которая собирает образы и развертывает приложение, но требует запуска из Git-репозитория приложения. Пример:
d8 d converge --repo example.org/mycompany/myapp
Если требуется разделить шаги сборки и развертывания, то это можно сделать так:
d8 d build --repo example.org/mycompany/myapp
d8 d converge --require-built-images --repo example.org/mycompany/myapp
Развертывание с использованием произвольных тегов образов
По умолчанию собранные образы получают тег на основе их содержимого, который становится доступен в Values для их дальнейшего использования в шаблонах при развертывании. Но если возникает необходимость тегировать образы иным тегом, то можно использовать параметр --use-custom-tag
, например:
d8 d converge --use-custom-tag '%image%-v1.0.0' --repo example.org/mycompany/myapp
Результат: образы были собраны и опубликованы с тегами <имя image>-v1.0.0
, после чего теги этих образов стали доступны в Values, на основе которых были сформированы и применены конечные манифесты Kubernetes.
В имени тега, указываемом в параметре --use-custom-tag
, можно использовать шаблоны %image%
, %image_slug%
и %image_safe_slug%
для подставления имени образа и %image_content_based_tag%
для подставления оригинального тега на основе содержимого.
Обратите внимание, что при указании произвольного тега публикуется также и образ с тегом на основе содержимого. В дальнейшем при вызове
d8 d cleanup
образ с тегом на основе содержимого и образы с произвольными тегами удаляются вместе.
Если требуется разделить шаги сборки и развертывания, то это можно сделать так:
d8 d build --add-custom-tag '%image%-v1.0.0' --repo example.org/mycompany/myapp
d8 d converge --require-built-images --use-custom-tag '%image%-v1.0.0' --repo example.org/mycompany/myapp
Развертывание без доступа к Git-репозиторию приложения
Если нужно развернуть приложение без доступа к Git-репозиторию приложения, то необходимо выполнить три шага:
-
Сборка образов и их публикация в container registry.
-
Добавление переданных параметров и публикация основного чарта в OCI-репозиторий. Чарт содержит указатели на опубликованные в первом шаге образы.
-
Применение опубликованного бандла в кластер.
Первые два шага выполняются командой d8 d bundle publish
, находясь в Git-репозитории приложения, например:
d8 d bundle publish --tag latest --repo example.org/mycompany/myapp
А третий шаг выполняется командой d8 d bundle apply
уже без необходимости находиться в Git-репозитории приложения, например:
d8 d bundle apply --tag latest --release myapp --namespace myapp-production --repo example.org/mycompany/myapp
Конечный результат будет тот же самый, что и при использовании d8 d converge
.
Если требуется разделить первый и второй шаг, то это можно сделать так:
d8 d build --repo example.org/mycompany/myapp
d8 d bundle publish --require-built-images --tag latest --repo example.org/mycompany/myapp
Развертывание без доступа к Git-репозиторию и container registry приложения
Если нужно развернуть приложение без доступа к Git-репозиторию приложения и без доступа к container registry приложения, то необходимо выполнить пять шагов:
-
Сборка образов и их публикация в container registry приложения.
-
Добавление переданных параметров и публикация основного чарта в OCI-репозиторий. Чарт содержит указатели на опубликованные в первом шаге образы.
-
Экспорт бандла и связанных с ним образов в локальный архив.
-
Импорт заархивированного бандла и его образов в container registry, доступный из Kubernetes-кластера, используемого для развертывания.
-
Применение в кластер бандла, опубликованного в новом container registry.
Первые два шага выполняются командой d8 d bundle publish
, находясь в Git-репозитории приложения, например:
d8 d bundle publish --tag latest --repo example.org/mycompany/myapp
Третий шаг выполняется командой d8 d bundle copy
уже без необходимости находиться в Git-репозитории приложения, например:
d8 d bundle copy --from example.org/mycompany/myapp:latest --to archive:myapp-latest.tar.gz
Теперь полученный локальный архив myapp-latest.tar.gz
переносится удобным способом туда, откуда имеется доступ в container registry, используемый для развертывания в Kubernetes-кластер, и снова выполняется команда d8 d bundle copy
, например:
d8 d bundle copy --from archive:myapp-latest.tar.gz --to registry.internal/mycompany/myapp:latest
В результате чарт и связанные с ним образы опубликуются в новый container registry, к которому из Kubernetes-кластера уже есть доступ. Осталось только развернуть опубликованный бандл в кластер командой d8 d bundle apply
, например:
d8 d bundle apply --tag latest --release myapp --namespace myapp-production --repo registry.internal/mycompany/myapp
На этом шаге уже не требуется доступ ни в Git-репозиторий приложения, ни в его первоначальный container registry. Конечный результат развертывания бандла будет тот же самый, что и при использовании d8 d converge
.
Если требуется разделить первый и второй шаг, то это можно сделать так:
d8 d build --repo example.org/mycompany/myapp
d8 d bundle publish --require-built-images --tag latest --repo example.org/mycompany/myapp
Развертывание сторонним инструментом
Если нужно выполнить применение конечных манифестов приложения не с Deckhouse Delivery, а с использованием другого инструмента (kubectl, Helm, …), то необходимо выполнить три шага:
-
Сборка образов и их публикация в container registry.
-
Формирование конечных манифестов.
-
Развертывание получившихся манифестов в кластер, используя сторонний инструмент.
Первые два шага выполняются командой d8 d render
, находясь в Git-репозитории приложения:
d8 d render --output manifests.yaml --repo example.org/mycompany/myapp
Теперь полученные манифесты можно передать в сторонний инструмент для дальнейшего развертывания, например:
kubectl apply -f manifests.yaml
Обратите внимание, что некоторые специальные возможности Deckhouse Delivery вроде возможности изменения порядка развертывания ресурсов на основании их веса (аннотация
werf.io/weight
) скорее всего не будут поддерживаться при применении манифестов сторонним инструментом.
Если требуется разделить первый и второй шаг, то это можно сделать так:
d8 d build --repo example.org/mycompany/myapp
d8 d render --require-built-images --output manifests.yaml --repo example.org/mycompany/myapp
Развертывание сторонним инструментом без доступа к Git-репозиторию приложения
Если нужно выполнить применение конечных манифестов приложения не с Deckhouse Delivery, а с использованием другого инструмента (kubectl, Helm, …), при этом не имея доступа к Git-репозиторию приложения, то необходимо выполнить три шага:
-
Сборка образов и их публикация в container registry.
-
Добавление переданных параметров и публикация основного чарта в OCI-репозиторий. Чарт содержит указатели на опубликованные в первом шаге образы.
-
Формирование из бандла конечных манифестов.
-
Развертывание получившихся манифестов в кластер используя сторонний инструмент.
Первые два шага выполняются командой d8 d bundle publish
, находясь в Git-репозитории приложения:
d8 d bundle publish --tag latest --repo example.org/mycompany/myapp
А третий шаг выполняется командой d8 d bundle render
уже без необходимости находиться в Git-репозитории приложения, например:
d8 d bundle render --output manifests.yaml --tag latest --release myapp --namespace myapp-production --repo example.org/mycompany/myapp
Теперь полученные манифесты можно передать в сторонний инструмент для дальнейшего развертывания, например:
kubectl apply -f manifests.yaml
Обратите внимание, что некоторые специальные возможности Deckhouse Delivery, вроде возможности изменения порядка развертывания ресурсов на основании их веса (аннотация
werf.io/weight
), скорее всего не будут поддерживаться при применении манифестов сторонним инструментом.
Если требуется разделить первый и второй шаг, то это можно сделать так:
d8 d build --repo example.org/mycompany/myapp
d8 d bundle publish --require-built-images --tag latest --repo example.org/mycompany/myapp
Сохранение отчета о развертывании
Команды d8 d converge
и d8 d bundle apply
имеют параметр --save-deploy-report
, который позволяет сохранить отчёт о последнем развертывании в файл. Отчёт содержит имя релиза, Namespace, статус развертывания и ряд других данных. Пример:
d8 d converge --save-deploy-report
Результат: после развертывания появится файл .werf-deploy-report.json
, содержащий информацию о последнем релизе.
Путь к отчёту о развертывании можно изменить параметром --deploy-report-path
.
Удаление развернутого приложения
Удалить развернутое приложение можно командой d8 d dismiss
, запущенной из Git-репозитория приложения, например:
d8 d dismiss --env staging
При отсутствии доступа к Git-репозиторию приложения можно явно указать имя релиза и Namespace:
d8 d dismiss --release myapp-staging --namespace myapp-staging
… или использовать отчёт о предыдущем развертывании, включаемый опцией --save-deploy-report
у d8 d converge
и d8 d bundle apply
, который содержит имя релиза и Namespace:
d8 d converge --save-deploy-report
cp .werf-deploy-report.json /anywhere
cd /anywhere
d8 d dismiss --use-deploy-report
Путь к отчёту о развертывании можно изменить параметром --deploy-report-path
.
Отслеживание ресурсов
Отслеживание состояния ресурсов
Развертывание ресурсов делится на две стадии: применение ресурсов в кластер и отслеживание состояния этих ресурсов. Deckhouse Delivery реализует продвинутое отслеживание состояния ресурсов (только в Deckhouse Delivery) благодаря библиотеке kubedog.
Отслеживание ресурсов включено по умолчанию для всех поддерживаемых ресурсов, а именно для:
-
всех ресурсов релиза;
-
некоторых ресурсов, опосредованно создаваемых ресурсами релиза;
-
ресурсов вне релиза, указанных в аннотациях
<name>.external-dependency.werf.io/resource
.
Для ресурсов Deployment, StatefulSet, DaemonSet, Job и Flagger Canary задействуются специальные отслеживатели состояния, которые не только точно определяют, удачно или неудачно ресурс был развернут, но и отслеживают состояние дочерних ресурсов, таких как Pod’ы, создаваемые Deployment’ом.
Для остальных ресурсов, не имеющих специальных отслеживателей состояния, задействуется универсальный отслеживатель, который предполагает удачность развертывания ресурса на основании доступной в кластере информации о ресурсе. В редких случаях, если универсальный отслеживатель ошибается в своих предположениях, отслеживание для этого ресурса можно отключить.
Изменение критериев неудачного развертывания ресурса (только в Deckhouse Delivery)
По умолчанию Deckhouse Delivery прерывает развертывание и помечает его как неудачное, если произошло более двух ошибок при развертывании одного из ресурсов.
Изменить максимальное количество ошибок развертывания для ресурса можно аннотацией werf.io/failures-allowed-per-replica
, например:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
annotations:
werf.io/failures-allowed-per-replica: "5"
Если ресурс имеет аннотацию werf.io/fail-mode: HopeUntilEndOfDeployProcess
, то ошибки его развертывания будут учитываться только после того, как все остальные ресурсы удачно развернутся.
А помеченный аннотацией werf.io/track-termination-mode: NonBlocking
ресурс будет отслеживаться только пока все остальные ресурсы не будут развернуты, после чего этот ресурс автоматически посчитается развернутым, даже если это не так.
Универсальный отслеживатель отсутствие активности у ресурса в течение 4 минут считает ошибкой развертывания. Изменить этот период можно аннотацией werf.io/no-activity-timeout
, например:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
annotations:
werf.io/no-activity-timeout: 10m
Отключение отслеживания состояния и игнорирование ошибок ресурса (только в Deckhouse Delivery)
Для отключения отслеживания состояния ресурса и игнорирования ошибок его развертывания пометьте ресурс аннотациями werf.io/fail-mode: IgnoreAndContinueDeployProcess
и werf.io/track-termination-mode: NonBlocking
, например:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
annotations:
werf.io/fail-mode: IgnoreAndContinueDeployProcess
werf.io/track-termination-mode: NonBlocking
Отображение логов контейнеров (только в Deckhouse Delivery)
Благодаря библиотеке kubedog werf автоматически отображает логи контейнеров, создаваемых при развертывании Deployment, StatefulSet, DaemonSet и Job.
Выключить отображение логов для ресурса можно аннотацией werf.io/skip-logs: "true"
, например:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
annotations:
werf.io/skip-logs: "true"
А в аннотации werf.io/show-logs-only-for-containers
можно явно перечислить контейнеры, логи которых следует отображать, в то же время скрыв логи всех остальных контейнеров, например:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
annotations:
werf.io/show-logs-only-for-containers: "backend,frontend"
… или наоборот — в аннотации werf.io/skip-logs-for-containers
перечислить контейнеры, логи которых не следует отображать, в то же время отображая логи всех остальных контейнеров, например:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
annotations:
werf.io/skip-logs-for-containers: "sidecar"
Для отображения только тех строк лога, которые соответствуют регулярному выражению, используйте аннотацию werf.io/log-regex
, например:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
annotations:
werf.io/log-regex: ".*ERROR.*"
Возможно отфильтровать строки лога согласно регулярному выражению не для всех контейнеров сразу, а только для определённого контейнера, если использовать аннотацию werf.io/log-regex-for-<имя контейнера>
, например:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
annotations:
werf.io/log-regex-for-backend: ".*ERROR.*"
Отображение Events ресурсов (только в Deckhouse Delivery)
Благодаря библиотеке kubedog werf может отображать Events отслеживаемых ресурсов, если ресурс имеет аннотацию werf.io/show-service-messages: "true"
, например:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
annotations:
werf.io/show-service-messages: "true"
Управление релизами
О релизах
Результатом развертывания является релиз — совокупность развернутых в кластере ресурсов и служебной информации.
Технически релизы Deckhouse Delivery являются релизами Helm 3 и полностью с ними совместимы. Служебная информация по умолчанию хранится в специальном Secret-ресурсе.
Автоматическое формирование имени релиза (только в Deckhouse Delivery)
По умолчанию имя релиза формируется автоматически по специальному шаблону [[ project ]]-[[ env ]]
, где project
— имя проекта Deckhouse Delivery, а env
— имя окружения, например:
# werf.yaml:
project: myapp
d8 d converge --env staging
d8 d converge --env production
Результат: созданы релизы myapp-staging
и myapp-production
.
Изменение шаблона имени релиза (только в Deckhouse Delivery)
Если вас не устраивает специальный шаблон, из которого формируется имя релиза, вы можете его изменить:
# werf.yaml:
project: myapp
deploy:
helmRelease: "backend-[[ env ]]"
d8 d converge --env production
Результат: создан релиз backend-production
.
Прямое указание имени релиза
Вместо формирования имени релиза по специальному шаблону можно указывать имя релиза явно для каждой команды:
d8 d converge --release backend-production # или $WERF_RELEASE=...
Результат: создан релиз backend-production
.
Форматирование имени релиза
Имя релиза, сформированное по специальному шаблону или указанное опцией --release
, приводится к формату RFC 1123 Label Names автоматически. Отключить автоматическое форматирование можно директивой deploy.helmReleaseSlug
файла werf.yaml
.
Вручную отформатировать любую строку согласно формату RFC 1123 Label Names можно командой d8 d slugify -f helm-release
.
Добавление в релиз уже существующих в кластере ресурсов
Deckhouse Delivery не позволяет развернуть новый ресурс релиза поверх уже существующего в кластере ресурса, если ресурс в кластере не является частью текущего релиза. Такое поведение предотвращает случайные обновления ресурсов, принадлежащих другому релизу или развернутых без Deckhouse Delivery. Если все же попытаться это сделать, то отобразится следующая ошибка:
Error: helm upgrade have failed: UPGRADE FAILED: rendered manifests contain a resource that already exists...
Чтобы добавить ресурс в кластере в текущий релиз и разрешить его обновление, выставьте ресурсу в кластере аннотации meta.helm.sh/release-name: <имя текущего релиза>
, meta.helm.sh/release-namespace: <Namespace текущего релиза>
и лейбл app.kubernetes.io/managed-by: Helm
, например:
kubectl annotate deploy/myapp meta.helm.sh/release-name=myapp-production
kubectl annotate deploy/myapp meta.helm.sh/release-namespace=myapp-production
kubectl label deploy/myapp app.kubernetes.io/managed-by=Helm
… после чего перезапустите развертывание:
d8 d converge
Результат: ресурс релиза myapp
успешно обновил ресурс myapp
в кластере и теперь ресурс в кластере является частью текущего релиза.
Автоматическое аннотирование выкатываемых ресурсов релиза
Deckhouse Delivery автоматически выставляет следующие аннотации всем ресурсам чарта в процессе развёртывания:
"werf.io/version": FULL_WERF_VERSION
— версия Deckhouse Delivery, использованная в процессе запуска командыd8 d converge
;"project.werf.io/name": PROJECT_NAME
— имя проекта, указанное в файле конфигурацииwerf.yaml
;"project.werf.io/env": ENV
— имя окружения, указанное с помощью параметра--env
или переменной окруженияWERF_ENV
(аннотация не устанавливается, если окружение не было указано при запуске).
При использовании команды d8 d ci-env
с поддерживаемыми CI/CD системами добавляются аннотации, которые позволяют пользователю перейти в связанный пайплайн, задание и коммит при необходимости.
Добавление произвольных аннотаций и лейблов для выкатываемых ресурсов релиза
Пользователь может устанавливать произвольные аннотации и лейблы используя CLI-параметры при развёртывании --add-annotation annoName=annoValue
(может быть указан несколько раз) и --add-label labelName=labelValue
(может быть указан несколько раз). Аннотации и лейблы так же могут быть заданы с помощью соответствующих переменных WERF_ADD_LABEL*
и WERF_ADD_ANNOTATION*
(к примеру, WERF_ADD_ANNOTATION_1=annoName1=annoValue1
и WERF_ADD_LABEL_1=labelName1=labelValue1
).
Например, для установки аннотаций и лейблов commit-sha=9aeee03d607c1eed133166159fbea3bad5365c57
, gitlab-user-email=vasya@myproject.com
всем ресурсам Kubernetes в чарте, можно использовать следующий вызов команды деплоя:
d8 d converge \
--add-annotation "commit-sha=9aeee03d607c1eed133166159fbea3bad5365c57" \
--add-label "commit-sha=9aeee03d607c1eed133166159fbea3bad5365c57" \
--add-annotation "gitlab-user-email=vasya@myproject.com" \
--add-label "gitlab-user-email=vasya@myproject.com" \
--env dev \
--repo REPO