What is werf config?
Application should be configured to use werf. This configuration includes:
- Definition of project meta information such as project name, which will affect build, deploy and other commands.
- Definition of the images to be built.
werf uses YAML configuration file werf.yaml
placed in the root folder of your application. The config is a collection of config sections – parts of YAML file separated by three hyphens:
CONFIG_SECTION
---
CONFIG_SECTION
---
CONFIG_SECTION
Each config section has a type. There are currently 3 types of config sections:
- Config section to describe project meta information, which will be referred to as meta config section.
- Config section to describe image build instructions, which will be referred to as image config section.
- Config section to describe artifact build instructions, which will be referred to as artifact config section.
More types can be added in the future.
Meta config section
project: PROJECT_NAME
configVersion: CONFIG_VERSION
OTHER_FIELDS
---
Config section with the key project: PROJECT_NAME
and configVersion: CONFIG_VERSION
is the meta config section. This is required section. There should be only one meta config section in a single werf.yaml
configuration.
There are other directives, deploy
and cleanup
, described in separate articles: deploy to Kubernetes and cleanup policies.
Project name
project
defines unique project name of your application. Project name affects build cache image names, Kubernetes Namespace, Helm Release name and other derived names (see deploy to Kubernetes for detailed description). This is single required field of meta configuration.
Project name should be unique within group of projects that shares build hosts and deployed into the same Kubernetes cluster (i.e. unique across all groups within the same gitlab).
Project name must be maximum 50 chars, only lowercase alphabetic chars, digits and dashes are allowed.
WARNING. You should never change project name, once it has been set up, unless you know what you are doing.
Changing project name leads to issues:
- Invalidation of build cache. New images must be built. Old images must be cleaned up from local host and Docker registry manually.
- Creation of completely new Helm Release. So if you already had deployed your application, then changed project name and deployed it again, there will be created another instance of the same application.
werf cannot automatically resolve project name change. Described issues must be resolved manually.
Config version
The configVersion
defines a werf.yaml
format. It should always be 1
for now.
Image config section
Each image config section defines instructions to build one independent docker image. There may be multiple image config sections defined in the same werf.yaml
config to build multiple images.
Config section with the key image: IMAGE_NAME
is the image config section. image
defines short name of the docker image to be built. This name must be unique in a single werf.yaml
config.
image: IMAGE_NAME_1
OTHER_FIELDS
---
image: IMAGE_NAME_2
OTHER_FIELDS
---
...
---
image: IMAGE_NAME_N
OTHER_FIELDS
Artifact config section
Artifact config section also defines instructions to build one independent artifact docker image. Arifact is a secondary image aimed to isolate a build process and build tools resources (environments, software, data, see artifacts article for the details). There may be multiple artifact config sections for multiple artifact config sections defined in the same werf.yaml
config.
Config section with the key artifact: IMAGE_NAME
is the artifact config section. artifact
defines short name of the artifact to be referred to from another config sections. This name must be unique in a single werf.yaml
config.
Minimal config example
project: my-project
configVersion: 1
Organizing configuration
With templates dir
Part of the configuration can be moved in separate template files and then included into werf.yaml. Template files should live in the .werf directory with .tmpl extension (any nesting is supported).
Tip: templates can be generated or downloaded before running werf. For example, for sharing common logic between projects
werf parses all files in one environment, thus described define of one template file becomes available in other files, including 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 }}
If there are templates with the same name werf will use template defined in werf.yaml or the latest described in templates files
If need to use the whole template file, use template file path relative to .werf directory as a template name in include function.
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
With tpl function
The tpl
function allows the user to evaluate strings as Go templates inside a template. Thus, werf partials can be located anywhere in the project and be included in werf.yaml
.
It is worth noting that these files can use anything defined in werf.yaml
and templates in .werf
(templates dir).
{{ $_ := 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 }}
Processing of config
The following steps could describe the processing of a YAML configuration file:
- Reading
werf.yaml
and extra templates from.werf
directory. - Executing Go templates.
- Saving dump into
.werf.render.yaml
(this file remains after the command execution and will be removed automatically with GC procedure). - Splitting rendered YAML file into separate config sections (part of YAML stream separated by three hyphens, https://yaml.org/spec/1.2/spec.html#id2800132).
- Validating each config section:
- Validating YAML syntax (you could read YAML reference here).
- Validating werf syntax.
- Generating a set of images.
Go templates
Go templates are available within YAML configuration. The following functions are supported:
-
Built-in Go template functions and other language features. E.g. using common variable:
{{ $base_image := "golang:1.11-alpine" }} project: my-project configVersion: 1 --- image: gogomonia from: {{ $base_image }} --- image: saml-authenticator from: {{ $base_image }}
-
Sprig functions. E.g. using environment variable:
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
function withdefine
for reusing configs: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
function to evaluate strings (either content of environment variable or project file) as Go templates inside a template: example with project files. -
.Files.Get
and.Files.Glob
functions to work with project files:.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
The function supports shell pattern matching +
**
. Results can be merged withmerge
sprig function (e.g{{ $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
The function supports shell pattern matching +
**
. Results can be merged withmerge
sprig function (e.g{{ $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 }}