Докер: Как разделить один и тот же код приложения между несколькими контейнерами / сервисами (например, PHP-FPM и NGINX)

ТЛ; др

  • код приложения требует шага сборки (тянуть в зависимости)
  • нескольким контейнерам нужен один и тот же "встроенный" код

Q: какая хорошая стратегия / рабочий процесс для архивирования с помощью docker / docker-compose.

Долго

Мы находимся в процессе стыковки приложения PHP с несколькими компонентами (контейнерами / службами), например

  • Рабочие узлы (процессы PHP поддерживаются через супервизор)
  • Планировщик (управление рабочими и запуск повторяющихся задач в cron)
  • PHP-FPM / Nginx (веб-интерфейс)

Сервисы определены в файле docker-compose. Во время разработки мы монтируем код приложения через том из каталога на хосте в каждом контейнере, чтобы мы видели изменения "немедленно" в каждом сервисе ( пример). Жизнь была хорошей.

Сейчас мы настраиваем среду CI/CD, основанную на Jenkins, которая должна создавать (+ тестировать) контейнеры и затем передавать их в реестр. Поскольку "монтировать с хоста" больше невозможно, мне интересно, как лучше всего поместить код приложения в каждый контейнер.

Две вещи в нашей установке делают это imho особенно сложным:

  1. у нас есть несколько контейнеров, которым нужен один и тот же код приложения
  2. "Артефакт сборки" - это не отдельный двоичный файл-контейнер (как у нас, например, с go), а "весь наш код + установленные зависимости" ==> "много файлов" (медленно...)
  3. есть этап сборки, который требует программного обеспечения, которое не требуется в конечном изображении

Решение для "3". обычно: Используйте многоэтапные сборки. Мы делаем это. Но: все примеры там, кажется, предполагают, что встроенный код будет использоваться только в одном другом контейнере (что в нашем случае неверно, см. 1.)

Чем мы сейчас занимаемся

  • структура папок
application-code/
  .docker/
    builder/
      Dockerfile
    php-fpm/
      Dockerfile
    docker-compose.yml
  build.sh
  index.php
  • ввести дополнительный контейнер "builder", который "собирает" приложение (получает "весь" код приложения в качестве контекста сборки; запускает "composer install")
# ./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

профессионал

  • "меньшие" изображения (в php-fpm не будет установлен композитор)
  • код приложения создается только один раз, а затем копируется поверх

против

  • контейнер конструктора не служит никакой другой цели, кроме как "встроенный" ==> не чувствует себя чистым
  • сборка "строителя" должна быть сделана до того, как будет построен любой другой контейнер
    • это означает, что у нас есть дополнительный

альтернативы

  • не используйте контейнер компоновщика, но "включите" этап компоновки, например, в контейнер "Планировщик", используя многоэтапные компоновки (поэтому мы не получим в конечном итоге composer)
    • get избавляется от "builder" - но теперь все другие службы зависят от "Scheduler" ==> чувствует себя еще более грязным
  • использовать том, чтобы поделиться кодом
    • нам не нужно "копировать" файлы в изображениях, мы можем просто "смонтировать том" ==> чувствует себя "чистым" / нет "дублирования файлов" (сначала я подумал, что это действительно хороший подход...)
    • НО:
    • Вы не можете заполнять тома во время сборки, поэтому вам нужно "запустить" контейнер, чтобы получить код приложения "в" контейнере ==> у нас внезапно появился не только контейнер компоновщика, но нам также нужно его "запустить" заполнить объем
    • контейнеры больше не являются "самодостаточными", то есть извлечение "просто Планировщика" из реестра не будет работать - мы ДОЛЖНЫ также иметь том на месте И его должен заполнять строитель ==> оркестровка становится более сложной
    • том не является эфемерным, то есть он будет содержать "старый" код приложения, пока не будет обновлен ==> это может привести к путанице и неожиданному поведению

связи

1 ответ

Беспокойство о количестве шагов не должно быть дискуссией. Вы должны беспокоиться о том, содержит ли изображение то, что вам нужно, без какого-либо шума.

Одной из вещей, которой явно не хватает в вашем методе компоновщика, является то, что вы продвигаете кодовую базу к производству, даже если вы запускали composer только install, Для того, чтобы запустить кодовую базу промышленного уровня, вам, по крайней мере, нужно запустить composer install --prefer-dist --no-dev -o чтобы:

  • удалить файлы, исключенные в .gitattributes используя --prefer-dist (это по умолчанию, так как композитор стабилен)
  • исключив загрузку require-dev с --no-dev
  • путем оптимизации файлов автозагрузчика с -o

Эта команда полностью отличается от той, которая использовалась для запуска ваших тестов.

Конвейер по умолчанию при работе с контейнером вашей кодовой базы будет:

  • build: оформить заказ, установить композитор и разрешить его повторное использование в следующих шагах или создать "образ тестирования"
  • test: запустить тесты и включить или отделить покрытие (одновременно)
  • подготовить: повторно использовать файлы сборки и запустить установку composer с необходимыми производственными флагами
  • изображение: создать изображение из предыдущего шага

Лично я считаю, что ответы на ваш вопрос весьма самоуверенны. Может быть, мой подход, используемый в работе с kubernetes, помогает.

Другие вопросы по тегам