Хабрахабр

Инфраструктура сборки проекта с docker

Например, Использование Docker для сборки и запуска проекта на C++. На Хабре уже есть материалы про то, как настроить docker-контейнер для компиляции проекта. В этой статье, как и в предыдущей будет рассмотрен вопрос сборки проекта, но здесь я бы хотел выйти за рамки туториала и рассмотреть глубже вопросы использования контейнеров в таких задачах, а так же построения инфраструктуры сборки с docker.

Немного о docker

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

Image

Для того, чтобы собрать image необходимо создать Dockerfile, в котором описываются все шаги сборки. Docker image это шаблон только для чтения с инструкциями по созданию контейнера. Каждый последующий слой накладывается поверх всех предыдущих и содержит лишь изменения, которые необходимо внести в предшествующий слой. Каждый такой шаг создает отдельный слой внутри image.

Например, для Dockerfile:

FROM ubuntu:18.04
ADD app.sh /app
ENTRYPOINT /bin/bash /app/app.sh

docker-образ будет иметь следующую структуру:

Если слой меняется(добавляется/удаляется), то все последующие создаются с нуля. Слои внутри image кешируются и могут быть переиспользованы, если никаких изменений не обнаружено. Для внесения изменений в образ контейнера (и соответственно в окружение запускаемого процесса) достаточно поправить Dockerfile и запустить сборку образа.

Контейнер

Его можно создать, запустить, остановить, удалить и пр. Docker контейнер — это запускаемый экземпляр image. При старте контейнер запускает только команду, которая указана в ENTRYPOINT или CMD, и останавливается при ее завершении. По умолчанию, контейнеры изолированы друг от друга и хост-системы.

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

В задачах сборки это означает, что при каждом запуске будет создавать новое чистое окружение, которое никак не связано с предыдущими выполнениями. При использовании команды docker run каждый раз будет создаваться новый контейнер, со своим слоем для записи. Список созданных контейнеров можно посмотреть, выполнив команду: docker container ls -a.

Собираем проект в контейнере

Для наглядности кратко опишем процесс сборки приложения в контейнере, более подробно этот процесс описан в статье 1 и статье 2.

Схематично возможные шаги по сборке приложения в docker можно представить следующим образом:

Разберем показанные этапы:

  1. Используем Dockerfile, который описывает окружение, команды для сборки и копирования результатов, и на его основе создаем образ контейнера.
  2. Применяем полученный образ для создания и запуска контейнера командой docker run. Монтируем в контейнер папку с исходниками и папку, куда будет скопирован результат сборки.
  3. После завершения работы контейнера артефакты сборки будут помещены в смонтрованную директорию.

Пример приведен в статье.

Необходимо не забывать чистить остановленные контейнеры. Так как здесь используется команда docker run, то для каждого запуска будет создаваться отдельный контейнер со своим слоем для записи, поэтому временные файлы из предыдущих сборок не попадут в текущую.

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

FROM ubuntu:bionic RUN apt-get update \ && apt-get install -y apt-utils RUN apt-get update \ && apt-get install -y make gcc g++ qt5-default git RUN mkdir -p /app/src WORKDIR /app/build # Собираем проект и копируем артефакты сборки
ENTRYPOINT git -C /app/src clone https://github.com/sqglobe/SimpleQtProject.git \ && qmake /app/src/SimpleQtProject/SimpleQtProject.pro \ && make \ && cp SimpleQtProject /app/res/SimpleQtProject-ubuntu-bionic

ENTRYPOINT выполняется всегда при запуске контейнера, а результат выполнения команды RUN может быть взят из кеша. Здесь клонирование выполняется в ENTRYPOINT, а не в инструкции RUN, по причине кеширования.

Инфраструктура для сборки

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

Далее, на каждой машине выполняется клонирование git-репозитория с проектом во временную директорию и запускается сама сборка. Здесь пользователь обращается к web-серверу, через который запускается сборка проекта на машинах с Ubuntu и Red Hat. Пользователь может скачать результирующие файлы с той же страницы, с которой и запускал весь процесс.

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

Из минусов — необходимо поддерживать целую инфраструктуру, администрировать несколько серверов, устранять баги в скриптах и web-приложении и пр.

Упрощаем с docker

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

В папке docker указанного проекта находится ряд файлов: Рассмотрим тривиальный Qt проект, который собирается с помощью qmake — SimpleQtProject.

  • centos7.docker — описывает контейнер для сборки проекта под CentOS 7;
  • ubuntu-bionic.docker — контейнер для сборки под Ubuntu 18.04;
  • ubuntu-xenial.docker — описывает контейнер для сборки под Ubuntu 16.04.

Данные файлы реализуют идею клонирования исходного кода внутрь контейнера.

Он очень короткий и содержит достаточно комментариев. Запускается вся сборка с помощью Makefile. Его основа — это создание образа и запуск контейнера:

%: %.docker docker build -t simple-qt-$(strip $(subst .docker,, $< )) --file $< . docker run --mount type=bind,source=$(RELEASE_DIR),target=/app/res simple-qt-$(strip $(subst .docker,, $< ))

В качестве Dockerfile используется соответствующий файл с разрешением .docker. В этом этапе сборки создается образ контейнера с именем, состоящим из префикса simple-qt- и названия системы (для centos 7 это будет simple-qt-centos7). Далее запускается контейнер на основе созданного образа, и к нему монтируется папка для копирования артефактов сборки.

После запуска команды make в директории docker, в папке docker/releases будут находится результаты сборки под несколько платформ.

Таким образом наша инфраструктура для сборки SimpleQtProject будет выглядеть следующим образом:

Достоинства данной конфигурации:

  1. Локальность. Разработчик собирает проект для нескольких платформ на своей локальной машине, это исключает необходимость содержать парк серверов, настраивать копирование артефактов между серверами по сети, отправку и обработку сетевых команд.
  2. Изоляция окружения. Контейнер обеспечивает полностью изолированную среду для сборки конкретного приложения. Есть возможность обеспечить сборку проектов с несовместимыми окружениями на одной машине (например таких, которые требуют различных версий одной и той же библиотеки).
  3. Версионирование. Поместив Dockerfile в git-репозиторий, можно отслеживать изменения в среде сборки с выходом новых релизов, откатываться к предыдущим версиям среды сборки и пр.
  4. Мобильность. При необходимости данная инфраструктура без особых проблем разворачивается на другом компьютере. Технология создания образа контейнера позволяет вносить изменения в сам образ очень легко — достаточно обновить Dockerfile и запустить сборку образа.
  5. Самодокументируемость. По сути, Dockerfile содержит шаги для развертывания окружения сборки. Поэтому, при необходимости развернуть такое окружение, но уже в обычной системе, можно воспользоваться командами из него же.
  6. Легковесность. Контейнер запускается в момент начала сборки и останавливается по ее завершению автоматически. Он не тратит процессорное время и оперативную память впустую.

При первом запуске это может занять продолжительное время. Однако есть и существенный минус — сборка проекта потребует и сборки образа контейнера. Но при повторных, особенно если Dockerfile не менялся, образ собирается с использованием кеша в разы быстрее.

Так же необходимо не забывать очищать остановленные контейнеры.

Заключение

Но есть некоторые особенности, которые его выгодно отличают для задач сборки от того же LXC: В заключении хотелось бы отметить, что docker является не единственной технологией контейнеризации.

  1. Создать контейнер можно используя текстовый Dockerfile. Это файл с простым синтаксисом, его можно добавить в репозиторий с проектом (как я всегда делаю) и держать постоянно под рукой.
  2. Каждый раз, запуская контейнер docker командой docker run мы получаем чистую среду, как если бы выполняли все в первый раз. Временные файлы между сборками не сохраняются.
  3. Контейнер запускет не целую операционную систему, а только необходимый процесс сборки.
Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»