Рубрики
Без рубрики

Более быстрые сборки Maven в Docker

На прошлой неделе я описал различные методы для закрепления ваших сборок Maven. Сегодня я хотел бы расширить… Помечен как maven, java, docker, build.

На прошлой неделе я описал различные методы для закрепления ваших сборок Maven. Сегодня я хотел бы расширить область применения и сделать то же самое для Maven builds внутри Docker .

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

Основание

Чтобы вычислить полезную базовую линию, нам нужен примерный проект. Я создал one только для этой цели: это относительно небольшой проект Kotlin.

Вот соответствующий Dockerfile :

FROM openjdk:11-slim-buster as build                         #1

COPY .mvn .mvn                                               #2
COPY mvnw .                                                  #2
COPY pom.xml .                                               #2
COPY src src                                                 #2

RUN ./mvnw -B package                                        #3

FROM openjdk:11-jre-slim-buster                              #4

COPY --from=build target/fast-maven-builds-1.0.jar .         #5

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "fast-maven-builds-1.0.jar"]     #6
  1. Начните с образа JDK для шага упаковки
  2. Добавление необходимых ресурсов
  3. Создайте JAR
  4. Начните с шага создания JRE для создания изображения
  5. Скопируйте JAR с предыдущего шага
  6. Установите точку входа

Давайте выполним сборку:

time DOCKER_BUILDKIT=0 docker build -t fast-maven:1.0 .      #1
  1. Забудьте пока о переменной окружения, как я объясню в следующем разделе

Вот результаты пяти пробежек:

* 0.36s user 0.53s system 0% cpu 1:53.06 total
* 0.36s user 0.56s system 0% cpu 1:52.50 total
* 0.35s user 0.55s system 0% cpu 1:56.92 total
* 0.36s user 0.56s system 0% cpu 2:04.55 total
* 0.38s user 0.61s system 0% cpu 2:04.68 total

Комплект для сборки для победы

В последней командной строке использовалась переменная окружения DOCKER_BUILD KIT . Это способ сказать Docker использовать устаревший движок. Если вы не обновляли Docker в течение некоторого времени, это движок, который вы используете. В настоящее время Build Kit заменил его и является новым значением по умолчанию.

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

  • Автоматический сбор мусора
  • Одновременное разрешение зависимостей
  • Эффективное кэширование инструкций
  • Импорт/экспорт кэша сборки
  • и т.д.

Давайте повторно выполним предыдущую команду на новом движке:

time docker build -t fast-maven:1.1 .

Вот выдержка из журнала консоли первого запуска:

...
 => => transferring context: 4.35kB
 => [build 2/6] COPY .mvn .mvn
 => [build 3/6] COPY mvnw .
 => [build 4/6] COPY pom.xml .
 => [build 5/6] COPY src src
 => [build 6/6] RUN ./mvnw -B package
...

0.68s user 1.04s system 1% cpu 2:06.33 total

Следующие выполнения одной и той же команды имеют несколько иной результат:

...
 => => transferring context: 1.82kB
 => CACHED [build 2/6] COPY .mvn .mvn
 => CACHED [build 3/6] COPY mvnw .
 => CACHED [build 4/6] COPY pom.xml .
 => [build 5/6] COPY src src
 => [build 6/6] RUN ./mvnw -B package
...

Помните, что мы меняем исходный код между запусками. Файлы, которые мы не изменяем, а именно .mvn , mvnw и pom.xml , кэшируются BuildKit. Но эти ресурсы невелики, так что кэширование не приводит к значительному увеличению времени сборки.

* 0.69s user 1.01s system 1% cpu 2:05.08 total
* 0.65s user 0.95s system 1% cpu 1:58.51 total
* 0.68s user 0.99s system 1% cpu 1:59.31 total
* 0.64s user 0.95s system 1% cpu 1:59.82 total

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

Слои, слои, слои

Мы должны сосредоточить наши усилия на зависимостях. Для этого мы можем использовать |/layers и разделить сборку на два этапа:

  • На первом шаге мы загружаем зависимости
  • Во втором случае мы делаем правильную упаковку

Каждый шаг создает слой, второй зависит от первого.

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

FROM openjdk:11-slim-buster as build

COPY .mvn .mvn
COPY mvnw .
COPY pom.xml .

RUN ./mvnw -B dependency:go-offline                          #1

COPY src src

RUN ./mvnw -B package                                        #2

FROM openjdk:11-jre-slim-buster

COPY --from=build target/fast-maven-builds-1.2.jar .

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "fast-maven-builds-1.2.jar"]
  1. Цель go-offline загружает все зависимости и плагины
  2. На данный момент доступны все зависимости

Обратите внимание, что go-offline загружает не все. Команда не будет выполнена успешно, если вы попытаетесь использовать опцию -o (для автономного режима). Это хорошо известная старая ошибка . Во всех случаях это “достаточно хорошо”.

Давайте запустим сборку:

time docker build -t fast-maven:1.2 .

Первый запуск занимает значительно больше времени, чем базовый:

0.84s user 1.21s system 1% cpu 2:35.47 total

Однако последующие сборки выполняются намного быстрее. Изменение исходного кода влияет только на второй уровень и не запускает загрузку (большинства) зависимостей:

* 0.23s user 0.36s system 5% cpu 9.913 total
* 0.21s user 0.33s system 5% cpu 9.923 total
* 0.22s user 0.38s system 6% cpu 9.990 total
* 0.21s user 0.34s system 5% cpu 9.814 total
* 0.22s user 0.37s system 5% cpu 10.454 total

Монтирование тома в сборке

Наложение слоев на сборку значительно сократило время сборки. Мы можем изменить исходный код и сохранить его на низком уровне. Однако есть одна нерешенная проблема. Изменение одной зависимости делает слой недействительным, поэтому нам нужно загрузить их все снова.

К счастью, Build Kit вводит монтирование томов во время сборки (и не только во время бега). Доступно несколько типов монтирования, но тот, который нас интересует, – это монтирование кэша . Это экспериментальная функция, поэтому вам нужно явно зарегистрироваться:

# syntax=docker/dockerfile:experimental                      #1
FROM openjdk:11-slim-buster as build

COPY .mvn .mvn
COPY mvnw .
COPY pom.xml .
COPY src src

RUN --mount=type=cache,target=/root/.m2,rw ./mvnw -B package #2

FROM openjdk:11-jre-slim-buster

COPY --from=build target/fast-maven-builds-1.3.jar .

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "fast-maven-builds-1.3.jar"]
  1. Подпишитесь на экспериментальные функции
  2. Сборка с использованием кэша

Пришло время запустить сборку:

time docker build -t fast-maven:1.3 .

Время сборки выше, чем при обычной сборке, но все же ниже, чем при сборке слоев:

0.71s user 1.01s system 1% cpu 1:50.50 total

Следующие сборки находятся на одном уровне со слоями:

* 0.22s user 0.33s system 5% cpu 9.677 total
* 0.30s user 0.36s system 6% cpu 10.603 total
* 0.24s user 0.37s system 5% cpu 10.461 total
* 0.24s user 0.39s system 6% cpu 10.178 total
* 0.24s user 0.35s system 5% cpu 10.283 total

Однако, в отличие от слоев, нам нужно только загрузить обновленные зависимости. Здесь давайте изменим версию Kotlin с 1.5.30 чтобы 1.5.31 :


    1.5.31

Это огромное улучшение в отношении времени сборки:

* 0.41s user 0.57s system 2% cpu 44.710 total

Рассматривая демон Maven

В предыдущем посте, касающемся регулярных сборок Maven , я упомянул демона Maven. Давайте соответствующим образом изменим нашу сборку:

FROM openjdk:11-slim-buster as build

ADD https://github.com/mvndaemon/mvnd/releases/download/0.6.0/mvnd-0.6.0-linux-amd64.zip . #1

RUN apt-get update \                                         #2
 && apt-get install unzip \                                  #3
 && mkdir /opt/mvnd \                                        #4
 && unzip mvnd-0.6.0-linux-amd64.zip \                       #5
 && mv mvnd-0.6.0-linux-amd64/* /opt/mvnd                    #6

COPY .mvn .mvn
COPY mvnw .
COPY pom.xml .
COPY src src

RUN /opt/mvnd/bin/mvnd -B package                            #7

FROM openjdk:11-jre-slim-buster

COPY --from=build target/fast-maven-builds-1.4.jar .

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "fast-maven-builds-1.4.jar"]
  1. Загрузите последнюю версию демона Maven
  2. Обновите индекс пакета
  3. Установить распаковать архив
  4. Создайте выделенную папку
  5. Извлеките архив, который мы загрузили на шаге #1
  6. Переместите содержимое извлеченного архива в ранее созданную папку
  7. Используйте mvn вместо оболочки Maven

Давайте запустим сборку прямо сейчас:

docker build -t fast-maven:1.4 .

В журнале выводится следующее:

* 0.70s user 1.01s system 1% cpu 1:51.96 total
* 0.72s user 0.98s system 1% cpu 1:47.93 total
* 0.66s user 0.93s system 1% cpu 1:46.07 total
* 0.76s user 1.04s system 1% cpu 1:50.35 total
* 0.80s user 1.18s system 1% cpu 2:01.45 total

Существенного улучшения по сравнению с исходным уровнем нет.

Я попытался создать выделенный mvnd образ и использовать его в качестве родительского образа:

# docker build -t mvnd:0.6.0 .
FROM openjdk:11-slim-buster as build

ADD https://github.com/mvndaemon/mvnd/releases/download/0.6.0/mvnd-0.6.0-linux-amd64.zip .

RUN --mount=type=cache,target=/var/cache/apt,rw apt-get update \
 && apt-get install unzip \
 && mkdir /opt/mvnd \
 && unzip mvnd-0.6.0-linux-amd64.zip \
 && mv mvnd-0.6.0-linux-amd64/* /opt/mvnd
# docker build -t fast-maven:1.5 .
FROM mvnd:0.6.0 as build

# ...

Этот подход изменяет результат каким-либо существенным образом.

mvnd хорош только тогда, когда демон работает в течение нескольких запусков. Я не нашел способа сделать это с помощью Docker. Если у вас есть какие-либо идеи о том, как этого достичь, пожалуйста, скажите мне; дополнительные баллы, если вы можете указать мне на реализацию.

Вот краткое описание всех времен выполнения:

111.96 155.47 113.06 125.08 #1 (ы) 110.5
107.93 9.91 112.5 118.51 #2 (ы) 9.68
106.07 9.92 116.92 119.31 #3 (ы) 10.6
110.35 9.99 124.55 119.82 #4 (ы) 10.46
121.45 9.81 124.68 #5 (ы) 10.18
10.45 #6 (ы) 10.28
#7 (ы) 44.71
111.55 9.91 118.34 120.68 Среднее значение (значения) 10.24
111.47 0.01 28.55 6.67 Отклонение 0.10
6.79 108.43 0 -2.34 Выигрыш от базового (ых) уровня (ов) 108.10
5.74% 91.63% 0.00% -1.98% % прироста 91.35%

Вывод

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

С помощью Build Kit я рекомендую использовать новую возможность монтирования кэша, чтобы избежать загрузки всех зависимостей, если уровень признан недействительным.

Полный исходный код для этого поста можно найти на Github в формате Maven:

javageek/быстрые сборки maven

Чтобы идти дальше:

Первоначально опубликовано на Фанат Java 10 октября th , 2021

Оригинал: “https://dev.to/nfrankel/faster-maven-builds-in-docker-2584”