ТЛ; др
- Код приложения требует шага сборки (получение зависимостей)
- нескольким контейнерам нужен один и тот же "встроенный" код
В: Какова хорошая стратегия / рабочий процесс для архивирования с помощью docker / docker-compose.
Long
Мы находимся в процессе стыковки приложения PHP с несколькими компонентами (контейнерами / службами), например,
- Рабочие узлы (процессы PHP поддерживаются через супервизор)
- Планировщик (управление рабочими и запуск повторяющихся задач в cron)
- PHP-FPM / Nginx (веб-интерфейс)
Сервисы определены в файле docker-compose.
Во время разработки мы монтируем код приложения через том из каталога на хосте в каждом контейнере,
так что мы видим изменения «немедленно» в каждом сервисе ( Пример ). Жизнь была хорошей.
Сейчас мы настраиваем среду CI / CD на основе Jenkins, которая должна создавать (+ тестировать) контейнеры и
нажмите на реестр позже. Поскольку "смонтировать с хоста" больше невозможно, мне сейчас интересно
что лучше всего сделать так, чтобы код приложения находился в каждом контейнере.
Две вещи в нашей настройке делают это imho особенно сложным:
- у нас есть несколько контейнеров, которым нужен один и тот же код приложения
- «Артефакт сборки» не является отдельным двоичным файлом с само контейнером (как, например, с go)
но "весь наш код + установленные зависимости" ==> "много файлов" (медленно ...)
- есть этап сборки, для которого требуется программное обеспечение, которое не требуется в конечном образе
Решение для «3». обычно: Используйте многоэтапные сборки. Мы делаем это. Но: все примеры там
похоже, предполагается, что встроенный код будет использоваться только в одном другом контейнере (что в нашем случае неверно, см. 1).
Что мы сейчас делаем
application-code/
.docker/
builder/
Dockerfile
php-fpm/
Dockerfile
docker-compose.yml
build.sh
index.php
- ввести дополнительный контейнер "builder", который "собирает" приложение
(получает «весь» код приложения в качестве контекста сборки; запускает «установку композитора»)
# ./builder/Dockerfile
COPY ./ /codebase
RUN cd /codebase && composer install
- «копировать» из этого компоновщика в каждый контейнер, для которого требуется код приложения, например, через
# ./php-fpm/Dockerfile
ARG APP_CODE_PATH="/var/www/current"
COPY --from=builder --chown=www-data /codebase ${APP_CODE_PATH}
- организован с помощью docker-compose
# ./docker-compose.yml
version: '3.7'
services:
builder-ci:
image: builder
build:
# ../ contains the "raw" application code
context: ../
dockerfile: ./.docker/builder/Dockerfile
php-fpm:
build:
context: .
dockerfile: ./php-fpm/Dockerfile
args:
- APP_CODE_PATH=/var/www/current
# build.sh
## build builder
docker-compose -f ./.docker/docker-compose.yml --project-directory ./.docker build builder
## build the rest
docker-compose -f ./.docker/docker-compose.yml --project-directory ./.docker build --parallel
Pro
- "меньших" изображения (в php-fpm не будет установлен композитор)
- код приложения создается только один раз, а затем копируется поверх
Contra
- контейнер конструктора не служит никакой другой цели, кроме как "встроенный" ==> не чувствует себя чистым
- сборка "строителя" должна быть сделана до того, как будет построен любой другой контейнер
- это означает, что у нас есть дополнительный
Альтернативы
- не используйте контейнер конструктора, но "включите" этап сборки, например, в контейнер "Планировщик"
с помощью многоэтапных сборок (поэтому в конечном изображении мы не получим композитора)
- get избавился от "builder" - но теперь все другие службы зависят от "Scheduler" ==> чувствует себя еще более грязным
- используйте том, чтобы поделиться кодом
- нам не нужно «копировать» файлы в изображениях, мы можем просто «смонтировать том» ==> чувствует себя «чистым» / нет «дублирования файлов»
(Сначала я подумал, что это действительно хороший подход ...)
- НО:
- вы не можете заполнять тома во время сборки, поэтому вам нужно "запустить" контейнер, чтобы получить код приложения "в" контейнере
==> у нас неожиданно появился не только контейнер компоновщика, но нам также нужно «запустить» его для заполнения тома
- контейнеры больше не являются "самодостаточными", то есть вытащить "просто планировщик" из реестра не получится -
мы ДОЛЖНЫ также иметь объем на месте И его должен заполнить строитель ==> оркестровка становится более сложной
- том не является эфемерным, то есть он будет содержать «старый» код приложения до его обновления ==> это может привести к путанице и неожиданному поведению
Ссылки