Параллельная сборка в Docker в отношении многоступенчатой - PullRequest
2 голосов
/ 08 июня 2019

У меня есть монолитное репо, которое содержит все мои проекты. Текущая настройка, которую я имею, состоит в том, чтобы вызвать сборочный контейнер, смонтировать мое монолитное хранилище и построить мои проекты последовательно. Скопируйте двоичные файлы и создайте их соответствующие контейнеры среды выполнения (производства) последовательно.

Я считаю этот процесс довольно медленным и хочу улучшить скорость. Два основных подхода Я хочу принять это

  1. В сборочном контейнере компилируйте двоичные файлы моего проекта одновременно. Вместо последовательно.

  2. Как и в шаге 1, также создайте мои контейнеры времени выполнения (производства) одновременно.

Я провел небольшое исследование, и мне кажется, что меня интересуют две функции Docker :

  1. Многоступенчатое здание . Что позволяет мне не беспокоиться о сборочном контейнере и складывать все в один Dockerfiles.

  2. --parallel опция для docker-compose, которая решает подход # 2, позволяя мне одновременно создавать свои контейнеры времени выполнения.

Тем не менее, есть еще две проблемы :

  1. Как мне склеить две функции вместе?

  2. Как мне собрать свои двоичные файлы одновременно в сборочном Docker? Другими словами, как я могу достичь подхода # 1?

Разъяснения

Независимо от того, используется многоступенчатый или нет, есть две логические фазы.

Во-первых, это фаза бинарного построения. На этом этапе артефакты представляют собой скомпилированные исполняемые файлы (двоичные файлы) из контейнеров сборки. Поскольку я не использую многоэтапную сборку, я копирую эти двоичные файлы на хост, поэтому хост служит промежуточной промежуточной областью. В настоящее время двоичные файлы собираются последовательно, я хочу построить их одновременно в контейнере сборки. Отсюда подход # 1.

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

Multi-stage позволяет мне исключить необходимость промежуточной промежуточной области (хост). И --parallel позволяет мне создавать производственные образы одновременно.

Что меня интересует, так это то, как я могу достичь подхода # 1 & # 2 с использованием многоступенчатого и --parallel. Потому что для каждого проекта я могу определить отдельную многоступенчатую Dockerfiles и вызвать --parallel для всех них, чтобы их изображения были построены отдельно. Это позволило бы достичь подхода # 2, но это породило бы отдельный контейнер сборки для каждого проекта и потребовало бы много ресурсов (я использую один и тот же контейнер сборки для всех моих проектов, и это 6 ГБ). С другой стороны, я могу написать скрипт для одновременной сборки двоичных файлов моего проекта в контейнере сборки. Это позволит достичь подхода # 1, но тогда я не смогу использовать многоэтапный режим, если я хочу одновременно создавать производственные образы.

Что мне действительно нужно, так это Dockerfiles вот так:

FROM alpine:latest AS builder
RUN concurrent_build.sh binary_a binary_b

FROM builder AS prod_img_a
COPY binary_a .

FROM builder AS prod_img_b
COPY binary_b .

И вы сможете запускать команду docker-compose вот так (я это выдумал):

docker-compose --parallel prod_img_a prod_img_b

Дополнительные уточнения

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

--parallel не использует разные хосты, но мой сборочный контейнер огромен. Если я использую многоступенчатую сборку и параллельно запускаю что-то вроде 15 из этих контейнеров сборки на моей локальной машине разработчика, это может быть плохо.

Я тоже думаю о раздельной компиляции бинарных и исполняемых контейнеров, но я не нахожу простой способ сделать это. Я никогда не использовал docker commit, принесет ли в жертву кеш докера?

Ответы [ 2 ]

2 голосов
/ 09 июня 2019

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

Если вы обнаружите, что это так, вы можете написать docker-compose.yml файлов, которые содержат только подмножество ваших служб, так что у вас есть только пять одновременно, и затем построить по очереди для каждого из них. В самом деле, вы могли бы написать скрипт, который будет читать вашу существующую конфигурацию YAML и разбивать ее на части, так что вам не нужно будет поддерживать вашу общую конфигурацию и ваши разделенные конфигурации отдельно.

Я предложил в комментариях, что многоступенчатый не поможет, но теперь я думаю, что это не так. Мне было интересно, будет ли второй этап в Dockerfile блокироваться, пока не завершится первый, но это не должно быть так - если второй этап начинается с известного образа, то он должен блокироваться только при обнаружении команды COPY --from=first_stage, которая Вы можете сделать это прямо в конце, когда копируете свой бинарный файл со стадии компиляции.

Конечно, если многоступенчатые сборки не распараллелены, то стоит попробовать docker commit. Вы спросили, использует ли он кэш слоя, и ответ - я не думаю, что это имеет значение - ваша операция здесь будет такой:

  • Раскрутите двоичный контейнер, чтобы запустить оболочку или команду сна
  • Поверните контейнер времени выполнения таким же образом
  • Используйте docker cp, чтобы скопировать двоичный файл из первого во второй
  • Используйте docker commit для создания нового образа времени выполнения из нового контейнера времени выполнения

Это не требует каких-либо сетевых операций, и поэтому должно быть довольно быстрым - вы уже очень выиграли от распараллеливания на этом этапе. Если двоичные файлы имеют нетривиальный размер, вы можете даже попытаться распараллелить операции копирования:

docker cp binary1:/path/to/binary runtime1:/path/to/binary &
docker cp binary2:/path/to/binary runtime2:/path/to/binary &
docker cp binary3:/path/to/binary runtime3:/path/to/binary &

Обратите внимание, что это операции с диском, поэтому вы можете обнаружить, что нет преимущества перед их последовательным выполнением.

Не могли бы вы попробовать и доложить:

  • ваше текущее время сборки для контейнера
  • общее время сборки
  • ваше новое время сборки после распараллеливания

Делайте все локально, чтобы начать, и если вы получите какое-то полезное ускорение, попробуйте это в своей инфраструктуре сборки, где у вас, вероятно, будет больше ядер ЦП.

1 голос
/ 02 июля 2019

Результаты

Мои контейнеры моно-репо 16 проектов, некоторые из них представляют собой микроуслуги, занимающие несколько мегабайт, некоторые - более крупные услуги, объем которых составляет от 300 до 500 мегабайт.

Сборка содержит компиляцию двух предпосылок, одна из которых gRPC, а другая - XDR. оба тривиально малы, на их создание уходит всего 1 или 2 секунды.

Сборка содержит этап установки node_modules. Установка и сборка NPM являются узким местом проекта и, безусловно, самым медленным.

Стратегия, которую я использую, состоит в том, чтобы разбить сборку на два этапа:

  1. Первый этап - раскрутить монолитный докер сборки, монтировать на него моно-репо с консистенцией cache в качестве связующего тома. И построить все двоичные зависимости моего контейнера внутри него параллельно, используя Goroutines. Каждый Goroutine вызывает bash-скрипт build.sh, который выполняет сборку. Полученные двоичные файлы записываются в тот же подключенный том. Кэш-память используется в виде смонтированного тома док-станции, и двоичные файлы сохраняются при каждом запуске с максимальной эффективностью.

  2. Второй этап - параллельное построение изображений. Это делается с помощью документированного Go SDK здесь . Это также делается параллельно с использованием Goroutines. На этом этапе нет ничего особенного, кроме некоторых базовых оптимизаций.

У меня нет данных о производительности старой системы сборки, но сборка всех 16 проектов легко заняла верхнюю границу 30 минут. Эта сборка была чрезвычайно простой и не создавала изображения параллельно и не использовала никаких оптимизаций.

Новая сборка очень быстрая. Если все кэшировано и изменений нет, сборка занимает ~ 2 минуты. Другими словами, затраты на запуск системы сборки, проверку кэша и сборку одинаковых кэшированных образов докера занимает ~ 2 минуты. Если кеша вообще нет, новая сборка занимает ~ 5 минут. ОГРОМНОЕ улучшение от старой сборки.

Спасибо @halfer за помощь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...