Что такое конфигурация werf?

Для использования werf в приложении должна быть описана конфигурация, которая включает в себя:

  1. Определение мета-информации проекта. Например, имени проекта, которое будет впоследствии влиять на результат сборки, деплоя и другие команды.
  2. Определение списка образов проекта и инструкций их сборки.

Конфигурация werf хранится в YAML-файле werf.yaml в корневой папке проекта (приложения), и представляет собой набор секций конфигурации – частей YAML-файла, разделенных тремя дефисами:

CONFIG_SECTION
---
CONFIG_SECTION
---
CONFIG_SECTION

Каждая секция конфигурации может быть одного из трех типов:

  1. Секция для описания мета-информации проекта, далее — секция мета-информации.
  2. Секция описания инструкций сборки образа, далее — секция образа.
  3. Секция описания инструкций сборки артефакта, далее — секция артефакта.

В будущем, возможно, количество типов увеличится.

Секция мета-информации

project: PROJECT_NAME
configVersion: CONFIG_VERSION
OTHER_FIELDS
---

Секция мета-информации, обязательная секция конфигурации, содержащая ключи project: PROJECT_NAME и configVersion: CONFIG_VERSION. В каждом файле конфигурации werf.yaml должна быть только одна секция мета-информации.

Имя проекта

Ключ project определяет уникальное имя проекта вашего приложения. Имя проекта влияет на имена образов в stages storage, namespace в Kubernetes, имя Helm-релиза и зависящие от него имена (смотри подробнее про развертывание в Kubernetes). Ключ project — единственное обязательное поле секции мета-информации.

Имя проекта должно быть уникальным в пределах группы проектов, собираемых на одном сборочном узле и развертываемых на один и тот же кластер Kubernetes (например, уникальным в пределах всех групп одного GitLab).

Имя проекта должно быть не более 50 символов, содержать только строчные буквы латинского алфавита, цифры и знак дефиса.

ВНИМАНИЕ. Никогда не меняйте имя проекта в процессе работы, если вы не осознаете всех последствий.

Смена имени проекта приводит к следующим проблемам:

  1. Инвалидация сборочного кэша. Все образы должны быть собраны повторно, а старые удалены из локального хранилища или Docker registry вручную.
  2. Создание совершенно нового 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.

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" . }}
.werf/ansible/components.tmpl
{{- 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.

werf.yaml
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" . }}
.werf/artifact/appserver.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
.werf/artifact/storefront.tmpl
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

Этапы обработки конфигурации

Следующие шаги описывают как происходит обработка конфигурации:

  1. Чтение файла werf.yaml и файлов шаблонов из папки .werf.
  2. Выполнение Go-шаблонов.
  3. Сохранение отрендеренного файла, получившегося после применения Go-шаблонов, в .werf.render.yaml (файл остаётся после выполнения команды и удаляется автоматически с помощью процедуры GC).
  4. Разбиение отрендеренного YAML-файла на секции (они отделяются друг от друга тремя дефисами, смотри подробнее спецификацию).
  5. Валидация каждой секции конфигурации:
    • Валидация YAML-синтаксиса (смотрите подробнее здесь).
    • Валидация werf-синтаксиса.
  6. Генерация описанных в файле конфигураций образов.

Шаблоны 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 }}
    
  • Функция .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
 project: my-project
 configVersion: 1
 ---

 image: app
 from: alpine
 shell:
   setup:
   - |
     head -c -1 <<EOF | tee file > /etc/nginx/nginx.conf
 {{ .Files.Get ".werf/nginx.conf" | indent 4 }}
     EOF