Pull to refresh

Comments 10

На данный момент я использую Rocker и в целом там все круто, исключая вот это:


Когда окажется, что у процесса сборки есть много общих частей

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

Тема общих частей была упомянута в обзорном докладе про dapp https://habrahabr.ru/company/flant/blog/324274/. В данный момент dapp поддерживает два способа выполнения команд на пользовательских стадиях: shell (как в этой статье) и chef-рецепты. Chef-рецепты живут в кукбуке приложения и в dimod-ах — кукбуках-модулях, которые выносятся в отдельные репозитории.


Про поддержку chef можно посмотреть в документации
http://flant.github.io/dapp/chef_for_advanced_build.html
http://flant.github.io/dapp/chef_dimod_for_advanced_build.html


chef-server для сборки конечно же не нужен, используется chef-solo, который выполняет рецепты для каждой указанной стадии.

Печалька. А у вас не планируется, например, поддержка ansible-плейбуков?

YAML для описания и Ansible для команд сборки — две самые часто спрашиваемые опции ;) Хотим, планируем, т.к. сами решили уходить от chef.

Кстати, уже сделали :)

А можно как-то объяснить, зачем всё это нужно вообще?
Ну ок, решили собирать приложения не на хосте дженкинсом, а в контейнере.
Делаем отдельный образ с build environment, содержащий утилиты сборки без кода приложения.
На хосте тем же дженкинсом делаем чекаут и запускам образ сборки, примонтировав ему зачекаутенные исходники и папки с кешами (~/.m2 для мавена, node_modules для npm и т.п.)
Образ с приложением делаем банальным docker build в папке с артефактами, собранными системой сборки.


Хотим, чтобы npm install/composer install вызывался только при изменении соответствующих файлов, а не всегда? пишем простой Makefile, gnu make создавался специально для этой цели.

Я правда не хотел, но всё-таки получилось много буковок. Этот вопрос вообще достоин своей отдельной статьи.


А можно как-то объяснить, зачем всё это нужно вообще?

Если философски без конкретики, то dapp создавался, чтобы решить проблемы сборки, которые возникают в CI/CD системе с несколькими билд машинами и большим количеством приложений, когда поддерживать развесистые скрипты сборки в каждом репозитории становится совсем накладно и когда одно приложение это не один репозиторий.


Ну ок, решили собирать приложения не на хосте дженкинсом, а в контейнере.
Делаем отдельный образ с build environment, содержащий утилиты сборки без кода приложения.
На хосте тем же дженкинсом делаем чекаут и запускам образ сборки, примонтировав ему зачекаутенные исходники и папки с кешами (~/.m2 для мавена, node_modules для npm и т.п.)
Образ с приложением делаем банальным docker build в папке с артефактами, собранными системой сборки.

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


Второй вопрос про дженкинс. В целом ваш сценарий может быть организован в любой CI-системе как некий начальный, быстрый вариант. В перспективе всплывают такие минусы: сборка приложения будет происходить всегда, даже когда без этого можно обойтись. Взять например, java-приложение, которое надо собрать maven-ом и собрать статику webpack-ом. C dapp это можно разделить на две секции artifact и объявить, что сборка приложения зависит от изменений в /src/main/java/, а статику собирать только при изменениях в /src/main/webapp/.
Другой минус — docker build и все его недостатки, точнее осознанно введённые ограничения при разработке Dockerfile и docker build, которые в случае CI/CD являются недостатками: нельзя пушить слои кэширования во внешний регистри, нет поддержки модульности сборки (общие скрипты), нет зависимостей выполнения команд от изменений файлов в git (есть зависимость на копирование) и прочее.
И это мы пока говорим про сборку из одного репозитория. А если перед сборкой итогового образа нужно пособирать части приложения из других репозиториев? dapp умеет в зависимости стадий сборки от изменений во внешних репозиториях.


Хотим, чтобы npm install/composer install вызывался только при изменении соответствующих файлов, а не всегда? пишем простой Makefile, gnu make создавался специально для этой цели.

Ничего не имею против gnu make — проверенный временем инструмент. Однако агент на билд машине в общем случае не обязан хранить дерево репозитория и файлы от предыдущей сборки, чтобы gnu make смог бы правильно обнаружить изменения файлов после git pull. dapp вычисляет изменившиеся файлы по git истории, что в системе CI/CD надёжнее, чем проверять mtime файлов. Т.е. если агент имеет доступ к предыдущей сборке, то gnu make может и хватить.


Укажу ещё несколько моментов, почему применение dapp и описание сборки в Dappfile интереснее, чем скрипт сборки в дженкинс.
Первое — Dappfile можно разрабатывать и отлаживать на локальной машине, последующая сборка на билд-машине будет предсказуемой.
Второе — сборка разных версий приложения может отличаться и вариант со своим хитрым скриптом в дженкинс сложно поддерживать (а это нужно поддерживать, т.к. новая версия, которая требует новый скрипт сборки, на прод ещё не выкатилась, а хотфикс нужно прямо сейчас собрать старым скриптом).
Третье — стадии сборки и их кэш в виде нормальных образов позволяют попасть в проблемную стадию и посмотреть, что там пошло не так (--introspect* флаги http://flant.github.io/dapp/debug_for_advanced_build.html)

Я имел в виду сборку приложения типа


git clone myapp ; cd myapp # или git pull
docker run -it -v ./:/build  my-js-build-tools /build/build.sh
docker build -t myapp:42 .
docker push myapp:42

Получается, основное преимущество dapp — он умеет сохранять и восстанавливать предыдущее состояние сборки, чтобы делать инкрементальный билд вместо полного?


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

Если вам интересна концептуальная сторона этого дела (зачем мы всё это городим, почему тратим свои силы и время) в докладе distol всё по полочкам разложено: видео https://www.youtube.com/watch?v=8R5UDg29Vic, текст https://habrahabr.ru/company/flant/blog/324274/


docker run -it -v ./:/build my-js-build-tools /build/build.sh

Да, упрощенно artifact в Dappfile так и делает. Только для артефакта доступно 5 стадий, то есть 5 скриптов. Можно использовать внешние докер-образы, собранные разработчиками языков или фреймоворков (тот же golang:1.8), дополнив их своими сценариями (добавить пакетов, создать нужных директорий и т.п.) на стадиях before_install, install, которые после первой сборки надолго закэшируются. В случае же my-js-build-tools — получается, что у нас есть где-то отдельный образ с инструментами, сценарий сборки этого образа надо поддерживать, собирать отдельными заданиями, версионировать, при этом как-то указывать нужную версию для сборки конкретного коммита приложения (тут у вас кстати некое противоречие с вашим последним абзацем). Но инструменты они тоже «живые». Иногда оправдано держать сборку образа с инструментами отдельно, в большинстве же случаев сценарий сборки образа с инструментами в Dappfile получается выгоднее, т.к. всегда видно, чем будет собираться конкретный релиз приложения.


Получается, основное преимущество dapp — он умеет сохранять и восстанавливать предыдущее состояние сборки, чтобы делать инкрементальный билд вместо полного?

В каком-то смысле да, можно считать так, dapp умеет что-то похожее на инкрементальный билд в зависимости от изменений файлов в git. Но я бы не сказал, что это прям основное преимущество. Одно из — это да.


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

Разработчики go так не считают (и вот кто их только надоумил). В Dappfile можно указать коммит или ветку или тэг во внешнем репозитории, который нужно склонировать перед сборкой. Если ветка наросла, то запустится пересборка. В целом это не сложнее, чем указывать версию внешних библиотек в pom.xml или Composer.json.

Sign up to leave a comment.