В статье представлен (и продемонстрирован в коротких видеороликах) инструментарий, облегчающий разработку и отладку конфигураций с dapp — Open Source-утилитой, которую мы ежедневно используем при построении и сопровождении процессов CI/CD.
Обновлено 13 августа 2019 г.: в настоящее время проект dapp переименован в werf, его код переписан на Go, а документация значительно улучшена.
Примечание: Недавно была анонсирована поддержка синтаксиса YAML в dapp, об особенностях которого можно прочитать в этой публикации. По умолчанию все описанные далее инструменты будут справедливы как для него, так и для конфигурации в Ruby DSL (используется в предыдущих версиях dapp), а если это не так — указано отдельно.
Интроспекция стадий
Написание конфигурации на первых шагах усложняется отсутствием понимания того, что содержится в сборочном контейнере при выполнении инструкций.
Сборка приложения и артефакта состоит из набора связанных стадий. Каждая стадия собирается в сборочном контейнере, за основу которого берётся образ предыдущей стадии (базовый образ в случае стадии from
), а затем сохраняется в Docker-образ, кэш приложения. Все стадии выполняют определённую функцию и связаны с соответствующими директивами в конфигурации. Подробнее узнать про природу стадий, их функции, особенности и возможные состояния можно в документации.
В процессе сборки есть возможность получить доступ к определённой стадии, воспользовавшись опциями интроспекции. При интроспекции сборочный контейнер, среда, содержит служебный инструментарий и переменные окружения. Инструментарий представляется в виде набора утилит, который необходим на время сборки. Его добавление осуществляется монтированием директорий из служебных контейнеров наших дистрибутивов dappdeps
(в сборочном контейнере они доступны по пути /.dapp/deps
). Подробнее про идею этих дистрибутивов, процесс их сборки и состав можно почитать в статье про dappdeps.
При разработке интроспекция позволяет прийти к необходимому результату в сборочном контейнере, а затем перенести все шаги, инструкции, в конфигурацию соответствующей стадии. Такой подход может быть полезен, когда поставленная задача ясна, но шаги для её решения не очевидны и требуют экспериментов.
Процесс написания конфигурации в режиме интроспекции (на примере приложения symfony-demo) продемонстрирован в этом видео:
При отладке интроспекция позволяет посмотреть, почему сборка завершилась с ошибкой или результат не соответствует ожиданиям, проверить наличие зависимых файлов, состояние системы:
Наконец, при использовании интроспекции с Ansible-сборщиком (подробнее о поддержке Ansible в dapp см. в этой статье) появляется возможность отладки Ansible playbooks в сборочном контейнере с последующим переносом Ansible-задач в соответствующие стадии конфигурации:
При сборке поддерживаются следующие опции интроспекции:
# интроспекция до и после выполнения проблемного набора инструкций
dapp dimg build --introspect-error
dapp dimg build --introspect-before-error
# интроспекция собранной стадии STAGE
dapp dimg build --introspect-stage STAGE
dapp dimg build --introspect-artifact-stage STAGE
# интроспекция до выполнения инструкций стадии STAGE
dapp dimg build --introspect-before STAGE
dapp dimg build --introspect-artifact-before STAGE
Работа с локальным Git-репозиторием в dev-режиме
При разработке конфигурации в стандартном режиме сборки требуются формальные коммиты для того, чтобы dapp учитывал изменения в файлах при сборке. По аналогии с монтированием рабочей директории при сборке Dockerfile
хочется работать с текущим состоянием локального репозитория.
Эта концепция реализована в dev-режиме сборки: при сборке учитываются некоммитнутые изменения локального Git-репозитория, соответствующие конфигурации. Если быть точным, учитываются пути из следующих поддиректив git
: add
, includePaths
, excludePaths
, stageDependencies
.
При добавлении git-submodules и вложенных Git-репозиториев учитываются файлы .gitignore
на всех уровнях.
dapp dimg build --dev
# удаление dev-кэша стадий всех проектов
dapp dimg mrproper --improper-dev-mode-cache
Альтернативная схема кэширования с директивой asLayers
Стадии before_install
, install
, before_setup
, setup
зависят от соответствующих инструкций в конфигурации. Любое изменение инструкций приводит к пересборке соответствующей стадии со всеми инструкциями. Таким образом, при тяжеловесных, требовательных ко времени, инструкциях разработка может затянуться.
Добавим ко всему этому ситуацию, когда при выполнении команд сборка падает на одной из последних инструкций стадии. Помимо того, что сборку необходимо выполнять заново, нет возможности получить состояние среды до упавшей инструкции, проверить корректность выполнения предыдущих.
Для удобства разработки и отладки была введена директива asLayers
, которая указывается для конкретного приложения или его артефакта в конфигурации (dappfile.yaml
). При сборке инструкции кэшируются по отдельности, а пересборка осуществляется только при изменении их порядка.
При отсутствии директивы asLayers
(или в случае asLayers: false
) используется кэширование по умолчанию, т.е. один Docker-образ на все инструкции стадии, а если указать asLayers: true
, то включится новый режим кэширования — один Docker-образ на одну команду для shell или один task для Ansible.
Переключение между режимами сборки регулируется только директивой asLayers
— остальные инструкции конфигурации остаются неизменными. После того, как сборочные инструкции отлажены, asLayers
необходимо выключить. Видеодемонстрация использования asLayers
в dapp:
Директива asLayers
позволяет кэшировать инструкции по отдельности. При использовании опций интроспекции --introspect-error
и --introspect-before-error
пользователь может получить среду до или после выполнения проблемной инструкции.
Важные примечания:
- Новые возможности dapp реализуются только в YAML-конфигурации, поэтому
asLayers
уже не поддерживается в вариантах с Ruby DSL. - Важно не пользоваться этой инструкцией при штатной сборке образов: данный режим порождает избыточное количество Docker-образов и не рассчитан на инкрементальную сборку (т.к. увеличивается время ожидания и размер сборочного кэша).
Резюмируя
Поскольку dapp остаётся одним из главных инструментов в ежедневной работе наших DevOps-инженеров, мы имеем высокую мотивацию делать его по-настоящему удобным, удовлетворяя все их потребности. В то же время это Open Source-проект, и мы рады также видеть issues и от сторонних пользователей, сценарии работы которых могут отличаться от нашего опыта. Также готовы ответить на любые ваши вопросы по использованию dapp в комментариях к этой статье — welcome!
P.S.
Читайте также в нашем блоге:
- «werf — наш инструмент для CI/CD в Kubernetes (обзор и видео доклада)» (Дмитрий Столяров; 27 мая 2019 на DevOpsConf);
- «Дождались: поддержка YAML и Ansible (без коров) в dapp»;
- «Linux-дистрибутив from scratch для сборки Docker-образов — наш опыт с dappdeps»;
- «Сборка проектов с dapp. Часть 1: Java»;
- «Практика с dapp. Часть 1: Сборка простых приложений»;
- «Практика с dapp. Часть 2. Деплой Docker-образов в Kubernetes с помощью Helm»;
- «Собираем Docker-образы для CI/CD быстро и удобно вместе с dapp (обзор и видео доклада)».