- PVSM.RU - https://www.pvsm.ru -
На Хабре уже есть материалы про то, как настроить docker-контейнер для компиляции проекта. Например, Использование Docker для сборки и запуска проекта на C++ [1]. В этой статье, как и в предыдущей [2] будет рассмотрен вопрос сборки проекта, но здесь я бы хотел выйти за рамки туториала и рассмотреть глубже вопросы использования контейнеров в таких задачах, а так же построения инфраструктуры сборки с docker.
Для наглядности дальнейшего изложения необходимо привести описание некоторых компонент docker.
Docker image [3] это шаблон только для чтения с инструкциями по созданию контейнера. Для того, чтобы собрать image необходимо создать Dockerfile, в котором описываются все шаги сборки. Каждый такой шаг создает отдельный слой внутри image. Каждый последующий слой накладывается поверх всех предыдущих и содержит лишь изменения, которые необходимо внести в предшествующий слой.
Например, для Dockerfile:
FROM ubuntu:18.04
ADD app.sh /app
ENTRYPOINT /bin/bash /app/app.sh
docker-образ будет иметь следующую структуру:
Слои внутри image кешируются и могут быть переиспользованы, если никаких изменений не обнаружено. Если слой меняется(добавляется/удаляется) [4], то все последующие создаются с нуля. Для внесения изменений в образ контейнера (и соответственно в окружение запускаемого процесса) достаточно поправить Dockerfile и запустить сборку образа.
Docker контейнер [5] — это запускаемый экземпляр image. Его можно создать, запустить, остановить, удалить и пр. По умолчанию, контейнеры изолированы друг от друга и хост-системы. При старте контейнер запускает только команду, которая указана в ENTRYPOINT [6] или CMD [7], и останавливается при ее завершении.
При создании каждого контейнера добавляется новый слой поверх всех существующих. Он доступен для записи в текущем контейнере, и уничтожается вместе с контейнером. Все операции записи, создания новых файлов при работе контейнера применяются к этому слою, image всегда остается неизменным. Таким образом структура слоев созданного контейнера будет иметь вид:
При использовании команды docker run
каждый раз будет создаваться новый [8] контейнер, со своим слоем для записи. В задачах сборки это означает, что при каждом запуске будет создавать новое чистое окружение, которое никак не связано с предыдущими выполнениями. Список созданных контейнеров можно посмотреть, выполнив команду: docker container ls -a
.
Для наглядности кратко опишем процесс сборки приложения в контейнере, более подробно этот процесс описан в статье 1 [9] и статье 2 [10].
Схематично возможные шаги по сборке приложения в docker можно представить следующим образом:
Разберем показанные этапы:
docker run
. Монтируем в контейнер папку с исходниками и папку, куда будет скопирован результат сборки.Пример приведен в статье [11].
Так как здесь используется команда docker run
, то для каждого запуска будет создаваться отдельный контейнер со своим слоем для записи [12], поэтому временные файлы из предыдущих сборок не попадут в текущую. Необходимо не забывать чистить остановленные контейнеры.
Монтирование директории с исходниками облегчает отладку сборки. Но несет риски — можно собрать релиз из кода, который не прошел проверку на качество, или вообще не добавлен в систему контроля версий. Чтобы этого избежать, можно при каждой сборке клонировать git-репозиторий внутрь контейнера, как, например, в файле [13]:
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 может применяться некая конфигурация серверов (машин сборки, серверов с системой контроля версий и пр.). На практике мне приходилось сталкиваться со следующей инфраструктурой:
Здесь пользователь обращается к web-серверу, через который запускается сборка проекта на машинах с Ubuntu и Red Hat. Далее, на каждой машине выполняется клонирование git-репозитория с проектом во временную директорию и запускается сама сборка. Пользователь может скачать результирующие файлы с той же страницы, с которой и запускал весь процесс.
Такая сборка является повторяемой, потому что разработчики используют одно и то же окружение.
Из минусов — необходимо поддерживать целую инфраструктуру, администрировать несколько серверов, устранять баги в скриптах и web-приложении и пр.
Поддержка показанной выше инфраструктуры требует определенных затрат, как денежных, так и людских. В случае, если Ваша команда трудится над небольшим стартапом, или Вы являетесь единственным разработчиком, то можно воспользоваться контейнерами docker для реализации своей инфраструктуры сборки.
Рассмотрим тривиальный Qt проект, который собирается с помощью qmake — SimpleQtProject [14]. В папке docker [15] указанного проекта находится ряд файлов:
Данные файлы реализуют идею клонирования исходного кода внутрь контейнера.
Запускается вся сборка с помощью Makefile [18]. Он очень короткий и содержит достаточно комментариев. Его основа — это создание образа и запуск контейнера:
%: %.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,, $< ))
В этом этапе сборки создается образ контейнера с именем, состоящим из префикса simple-qt- и названия системы (для centos 7 это будет simple-qt-centos7). В качестве Dockerfile используется соответствующий файл с разрешением .docker. Далее запускается контейнер на основе созданного образа, и к нему монтируется папка для копирования артефактов сборки.
После запуска команды make
в директории docker [15], в папке docker/releases будут находится результаты сборки под несколько платформ.
Таким образом наша инфраструктура для сборки SimpleQtProject [14] будет выглядеть следующим образом:
Достоинства данной конфигурации:
Однако есть и существенный минус — сборка проекта потребует и сборки образа контейнера. При первом запуске это может занять продолжительное время. Но при повторных, особенно если Dockerfile не менялся, образ собирается с использованием кеша в разы быстрее.
Так же необходимо не забывать очищать остановленные контейнеры.
В заключении хотелось бы отметить, что docker является не единственной технологией контейнеризации. Но есть некоторые особенности, которые его выгодно отличают для задач сборки от того же LXC [20]:
docker run
мы получаем чистую среду, как если бы выполняли все в первый раз. Временные файлы между сборками не сохраняются. Автор: sqglobe
Источник [21]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/323040
Ссылки в тексте:
[1] Использование Docker для сборки и запуска проекта на C++: https://habr.com/ru/post/414109/
[2] предыдущей: https://habr.com/ru/post/456916/
[3] Docker image: https://docs.docker.com/engine/docker-overview/#images
[4] меняется(добавляется/удаляется): https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache.
[5] Docker контейнер: https://docs.docker.com/engine/docker-overview/#containers
[6] ENTRYPOINT: https://docs.docker.com/engine/reference/builder/#entrypoint
[7] CMD: https://docs.docker.com/engine/reference/builder/#cmd
[8] создаваться новый: https://docs.docker.com/engine/docker-overview/#example-docker-run-command
[9] статье 1: https://habr.com/ru/post/414109
[10] статье 2: https://habr.com/ru/post/456916
[11] статье: https://habr.com/ru/post/456916/#sobiraem-vse-vmeste
[12] слоем для записи: #konteyner
[13] в файле: https://github.com/sqglobe/SimpleQtProject/blob/master/docker/ubuntu-bionic.docker
[14] SimpleQtProject: https://github.com/sqglobe/SimpleQtProject
[15] docker: https://github.com/sqglobe/SimpleQtProject/tree/master/docker
[16] centos7.docker: https://github.com/sqglobe/SimpleQtProject/blob/master/docker/centos7.docker
[17] ubuntu-xenial.docker: https://github.com/sqglobe/SimpleQtProject/blob/master/docker/ubuntu-xenial.docker
[18] Makefile: https://github.com/sqglobe/SimpleQtProject/blob/master/docker/Makefile
[19] образа контейнера: https://habr.com/ru/post/457870/#image
[20] LXC: https://linuxcontainers.org/ru/lxc
[21] Источник: https://habr.com/ru/post/457870/?utm_campaign=457870&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.