Что такое конфигурация werf?
Для использования werf в приложении должна быть описана конфигурация, которая включает в себя:
- Определение мета-информации проекта. Например, имени проекта, которое будет впоследствии влиять на результат сборки, деплоя и другие команды.
- Определение списка образов проекта и инструкций их сборки.
Конфигурация werf хранится в YAML-файле werf.yaml
в корневой папке проекта (приложения), и представляет собой набор секций конфигурации – частей YAML-файла, разделенных тремя дефисами:
CONFIG_SECTION
---
CONFIG_SECTION
---
CONFIG_SECTION
Каждая секция конфигурации может быть одного из трех типов:
- Секция для описания мета-информации проекта, далее — секция мета-информации.
- Секция описания инструкций сборки образа, далее — секция образа.
- Секция описания инструкций сборки артефакта, далее — секция артефакта.
В будущем, возможно, количество типов увеличится.
Секция мета-информации
project: PROJECT_NAME
configVersion: CONFIG_VERSION
OTHER_FIELDS
---
Секция мета-информации, обязательная секция конфигурации, содержащая ключи project: PROJECT_NAME
и configVersion: CONFIG_VERSION
.
В каждом файле конфигурации werf.yaml
должна быть только одна секция мета-информации.
Директивы deploy
и cleanup
вынесены в отдельные статьи, развертывание в Kubernetes и политики очистки.
Имя проекта
Ключ project
определяет уникальное имя проекта вашего приложения.
Имя проекта влияет на имена образов в stages storage, namespace в Kubernetes, имя Helm-релиза и зависящие от него имена (смотри подробнее про развертывание в Kubernetes).
Ключ project
— единственное обязательное поле секции мета-информации.
Имя проекта должно быть уникальным в пределах группы проектов, собираемых на одном сборочном узле и развертываемых на один и тот же кластер Kubernetes (например, уникальным в пределах всех групп одного GitLab).
Имя проекта должно быть не более 50 символов, содержать только строчные буквы латинского алфавита, цифры и знак дефиса.
ВНИМАНИЕ. Никогда не меняйте имя проекта в процессе работы, если вы не осознаете всех последствий.
Смена имени проекта приводит к следующим проблемам:
- Инвалидация сборочного кэша. Все образы должны быть собраны повторно, а старые удалены из локального хранилища или Docker registry вручную.
- Создание совершенно нового Helm-релиза. Смена имени проекта и повторное развертывание приложения приведет к созданию еще одного экземпляра, если вы уже развернули ваше приложение в кластере Kubernetes.
werf не поддерживает изменение имени проекта и все возникающие проблемы должны быть решены вручную.
Версии конфигурации
Директива configVersion
определяет формат файла werf.yaml
.
В настоящее время, это всегда — 1
.
Секция образа
В каждой секции образа содержатся инструкции, описывающие правила сборки одного независимого образа.
В одном файле конфигурации werf.yaml
может быть произвольное количество секций образов.
Секция образа определяется по наличию в секции конфигурации ключа image: IMAGE_NAME
, где IMAGE_NAME
— короткое имя Docker-образа.
Это имя должно быть уникальным в пределах одного файла конфигурации werf.yaml
.
image: IMAGE_NAME_1
OTHER_FIELDS
---
image: IMAGE_NAME_2
OTHER_FIELDS
---
...
---
image: IMAGE_NAME_N
OTHER_FIELDS
Секция артефакта
Секция артефакта также содержит инструкции, описывающие правила сборки одного независимого образа артефактов.
Образ артефакта — это вспомогательный образ, предназначенный для изолирования сборочного окружения, инструментов и данных, от конечного образа приложения (подробнее здесь).
В одном файле конфигурации werf.yaml
может быть произвольное количество секций артефактов.
Секция артефакта определяется по наличию в секции конфигурации ключа artifact: IMAGE_NAME
, где IMAGE_NAME
— произвольное имя, на которое можно ссылаться из других секций конфигурации.
Это имя должно быть уникальным в пределах одного файла конфигурации werf.yaml
.
Пример минимальной конфигурации
project: my-project
configVersion: 1
Описание конфигурации в нескольких файлах
При использовании директории шаблонов
Часть конфигурации может быть вынесена в отдельные файлы шаблонов и затем включаться в werf.yaml. Файлы шаблонов должны размещаться в папке .werf и иметь расширение .tmpl (поддерживается вложенность).
Совет: вы можете скачать или сгенерировать шаблоны до запуска werf. Например, это может быть удобно при вынесении общей логики нескольких проектов в общие шаблоны
werf обрабатывает все файлы в одном окружении, поэтому define в одном шаблоне, доступен в любом другом, включая сам werf.yaml.
{{ $_ := set . "RubyVersion" "2.3.4" }}
{{ $_ := set . "BaseImage" "alpine" }}
project: my-project
configVersion: 1
---
image: rails
from: {{ .BaseImage }}
ansible:
beforeInstall:
{{- include "(component) mysql client" . }}
{{- include "(component) ruby" . }}
install:
{{- include "(component) Gemfile dependencies" . }}
{{- define "(component) Gemfile dependencies" }}
- file:
path: /root/.ssh
state: directory
owner: root
group: root
recurse: yes
- name: "Setup ssh known_hosts used in Gemfile"
shell: |
set -e
ssh-keyscan github.com >> /root/.ssh/known_hosts
ssh-keyscan mygitlab.myorg.com >> /root/.ssh/known_hosts
args:
executable: /bin/bash
- name: "Install Gemfile dependencies with bundler"
shell: |
set -e
source /etc/profile.d/rvm.sh
cd /app
bundle install --without development test --path vendor/bundle
args:
executable: /bin/bash
{{- end }}
{{- define "(component) mysql client" }}
- name: "Install mysql client"
apt:
name: "{{`{{ item }}`}}"
update_cache: yes
with_items:
- libmysqlclient-dev
- mysql-client
- g++
{{- end }}
{{- define "(component) ruby" }}
- command: gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
- get_url:
url: https://raw.githubusercontent.com/rvm/rvm/master/binscripts/rvm-installer
dest: /tmp/rvm-installer
- name: "Install rvm"
command: bash -e /tmp/rvm-installer
- name: "Install ruby {{ .RubyVersion }}"
raw: bash -lec {{`{{ item | quote }}`}}
with_items:
- rvm install {{ .RubyVersion }}
- rvm use --default {{ .RubyVersion }}
- gem install bundler --no-ri --no-rdoc
- rvm cleanup all
{{- end }}
При существовании нескольких шаблонов с одинаковым именем, werf будет использовать шаблон определенный в werf.yaml, либо последний описанный в файлах шаблонов
Если нужно обратиться непосредственно к файлу шаблона, например, в функции include, то можно использовать путь к соответствующему файлу относительно папки .werf.
project: my-project
configVersion: 1
---
image: app
from: java:8-jdk-alpine
shell:
beforeInstall:
- mkdir /app
- adduser -Dh /home/gordon gordon
import:
- artifact: appserver
add: '/usr/src/atsea/target/AtSea-0.0.1-SNAPSHOT.jar'
to: '/app/AtSea-0.0.1-SNAPSHOT.jar'
after: install
- artifact: storefront
add: /usr/src/atsea/app/react-app/build
to: /static
after: install
docker:
ENTRYPOINT: ["java", "-jar", "/app/AtSea-0.0.1-SNAPSHOT.jar"]
CMD: ["--spring.profiles.active=postgres"]
---
{{ include "artifact/appserver.tmpl" . }}
---
{{ include "artifact/storefront.tmpl" . }}
artifact: appserver
from: maven:latest
git:
- add: '/app'
to: '/usr/src/atsea'
shell:
install:
- cd /usr/src/atsea
- mvn -B -f pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolve
- mvn -B -s /usr/share/maven/ref/settings-docker.xml package -DskipTests
artifact: storefront
from: node:latest
git:
- add: /app/react-app
to: /usr/src/atsea/app/react-app
shell:
install:
- cd /usr/src/atsea/app/react-app
- npm install
- npm run build
При использовании функции tpl
Функция tpl
позволяет обрабатывать строки, как Go шаблоны в werf.yaml
, передавая их в переменных окружения либо в файлах проекта. Таким образом, шаблоны могут располагаться в произвольном месте в проекте и добавляться в werf.yaml
.
Стоит отметить, что шаблоны выполняются в общем контексте, поэтому в них доступны все шаблоны и значения из werf.yaml
и директории шаблонов.
{{ $_ := set . "BaseImage" "node:14.3" }}
project: app
configVersion: 1
---
{{ range $path, $content := .Files.Glob "**/werf-partial.yaml" }}
{{ tpl $content $ }}
{{ end }}
{{- define "common install commands" }}
- npm install
- npm run build
{{- end }}
image: backend
from: {{ .BaseImage }}
git:
- add: /backend
to: /app/backend
shell:
install:
- cd /app/backend
{{- include "common install commands" . | indent 2 }}
image: frontend
from: {{ .BaseImage }}
git:
- add: /frontend
to: /app/frontend
shell:
install:
- cd /app/frontend
{{- include "common install commands" . | indent 2 }}
Этапы обработки конфигурации
Следующие шаги описывают как происходит обработка конфигурации:
- Чтение файла
werf.yaml
и файлов шаблонов из папки.werf
. - Выполнение Go-шаблонов.
- Сохранение отрендеренного файла, получившегося после применения Go-шаблонов, в
.werf.render.yaml
(файл остаётся после выполнения команды и удаляется автоматически с помощью процедуры GC). - Разбиение отрендеренного YAML-файла на секции (они отделяются друг от друга тремя дефисами, смотри подробнее спецификацию).
- Валидация каждой секции конфигурации:
- Валидация YAML-синтаксиса (смотрите подробнее здесь).
- Валидация werf-синтаксиса.
- Генерация описанных в файле конфигураций образов.
Шаблоны Go
В конфигурации возможно использование шаблонов Go (Go templates). Поддерживаются следующие функции:
-
Встроенные функции шаблонов Go и некоторые особенности языка. Например, использование переменных:
{{ $base_image := "golang:1.11-alpine" }} project: my-project configVersion: 1 --- image: gogomonia from: {{ $base_image }} --- image: saml-authenticator from: {{ $base_image }}
-
Sprig-функции. Например, использование переменных окружения:
project: my-project configVersion: 1 --- {{ $_ := env "SPECIFIC_ENV_HERE" | set . "GitBranch" }} image: ~ from: alpine git: - url: https://github.com/company/project1.git branch: {{ .GitBranch }} add: / to: /app/project1 - url: https://github.com/company/project2.git branch: {{ .GitBranch }} add: / to: /app/project2
-
Функция
include
и конструкцияdefine
для переиспользования частей конфигурации:project: my-project configVersion: 1 --- image: app1 from: alpine ansible: beforeInstall: {{- include "(component) ruby" . }} --- image: app2 from: alpine ansible: beforeInstall: {{- include "(component) ruby" . }} {{- define "(component) ruby" }} - command: gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 - get_url: url: https://raw.githubusercontent.com/rvm/rvm/master/binscripts/rvm-installer dest: /tmp/rvm-installer - name: "Install rvm" command: bash -e /tmp/rvm-installer - name: "Install ruby 2.3.4" raw: bash -lec {{`{{ item | quote }}`}} with_items: - rvm install 2.3.4 - rvm use --default 2.3.4 - gem install bundler --no-ri --no-rdoc - rvm cleanup all {{- end }}
-
Функция
tpl
позволяет обрабатывать строки, как Go шаблоны вwerf.yaml
, передавая их в переменных окружения либо в файлах проекта: раннее описанный пример с файлами проекта. -
Функции
.Files.Get
и.Files.Glob
для работы с файлами проекта:.Files.Get
project: my-project configVersion: 1 --- image: app from: alpine ansible: setup: - name: "Setup /etc/nginx/nginx.conf" copy: content: | {{ .Files.Get ".werf/nginx.conf" | indent 8 }} dest: /etc/nginx/nginx.conf
.Files.Glob
Функция поддерживает shell pattern matching +
**
. Результаты вызова функции можно объединить со sprig функциейmerge
(к примеру,{{ $filesDict := merge (.Files.Glob "*/*.txt") (.Files.Glob "app/**/*.txt") }}
)project: my-project configVersion: 1 --- image: app from: alpine ansible: install: - raw: mkdir /app setup: {{ range $path, $content := .Files.Glob ".werf/files/*" }} - name: "Setup /app/{{ base $path }}" copy: content: | {{ $content | indent 8 }} dest: /app/{{ base $path }} {{ end }}
.Files.Get
project: my-project configVersion: 1 --- image: app from: alpine shell: setup: - | head -c -1 <<'EOF' > /etc/nginx/nginx.conf {{ .Files.Get ".werf/nginx.conf" | indent 4 }} EOF
.Files.Glob
Функция поддерживает shell pattern matching +
**
. Результаты вызова функции можно объединить со sprig функциейmerge
(к примеру,{{ $filesDict := merge (.Files.Glob "*/*.txt") (.Files.Glob "app/**/*.txt") }}
)project: my-project configVersion: 1 --- image: app from: alpine shell: install: mkdir /app setup: {{ range $path, $content := .Files.Glob ".werf/files/*" }} - | head -c -1 <<EOF > /app/{{ base $path }} {{ $content | indent 4 }} EOF {{ end }}