Пользовательские стадии
shell:
  beforeInstall:
  - <bash command>
  install:
  - <bash command>
  beforeSetup:
  - <bash command>
  setup:
  - <bash command>
  cacheVersion: <arbitrary string>
  beforeInstallCacheVersion: <arbitrary string>
  installCacheVersion: <arbitrary string>
  beforeSetupCacheVersion: <arbitrary string>
  setupCacheVersion: <arbitrary string>
ansible:
  beforeInstall:
  - <task>
  install:
  - <task>
  beforeSetup:
  - <task>
  setup:
  - <task>
  cacheVersion: <arbitrary string>
  beforeInstallCacheVersion: <arbitrary string>
  installCacheVersion: <arbitrary string>
  beforeSetupCacheVersion: <arbitrary string>
  setupCacheVersion: <arbitrary string>
Запуск инструкций сборки при изменениях в git-репозитории Запуск инструкций сборки при изменениях в git-репозитории

Пользовательские стадии

Пользовательские стадии — это стадии со сборочными инструкциями из конфигурации. Другими словами — это стадии, конфигурируемые пользователем (существуют также служебные стадии, которые пользователь конфигурировать не может). В настоящее время существует два вида сборочных инструкций: shell и ansible.

werf поддерживает четыре пользовательские стадии, которые выполняются последовательно в следующем порядке: beforeInstall, install, beforeSetup и setup. В результате выполнения инструкций пользовательской стадии создается один Docker-слой. Т.е. по одному слою на каждую стадию вне зависимости от количества инструкций.

Использование пользовательских стадий

werf позволяет определять до четырех пользовательских стадий с инструкциями сборки. На содержание самих инструкций сборки werf не накладывает каких-либо ограничений, т.е. вы можете указывать все те же инструкции, которые указывали в Dockerfile в директиве RUN. Однако важно не просто перенести инструкции из Dockerfile, а правильно разбить их на пользовательские стадии. Исходя из опыта работы с реальными приложениями, мы пришли к заключению, что сборка большинства приложений проходит следующие этапы:

  • установка системных пакетов;
  • установка системных зависимостей;
  • установка зависимостей приложения;
  • настройка системных пакетов;
  • настройка приложения.

Какая может быть наилучшая стратегия выполнения этих этапов?

Первая мысль — лучше выполнять эти этапы последовательно, кэшируя промежуточные результаты, а с другой стороны – разбивать инструкции по файловым зависимостям.

Подход с пользовательскими стадиями предлагает следующую стратегию:

  • использовать стадию beforeInstall для инсталляции системных пакетов;
  • использовать стадию install для инсталляции системных зависимостей и зависимостей приложения;
  • использовать стадию beforeSetup для настройки системных параметров и установки приложения;
  • использовать стадию setup для настройки приложения.
Как работает сборка стадий stapel

При сборке стадии предполагается, что инструкции стадии будут запускаться в контейнере, основанном на предыдущей собранной стадии или на базовом образе. Такой контейнер будет упоминаться далее как сборочный контейнер.

Перед запуском сборочного контейнера werf подготавливает набор инструкций, который зависит от типа стадии и содержит как служебные команды werf, так и пользовательские, указанные в конфигурации werf.yaml. Например, среди служебных команд может быть добавление файлов, наложение патчей, запуск ansible заданий и т.п.

Stapel-сборщик использует свой набор инструментов и библиотек и никак не зависит от базового образа. При запуске сборочного контейнера werf монтирует всё необходимое из специального служебного образа registry.werf.io/werf/stapel.

В сборочный контейнер пробрасывается сокет SSH-агента с хоста, а также могут использоваться пользовательские маунты.

Также стоит отметить, что при сборке werf игнорирует некоторые параметры манифеста базового образа, заменяя их определёнными значениями:

  • --user=0:0;
  • --workdir=/;
  • --entrypoint=/.werf/stapel/embedded/bin/bash.

В итоге запуск сборочного контейнера произвольной стадии можно представить следующим образом:

docker run \
  --volume=/tmp/ssh-ln8yCMlFLZob/agent.17554:/.werf/tmp/ssh-auth-sock \
  --volumes-from=stapel_0.6.1 \
  --env=SSH_AUTH_SOCK=/.werf/tmp/ssh-auth-sock \
  --user=0:0 \
  --workdir=/ \
  --entrypoint=/.werf/stapel/embedded/bin/bash \
  sha256:d6e46aa2470df1d32034c6707c8041158b652f38d2a9ae3d7ad7e7532d22ebe0 \
  -ec eval $(echo c2V0IC14 | /.werf/stapel/embedded/bin/base64 --decode)

beforeInstall

shell:
  beforeInstall:
    - apt update -q
    - apt install -y curl mysql-client libmysqlclient-dev g++ build-essential libcurl4
  beforeInstallCacheVersion: "1"

Данная стадия предназначена для выполнения инструкций перед установкой приложения. Этот этап предназначен для системных приложений, которые редко изменяются, но требуют много времени для установки. Примером таких приложений могут быть языковые пакеты или инструменты сборки, такие как Composer, Java, Gradle и т.д. Также сюда можно добавлять инструкции настройки системы, которые редко изменяются. Например, языковые настройки, настройки часового пояса, добавление пользователей и групп.

Поскольку эти компоненты меняются редко, они будут кэшироваться в рамках стадии beforeInstall на длительный период.

beforeInstallCacheVersion: <string> — опциональная директива, которая позволяет инвалидировать сборочный кеш данной стадии детерминированным способом через Git.

install

shell:
  install:
    - bundle install
    - npm ci
  installCacheVersion: "1"

Данная стадия предназначена для установки приложения и его зависимостей, а также выполнения базовых настроек.

На данной стадии появляется доступ к исходному коду используемых Git-репозиториев (директива git) и появляется возможность установки зависимостей на основе manifest-файлов с использованием таких инструментов, как Composer, Gradle, npm и т.д. Поскольку сборка стадии зависит от manifest-файла, для достижения наилучшего результата важно добавить зависимость от изменений в manifest-файлах репозитория для этой стадии. Например, если в проекте используется Composer, то добавление файла composer.lock в зависимости стадии beforeInstall позволит пересобирать стадию при изменении файла composer.lock.

installCacheVersion: <string> — опциональная директива, которая позволяет инвалидировать сборочный кеш данной стадии детерминированным способом через Git.

beforeSetup

shell:
  beforeSetup:
    - rake assets:precompile
  beforeSetupCacheVersion: "1"

Данная стадия предназначена для подготовки приложения перед настройкой.

На данной стадии рекомендуется выполнять разного рода компиляцию и обработку. Например, компиляция jar-файлов, бинарных файлов, файлов библиотек, создание ассетов web-приложений, минификация, шифрование и т.п. Перечисленные операции, как правило, зависят от изменений в исходном коде, и на данной стадии также важно определить достаточные зависимости от изменений в репозитории. Логично, что стадия будет зависеть от большего числа файлов в репозитории, чем на предыдущей стадии, и, соответственно, ее пересборка будет выполняться чаще.

При правильно определенных зависимостях изменения в коде приложения должны приводить к пересборке стадии beforeSetup, а изменение manifest-файла к стадии install и последующих стадий.

beforeSetupCacheVersion: <string> — опциональная директива, которая позволяет инвалидировать сборочный кеш данной стадии детерминированным способом через Git.

setup

shell:
  setup:
    - npm run build
  setupCacheVersion: "1"

Данная стадия предназначена для настройки приложения.

Обычно на данной стадии выполняется копирование файлов конфигурации (например, в каталог /etc), создание файлов текущей версии приложения и т.д. Такого рода операции не должны быть затратными по времени, т.к. они, скорее всего, будут выполняться в большинстве сборок.

setupCacheVersion: <string> — опциональная директива, которая позволяет инвалидировать сборочный кеш данной стадии детерминированным способом через Git.

Пользовательская стратегия

Несмотря на изложенную четкую стратегию шаблона пользовательских стадий и функции каждой стадии, по сути для пользователя нет никаких ограничений. Предложенное использование стадий является лишь рекомендацией, которая основана на нашем опыте с реальными приложениями. Вы можете использовать только одну пользовательскую стадию, либо определить свою стратегию группировки инструкций, чтобы получить преимущества кэширования и зависимостей от изменений в Git-репозиториях с учетом особенностей сборки вашего приложения.

Синтаксис

Пользовательские стадии и инструкции сборки определяются внутри двух взаимоисключающих директив — shell и ansible. Каждый образ может собираться либо используя сборочные инструкции shell, либо задачи ansible, описанные в соответствующих директивах.

В каждой директиве можно описывать инструкции для пользовательских стадий, соответственно:

  • beforeInstall;
  • install;
  • beforeSetup;
  • setup.

Также можно указывать директивы версий кэша (cacheVersion), которые по сути являются частью дайджеста каждой пользовательской стадии. Более подробно в соответствующем разделе.

Shell

Синтаксис описания пользовательских стадий при использовании сборочных инструкций shell:

shell:
  beforeInstall:
  - <bash_command 1>
  - <bash_command 2>
  # ...
  - <bash_command N>
  install:
  - bash command
  # ...
  beforeSetup:
  - bash command
  # ...
  setup:
  - bash command
  # ...
  cacheVersion: <version>
  beforeInstallCacheVersion: <version>
  installCacheVersion: <version>
  beforeSetupCacheVersion: <version>
  setupCacheVersion: <version>

Сборочные инструкции shell — это массив Bash-команд для соответствующей пользовательской стадии. Все команды одной стадии выполняются как одна инструкция RUN в Dockerfile, т.е. в результате создается один слой на каждую пользовательскую стадию.

werf при сборке использует собственный исполняемый файл bash, и вам не нужно отдельно добавлять его в образ (или базовый образ) при сборке.

Пример описания стадии beforeInstall, содержащей команды apt-get update и apt-get install:

beforeInstall:
- apt-get update
- apt-get install -y build-essential g++ libcurl4

Исполняемый файл bash находится внутри Docker-тома stapel. Подробнее про эту концепцию можно узнать в этой статье (упоминаемый в статье dappdeps был переименован в stapel, но принцип сохранился)

Ansible

Синтаксис описания пользовательских стадий при использовании сборочных инструкций ansible:

ansible:
  beforeInstall:
  - <ansible task 1>
  - <ansible task 2>
  # ...
  - <ansible task N>
  install:
  - ansible task
  # ...
  beforeSetup:
  - ansible task
  # ...
  setup:
  - ansible task
  # ...
  cacheVersion: <version>
  beforeInstallCacheVersion: <version>
  installCacheVersion: <version>
  beforeSetupCacheVersion: <version>
  setupCacheVersion: <version>

Примечание: синтаксис ansible не доступен для использования при использовании сборочного бекэнда Buildah.

Ansible config и stage playbook

Сборочные инструкции ansible — это массив Ansible-заданий для соответствующей пользовательской стадии.

Сгенерированный ansible.cfg содержит настройки для Ansible:

  • использование локального транспорта (transport = local);
  • подключение callback плагина werf для удобного логирования (stdout_callback = werf);
  • включение режима цвета (force_color = 1);
  • установка использования sudo для повышения привилегий (чтобы не было необходимости использовать become в Ansible-заданиях).

Сгенерированный playbook.yml — playbook, содержащий все задания соответствующей пользовательской стадии. Пример werf.yaml с описанием стадии install:

ansible:
  install:
  - debug: msg='Start install'
  - file: path=/etc mode=0777
  - copy:
      src: /bin/sh
      dest: /bin/sh.orig
  - apk:
      name: curl
      update_cache: yes
  # ...

Исполняемые файлы и библиотеки ansible и python находятся внутри Docker-тома stapel. Подробнее про эту концепцию можно узнать в этой статье (упоминаемый в статье dappdeps был переименован в stapel, но принцип сохранился)

Поддерживаемые модули

Одной из концепций, которую использует werf, является идемпотентность сборки. Это значит, что если «ничего не изменилось», то werf при повторном и последующих запусках сборки должен создавать бинарно идентичные образы. В werf эта задача решается с помощью подсчета дайджестов стадий.

Многие модули Ansible не являются идемпотентными, т.е. они могут давать разный результат запусков при неизменных входных параметрах. Это, конечно, не дает возможность корректно высчитывать дайджест стадии, чтобы определять реальную необходимость её пересборки из-за изменений. Это привело к тому, что список поддерживаемых модулей был ограничен.

На текущий момент, список поддерживаемых модулей Ansible следующий:

  • Command modules: command, shell, raw, script.
  • Crypto modules: openssl_certificate и другие.
  • Files modules: acl, archive, copy, stat, tempfile и другие.
  • Net Tools Modules: get_url, slurp, uri.
  • Packaging/Language modules: composer, gem, npm, pip и другие.
  • Packaging/OS modules: apt, apk, yum и другие.
  • System modules: user, group, getent, locale_gen, timezone, cron и другие.
  • Utilities modules: assert, debug, set_fact, wait_for.

При указании в конфигурации сборки модуля, отсутствующего в приведенном списке, сборка прервется с ошибкой. Не стесняйтесь сообщать нам, если вы считаете что какой-либо модуль должен быть включен в список поддерживаемых.

Копирование файлов

Предпочтительный способ копирования файлов в образ — использование git mapping. werf не может определять изменения в копируемых файлах при использовании модуля copy. Единственный вариант копирования внешнего файла в образ на текущий момент — использовать метод .Files.Get Go-шаблона. Данный метод возвращает содержимое файла как строку, что дает возможность использовать содержимое как часть пользовательской стадии. Таким образом, при изменении содержимого файла изменится дайджест соответствующей стадии, что приведет к пересборке всей стадии.

Пример копирования файла nginx.conf в образ:

ansible:
  install:
  - copy:
      content: |
{{ .Files.Get "/conf/etc/nginx.conf" | indent 8}}
      dest: /etc/nginx/nginx.conf

В результате получится подобный playbook.yml:

- hosts: all
  gather_facts: no
  tasks:
    install:
    - copy:
        content: |
          http {
            sendfile on;
            tcp_nopush on;
            tcp_nodelay on;
            # ...

Шаблоны Jinja

В Ansible реализована поддержка шаблонов Jinja в playbook’ах. Однако у Go-шаблонов и Jinja-шаблонов одинаковый разделитель: {{ и }}. Чтобы использовать Jinja-шаблоны в конфигурации werf, их нужно экранировать. Для этого есть два варианта: экранировать только {{, либо экранировать все выражение шаблона Jinja.

Например, у вас есть следующая задача Ansible:

- copy:
    src: {{item}}
    dest: /etc/nginx
    with_files:
    - /app/conf/etc/nginx.conf
    - /app/conf/etc/server.conf

Тогда, выражение Jinja-шаблона {{item}} должно быть экранировано:

# Экранируем только {{.
src: {{"{{"}} item }}

либо

# Экранируем все выражение.
src: {{`{{item}}`}}

Проблемы с Ansible

  • Live-вывод реализован только для модулей raw и command. Остальные модули отображают вывод каналов stdout и stderr после выполнения, что приводит к задержкам и скачкообразному выводу.
  • Модуль apt подвисает на некоторых версиях Debian и Ubuntu. Проявляется также на наследуемых образах (issue #645).

Переменные окружения сборочного контейнера

Вы можете использовать сервисные переменные окружения, которые доступны в сборочном контейнере, и, соответственно, доступны в инструкциях сборки. Их использование не приведёт к изменению инструкций сборки и вытекающим из этого пересборкам, даже если сами значения сервисных переменных меняются.

Доступны следующие переменные:

  • WERF_COMMIT_HASH. Пример значения: cda9d17265d174c62424e8f7b5e5640bf749c565.
  • WERF_COMMIT_TIME_HUMAN. Пример значения: 2022-01-24 17:26:19 +0300 +0300.
  • WERF_COMMIT_TIME_UNIX. Пример значения: 1643034379.

Пример использования:

shell:
  install:
  - echo "Commands on the Install stage for $WERF_COMMIT_HASH"

В примере выше хэш текущего коммита будет подставлен в команду echo ..., но произойдет это в самый последний момент — на этапе выполнения инструкций shell’ом. Таким образом пересборок слоя install на каждом коммите происходить не будет.

Зависимости пользовательских стадий

Одна из особенностей werf — возможность определять зависимости, при которых происходит пересборка стадии. Сборка стадий выполняется последовательно, одна за другой, и для каждой стадии высчитывается дайджест стадии. У дайджестов есть ряд зависимостей, при изменении которых дайджест стадии меняется, что служит для werf сигналом для пересборки стадии с измененным дайджестом. Поскольку каждая следующая стадия имеет зависимость, в том числе и от предыдущей стадии согласно конвейеру стадий, при изменении дайджеста какой-либо стадии произойдет пересборка и стадии с изменённым дайджестом и всех последующих стадий.

Дайджест пользовательских стадий и, соответственно, пересборка пользовательских стадий зависят от изменений:

  • в инструкциях сборки;
  • в директивах семейства cacheVersion;
  • в Git-репозитории (или Git-репозиториях);
  • в файлах, импортируемых из образов.

Первые три описанных варианта зависимостей рассматриваются подробно далее.

Зависимость от изменений в инструкциях сборки

Дайджест пользовательской стадии зависит от итогового текста инструкций, т.е. после применения шаблонизатора. Любые изменения в тексте инструкций с учетом применения шаблонизатора Go или Jinja (в случае Ansible) в пользовательской стадии приводят к пересборке стадии. Например, вы используете следующие shell-инструкции:

shell:
  beforeInstall:
  - echo "Commands on the Before Install stage"
  install:
  - echo "Commands on the Install stage"
  beforeSetup:
  - echo "Commands on the Before Setup stage"
  setup:
  - echo "Commands on the Setup stage"

При первой сборке этого образа будут выполнены инструкции всех четырех пользовательских стадий. В данной конфигурации нет git mapping, так что последующие сборки не приведут к повторному выполнению инструкций — дайджест пользовательских стадий не изменилась, сборочный кэш содержит актуальную информацию.

Изменим инструкцию сборки для стадии install:

shell:
  beforeInstall:
  - echo "Commands on the Before Install stage"
  install:
  - echo "Commands on the Install stage"
  - echo "Installing ..."
  beforeSetup:
  - echo "Commands on the Before Setup stage"
  setup:
  - echo "Commands on the Setup stage"

Дайджест стадии install изменилась, и запуск werf для сборки приведет к выполнению всех инструкций стадии install и инструкций последующих стадий, т.е. beforeSetup и setup.

Дайджест стадии может меняться также из-за использования переменных окружения и Go-шаблонов. Если не уделять этому достаточное внимание при написании конфигурации, можно столкнуться с неожиданными пересборками стадий. Например:

shell:
  beforeInstall:
  - echo "Commands on the Before Install stage for {{ env "CI_COMMIT_SHA” }}"
  install:
  - echo "Commands on the Install stage"
  # ...

Первая сборка высчитает дайджест стадии beforeInstall на основе команды (хэш коммита, конечно, будет другой):

echo "Commands on the Before Install stage for 0a8463e2ed7e7f1aa015f55a8e8730752206311b"

После очередного коммита при сборке дайджест стадии beforeInstall уже будет другой (с другим хешем коммита):

echo "Commands on the Before Install stage for 36e907f8b6a639bd99b4ea812dae7a290e84df27"

Соответственно, используя переменную CI_COMMIT_SHA дайджест стадии beforeInstall будет меняться после каждого коммита, что будет приводить к пересборке.

Зависимость от изменений в Git-репозитории

Зависимость от изменений в git-репозитории

Как описывалось в статье про git mapping, существуют специальные стадии gitArchive и gitLatestPatch. Стадия gitArchive выполняется после пользовательской стадии beforeInstall, а стадия gitLatestPatch после пользовательской стадии setup, если в локальном Git-репозитории есть изменения. Таким образом, чтобы выполнить сборку с последней версией исходного кода, можно пересобрать стадию beforeInstall, изменив значение директивы cacheVersion либо изменив сами инструкции стадии beforeInstall.

Пользовательские стадии install, beforeSetup и setup также могут зависеть от изменений в Git-репозитории. В этом случае (если такая зависимость определена) Git-патч применяется перед выполнением инструкций пользовательской стадии, чтобы сборочные инструкции выполнялись с актуальной версией кода приложения.

Во время процесса сборки исходный код обновляется только в рамках одной стадии, последующие стадии, зависящие последовательно друг от друга, будут использовать также обновленную версию файлов. Первая сборка добавляет файлы из Git-репозитория на стадии gitArchive. Все последующие сборки обновляют файлы на стадии gitCache, gitLatestPatch или на одной из следующих пользовательских стадий: install, beforeSetup, setup.

Пример этого этапа (фаза подсчета дайджестов, calculating digests): git files actualized on specific stage

Зависимость пользовательской стадии от изменений в Git-репозитории указывается с помощью параметра git.stageDependencies. Синтаксис:

git:
- ...
  stageDependencies:
    install:
    - <mask 1>
    # ...
    - <mask N>
    beforeSetup:
    - <mask>
    # ...
    setup:
    - <mask>

У параметра git.stageDependencies возможно указывать 3 ключа: install, beforeSetup и setup. Значение каждого ключа — массив масок файлов, относящихся к соответствующей стадии. Соответствующая пользовательская стадия пересобирается, если в Git-репозитории происходят изменения подпадающие под указанную маску.

Для каждой пользовательской стадии werf создает список подпадающих под маску файлов и вычисляет контрольную сумму каждого файла с учетом его аттрибутов и содержимого. Эти контрольные суммы являются частью дайджеста стадии, поэтому любое изменение файлов в репозитории, подпадающее под маску, приводит к изменениям дайджеста стадии. К этим изменениям относятся: изменение атрибутов файла, изменение содержимого файла, добавление или удаление подпадающего под маску файла и т.п.

При применении маски, указанной в git.stageDependencies, учитываются значения параметров git.includePaths и git.excludePaths (смотри подробнее про них в соответствующем разделе). werf считает подпадающими под маску только файлы, удовлетворяющие фильтру includePaths и подпадающие под маску stageDependencies. Аналогично werf считает подпадающими под маску только файлы, не удовлетворяющие фильтру excludePaths и не подпадающие под маску stageDependencies.

Правила описания маски в параметре stageDependencies аналогичны описанию параметров includePaths и excludePaths. Маска определяет шаблон для файлов и путей и может содержать следующие шаблоны:

  • * — удовлетворяет любому файлу. Шаблон включает . и исключает /.
  • ** — удовлетворяет директории со всем ее содержимым, рекурсивно.
  • ? — удовлетворяет любому одному символу в имени файла (аналогично regexp-шаблону /.{1}/).
  • [set] — удовлетворяет любому символу из указанного набора символов. Аналогично использованию в regexp-шаблонах, включая указание диапазонов типа [^a-z].
  • \ — экранирует следующий символ.

Маска, которая начинается с шаблона * или **, должна быть взята в одинарные или двойные кавычки в werf.yaml:

# * в начале маски, используем двойные кавычки.
- "*.rb"
# Одинарные также работают.
- '**/*'
# Нет * в начале, можно не использовать кавычки.
- src/**/*.js

Факт изменения файлов в Git-репозитории werf определяет, подсчитывая их контрольные суммы. Для пользовательской стадии и для каждой маски применяется следующий алгоритм:

  • werf создает список всех файлов согласно пути, определенному в параметре add, и применяет фильтры excludePaths и includePaths;
  • К каждому файлу с учетом его пути применяется маска, согласно правилам применения шаблонов;
  • Если под маску подпадает каталог, то все содержимое этого каталога считается подпадающей под маску рекурсивно;
  • У получившегося списка файлов werf подсчитывает контрольные суммы с учетом аттрибутов файлов и их содержимого.

Контрольные суммы подсчитываются вначале сборочного процесса перед запуском какой-либо стадии.

Пример:

image: app
git:
- add: /src
  to: /app
  stageDependencies:
    beforeSetup:
    - "*"
shell:
  install:
  - echo "install stage"
  beforeSetup:
  - echo "beforeSetup stage"
  setup:
  - echo "setup stage"

В приведенном файле конфигурации werf.yaml указан git mapping, согласно которому содержимое каталога /src локального Git-репозитория копируется в каталог /app собираемого образа. Во время первой сборки файлы кэшируются в стадии gitArchive и выполняются сборочные инструкции стадий install, beforeSetup и setup.

Сборка следующего коммита, в котором будут только изменения файлов за пределами каталога /src, не приведет к выполнению инструкций каких-либо стадий. Если коммит будет содержать изменение внутри каталога /src, контрольные суммы файлов подпадающих под маску изменятся, werf применит Git-патч и пересоберёт все пользовательские стадии, начиная со стадии beforeSetup, а именно — beforeSetup и setup. Применение Git-патча будет выполнено один раз на стадии beforeSetup.

Отключение обновления исходников (стадии gitCache и gitLatestPatch)

Параметр disableGitAfterPatch позволяет зафиксировать исходный код в образе на этапе сборки артефакта и предотвратить его обновление при последующих сборках.

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

Таким образом:

  • если использовать git.stageDepedencies, то пересборка, выполнение команд с актуальными исходниками, будет выполняться при соответствующих изменениях;
  • если не использовать git.stageDepedencies, то пересборок не будет.

Зависимость от значения CacheVersion

Существуют ситуации, когда необходимо принудительно пересобрать все или какую-то конкретную пользовательскую стадию. Этого можно достичь, изменяя параметры cacheVersion или <user stage name>CacheVersion.

Дайджест пользовательской стадии install зависит от значения параметра installCacheVersion. Чтобы пересобрать пользовательскую стадию install (и все последующие стадии), можно изменить значение параметра installCacheVersion.

Обратите внимание, что параметры cacheVersion и beforeInstallCacheVersion имеют одинаковый эффект — при изменении этих параметров возникает пересборка стадии beforeInstall и всех последующих стадий.

Пример: общий образ для нескольких приложений

Вы можете определить образ, содержащий общие системные пакеты в отдельном файле werf.yaml. Изменение параметра cacheVersion может быть использовано для пересборки этого образа, чтобы обновить версии системных пакетов.

image: app
from: ubuntu:latest
shell:
  beforeInstallCacheVersion: 2
  beforeInstall:
  - apt update
  - apt install ...

Этот образ может быть использован как базовый образ для нескольких приложений (например, если образ с hub.docker.com не удовлетворяет вашим требованиям).

Пример использования внешних зависимостей

Параметры CacheVersion можно использовать совместно с шаблонами Go-шаблонизатора, чтобы определить зависимость пользовательской стадии от файлов, не находящихся в Git-репозитории.

image: app
from: ubuntu:latest
shell:
  installCacheVersion: {{.Files.Get "some-library-latest.tar.gz" | sha256sum}}
  install:
  - tar zxf some-library-latest.tar.gz
  - <build application>

Если использовать, например, скрипт загрузки файла some-library-latest.tar.gz и запускать werf для сборки уже после скачивания файла, то пересборка пользовательской стадии install (и всех последующих) будет происходить в случае, если скачан новый (измененный) файл.

Как использовать SSH-агент в сборочных инструкциях

При работе с удаленными Git-репозиториями используется UNIX-сокет SSH_AUTH_SOCK, который монтируется во все сборочные контейнеры. Таким образом, сборочные инструкции могут использовать SSH-агент через указанный UNIX-сокет.

ЗАМЕЧАНИЕ: Существует ограничение, из-за которого только пользователь root внутри сборочного контейнера имеет доступ к UNIX-сокету из переменной окружения SSH_AUTH_SOCK.

По умолчанию (без указания каких-либо параметров) werf пытается использовать SSH-agent, запущенный в системе, проверяя его доступность с помощью переменной окружения SSH_AUTH_SOCK.

В случае отсутствия в системе запущенного SSH-агента, werf пытается выступать в качестве SSH-клиента, для чего использует SSH-ключ пользователя по умолчанию, т.е. (~/.ssh/id_rsa|id_dsa). Если werf обнаруживает один из этих файлов, то выполняется запуск временного SSH-агента с добавлением найденных ключей.

Для использования только конкретных SSH-ключей необходимо указывать опцию запуска --ssh-key PRIVATE_KEY_FILE_PATH (может быть указана несколько раз, для указания нескольких SSH-ключей). В этом случае werf выполняет запуск временного SSH-агента и добавляет ему только указанные SSH-ключи.

Временный SSH-агент

werf может запускать временный SSH-агент для работы некоторых команд. Такой SSH-агент завершает работу при завершении работы соответствующей команды werf. В случае, если в системе есть запущенный SSH-агент, то запускаемый werf временный SSH-агент не конфликтует с запущенным в системе SSH-агентом.