werf.yaml и два билдера

Уровень: intermediate ~55 мин Практика: Vikunja (AGPL-3.0)

Что нужно знать перед уроком

  • M3.01 — Docker-основы — нужен опыт с обычным Dockerfile и multi-stage

Что нужно знать перед уроком

M3 урок 1 — базовый Dockerfile и multi-stage. Теперь оборачиваем это в конфигурацию werf.yaml, единую для всех образов Application-пакета.

Теория

Структура werf.yaml: project/configVersion + множественные image:-документы

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
project: vikunja
configVersion: 1
---
image: backend
dockerfile: Dockerfile
context: api/
---
image: frontend
dockerfile: Dockerfile
context: frontend/

werf.yaml — YAML-документ, разделённый на секции --- (YAML-мультидокумент). Первая секция — общие метаданные проекта (project, configVersion — версия схемы самого конфига, не приложения). Каждая следующая секция, начинающаяся с image:, описывает один собираемый образ. Ключевой момент, критичный для Application с несколькими компонентами (backend+frontend, как у Vikunja, или auth+controlplane): все image:-блоки одного werf.yaml собираются одной командой werf build/werf converge, а связи между ними (кто от кого зависит) описываются директивой dependencies — разбираем в M3 уроке 4.

Dockerfile-builder: dockerfile/context/target

1
2
3
4
5
6
image: backend
dockerfile: Dockerfile
context: api/
target: production # выбор конкретной стадии из multi-stage Dockerfile
args:
  BUILD_VERSION: "{{ .Version }}"

target: — прямой аналог docker build --target — позволяет держать несколько независимых образов в одном multi-stage Dockerfile и выбирать нужный per-image:-блок. Классический пример: один Dockerfile с стадиями FROM node AS frontend-build, FROM golang AS backend-build, FROM nginx AS frontend (target: frontend, копирует статику из frontend-build), FROM alpine AS backend (target: backend, копирует бинарник из backend-build) — четыре стадии, две из которых стадии-инструменты, две — реальные image:-блоки werf.yaml с разными target:.

Выбор Dockerfile-builder оправдан, когда у вас уже есть готовый (или предпочитаемый по стандарту команды) Dockerfile, нет необходимости в тонком контроле кэша по отдельным Git-путям, а инструментарий вокруг Dockerfile (например, hadolint, IDE-подсветка) важен для команды.

Stapel-builder: альтернатива с YAML-инструкциями вместо Dockerfile

1
2
3
4
5
6
7
8
image: backend
from: golang:1.26
git:
  - add: /api
    to: /app
shell:
  install:
    - cd /app && go build -o /app/bin/server .

Stapel — собственный builder werf, где вместо текста Dockerfile вы описываете шаги как YAML-структуры (shell/ansible-инструкции по user-стадиям — детально в M3 уроке 3). Главное преимущество, которого нет у обычного Dockerfile: более тонкий контроль кэша — можно указать, какие именно файлы Git-репозитория триггерят пересборку конкретной стадии (git.stageDependencies), а не всего образа целиком, как это происходит с COPY в обычном Dockerfile (любое изменение любого скопированного файла инвалидирует весь последующий кэш слоёв Docker-инструкций).

Когда предпочесть какой билдер

КритерийDockerfile-builderStapel-builder
Готовый существующий Dockerfileиспользовать как естьнужно переписать под YAML
Тонкий контроль кэша по путям Gitнет (только порядок COPY)да, git.stageDependencies per-стадия
Множество вспомогательных образов и импортов между нимичерез dependencies+multi-stageнативная поддержка import (M3 урок 5)
Знакомство команды с синтаксисомDockerfile — общий стандарт индустрииYAML, специфичный для werf
Ansible-инструкции вместо shellнетда (ansible: вместо shell:)

Для Application-пакетов курса допустимы оба подхода, и часто один и тот же werf.yaml сочетает оба билдера в разных image:-блоках (например, backend через Stapel с тонким кэшем, frontend через готовый Dockerfile от команды фронтенда) — именно такой гибридный подход мы применим для Vikunja.

Частые ошибки и подводные камни

  • Забыть context: при Dockerfile-builder, если Dockerfile не в корне репозитория. По умолчанию context — корень проекта; для монорепозитория с api//frontend/ в разных директориях нужно явно указывать context: для каждого image:-блока.
  • Путать configVersion (версия схемы werf.yaml) с версией самого Application-пакета. configVersion: 1 — это версия формата конфигурации werf, а версия пакета задаётся отдельно через package.yaml (детали — M5 урок 2) и Git-тег.
  • Пытаться смешать Dockerfile-builder и Stapel-builder в одном image:-блоке. Каждый image:-блок — это либо dockerfile:, либо from:+shell:/ansible:, но не оба одновременно; смешение допустимо только между разными image:-блоками одного файла.

Практика в кластере

Практики в кластере в этом уроке нет — фокус на конфигурации сборки, первый деплой собранного Vikunja через Nelm — с M4.

Практика разработки

🧩 Практика урока: Vikunja · лицензия AGPL-3.0
  1. В корне клонированного в M3.01 репозитория Vikunja создайте werf.yaml:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
project: vikunja
configVersion: 1
---
image: backend
dockerfile: Dockerfile
context: api/
target: production
---
image: frontend
from: node:24-alpine
git:
  - add: /frontend
    to: /app
shell:
  install:
    - cd /app && npm ci
  setup:
    - cd /app && npm run build
  1. Проверьте синтаксическую валидность конфигурации без реальной сборки:
1
2
werf config render          # печатает итоговый YAML после обработки Go-шаблонов
werf config list             # список всех image:-блоков конфигурации
  1. Соберите только backend-образ, указав конкретное имя:
1
werf build backend

Обратите внимание на вывод — werf показывает прогресс по стадиям (для Dockerfile-builder — по инструкциям Dockerfile, каждая со своим статусом кэша: [USING CACHE] или пересобрана).

Шпаргалка команд урока

1
2
3
werf config render
werf config list
werf build [<image-name>]

Полная шпаргалка werf: 📋 werf — будет пополняться в каждом уроке M3/M8.

Вопросы для самопроверки

Что определяет target: в Dockerfile-builder werf.yaml?

target: позволяет держать несколько логических образов в одном multi-stage Dockerfile и выбирать нужную стадию per image:-блок.

Источник: Werf — документация

В чём главное преимущество Stapel-builder перед обычным Dockerfile для контроля кэша?

В обычном Dockerfile любое изменение скопированного файла инвалидирует весь последующий кэш; Stapel позволяет точечно связать маски файлов с конкретными стадиями.

Источник: Werf — документация

Можно ли в одном werf.yaml сочетать Dockerfile-builder для одного image: и Stapel-builder для другого?

Ограничение на единый билдер действует только внутри одного image:-блока; разные image:-блоки одного файла могут использовать разные билдеры.

Источник: Werf — документация — множественные image:-документы

Что произойдёт, если не указать context: для Dockerfile-builder в монорепозитории?

Без явного context: werf использует корень проекта, что для монорепозитория с несколькими компонентами в разных директориях обычно неверно.

Источник: Werf — документация

Что означает configVersion в werf.yaml?

configVersion описывает версию схемы конфигурационного файла werf, версия пакета задаётся отдельно (package.yaml + Git-тег, M5 урок 2).

Источник: Werf — документация

Рекомендуемая литература

Официальная документация

Статьи и блоги

Книги

  • Nigel Poulton. Docker Deep Dive. 2023. ISBN 978-1916585206.

Связанные материалы