Зависимости кеша Maven докеров
Я пытаюсь использовать докер для автоматизации сборки maven. Проект, который я хочу построить, занимает около 20 минут, чтобы загрузить все зависимости, поэтому я попытался создать образ докера, который будет кэшировать эти зависимости, но он, похоже, не сохраняет его. Мой файл докеров
FROM maven:alpine
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
ADD pom.xml /usr/src/app
RUN mvn dependency:go-offline
Изображение строит, и он загружает все. Однако результирующее изображение имеет тот же размер, что и базовый образ maven:alpine
, поэтому он, похоже, не кэшировал зависимости в изображении. Когда я пытаюсь использовать изображение в mvn compile
, он проходит через полные 20 минут перезагрузки всего.
Можно ли создать изображение maven, которое кэширует мои зависимости, чтобы они не загружались каждый раз, когда я использую изображение для выполнения сборки?
Я запускаю следующие команды:
docker build -t my-maven .
docker run -it --rm --name my-maven-project -v "$PWD":/usr/src/mymaven -w /usr/src/mymaven my-maven mvn compile
Я понимаю, что все, что RUN
делает во время процесса сборки докеров, становится частью результирующего изображения.
Ответы
Ответ 1
Обычно нет никаких изменений в файле pom.xml
, но только некоторые другие изменения исходного кода, когда вы пытаетесь запустить сборку образа Docker. При таких обстоятельствах вы можете сделать это:
FYI:
FROM maven:3-jdk-8
ENV HOME=/home/usr/app
RUN mkdir -p $HOME
WORKDIR $HOME
# 1. add pom.xml only here
ADD pom.xml $HOME
# 2. start downloading dependencies
RUN ["/usr/local/bin/mvn-entrypoint.sh", "mvn", "verify", "clean", "--fail-never"]
# 3. add all source code and start compiling
ADD . $HOME
RUN ["mvn", "package"]
EXPOSE 8005
CMD ["java", "-jar", "./target/dist.jar"]
Итак, ключ:
добавить файл pom.xml
.
затем mvn verify --fail-never
загрузит maven-зависимости.
Затем добавьте все свои исходные файлы и начните компиляцию (mvn package
).
Если в файле pom.xml
есть изменения или вы запускаете этот скрипт в первый раз, docker выполнит 1 → 2 → 3. Если в файле pom.xml
нет изменений, Docker пропустит шаг 1、2 и сделать 3 напрямую.
Этот простой трюк может быть использован во многих других ситуациях управления пакетами (gradle 、 yarn pm npm pip).
Изменить:
Вам также следует рассмотреть возможность использования mvn dependency:resolve
или mvn dependency:go-offline
соответственно в качестве других комментариев & ответы предполагают.
Ответ 2
Оказывается, изображение, которое я использую в качестве основы, имеет родительское изображение, которое определяет
VOLUME "$USER_HOME_DIR/.m2"
см.: https://github.com/carlossg/docker-maven/blob/322d0dff5d0531ccaf47bf49338cb3e294fd66c8/jdk-8/Dockerfile
В результате во время сборки все файлы записываются в $USER_HOME_DIR/.m2
, но, поскольку ожидается, что это будет том, ни один из этих файлов не будет сохранен с образом контейнера.
В настоящее время в Docker нет способа отменить регистрацию этого определения тома, поэтому необходимо создать отдельный образ maven, а не использовать официальный образ maven.
Ответ 3
@Kim ближе всего, но пока не совсем. Я не думаю, что добавление --fail-never
является правильным, даже через это сделать работу.
Команда verify
вызывает много плагинов для выполнения, что является проблемой (для меня) - я не думаю, что они должны выполняться, когда все, что я хочу, это установить зависимости! У меня также есть многомодульная сборка и сборка javascript, так что это еще больше усложняет настройку.
Но запуска только verify
недостаточно, потому что если вы запустите install
в следующих командах, будет использоваться больше плагинов - что означает больше зависимостей для загрузки - maven отказывается загружать их в противном случае. Соответствующее чтение: Maven: введение в жизненный цикл сборки
Вы должны найти, какие свойства отключают каждый плагин, и добавлять их по одному, чтобы они не нарушали вашу сборку.
WORKDIR /srv
# cache Maven dependencies
ADD cli/pom.xml /srv/cli/
ADD core/pom.xml /srv/core/
ADD parent/pom.xml /srv/parent/
ADD rest-api/pom.xml /srv/rest-api/
ADD web-admin/pom.xml /srv/web-admin/
ADD pom.xml /srv/
RUN mvn -B clean install -DskipTests -Dcheckstyle.skip -Dasciidoctor.skip -Djacoco.skip -Dmaven.gitcommitid.skip -Dspring-boot.repackage.skip -Dmaven.exec.skip=true -Dmaven.install.skip -Dmaven.resources.skip
# cache YARN dependencies
ADD ./web-admin/package.json ./web-admin/yarn.lock /srv/web-admin/
RUN yarn --non-interactive --frozen-lockfile --no-progress --cwd /srv/web-admin install
# build the project
ADD . /srv
RUN mvn -B clean install
но некоторые плагины не так легко пропустить - я не эксперт по maven (поэтому я не знаю, почему он игнорирует опцию cli - это может быть ошибка), но следующее работает как и ожидалось для org.codehaus.mojo:exec-maven-plugin
<project>
<properties>
<maven.exec.skip>false</maven.exec.skip>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.3.2</version>
<executions>
<execution>
<id>yarn install</id>
<goals>
<goal>exec</goal>
</goals>
<phase>initialize</phase>
<configuration>
<executable>yarn</executable>
<arguments>
<argument>install</argument>
</arguments>
<skip>${maven.exec.skip}</skip>
</configuration>
</execution>
<execution>
<id>yarn run build</id>
<goals>
<goal>exec</goal>
</goals>
<phase>compile</phase>
<configuration>
<executable>yarn</executable>
<arguments>
<argument>run</argument>
<argument>build</argument>
</arguments>
<skip>${maven.exec.skip}</skip>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
обратите внимание на явный <skip>${maven.exec.skip}</skip>
- другие плагины выбирают это из параметров cli, но не этот (ни -Dmaven.exec.skip=true
ни -Dexec.skip=true
работа сама по себе)
Надеюсь это поможет
Ответ 4
Аналогично с ответом @Kim, но я использую dependency:resolve
решите команду mvn. Итак, вот мой полный Dockerfile:
FROM maven:3.5.0-jdk-8-alpine
WORKDIR /usr/src/app
# First copy only the pom file. This is the file with less change
COPY ./pom.xml .
# Download the package and make it cached in docker image
RUN mvn -B -f ./pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolve
# Copy the actual code
COPY ./ .
# Then build the code
RUN mvn -B -f ./pom.xml -s /usr/share/maven/ref/settings-docker.xml package
# The rest is same as usual
EXPOSE 8888
CMD ["java", "-jar", "./target/YOUR-APP.jar"]
Ответ 5
Я не думаю, что другие ответы здесь являются оптимальными. Например, ответ mvn verify
выполняет следующие этапы и делает гораздо больше, чем просто разрешает зависимости:
validate - подтвердить правильность проекта и получить всю необходимую информацию
compile - скомпилировать исходный код проекта
test - протестируйте скомпилированный исходный код, используя подходящую среду модульного тестирования. Эти тесты не должны требовать, чтобы код был упакован или развернут
package - взять скомпилированный код и упаковать его в распространяемый формат, такой как JAR.
проверить - выполнить любые проверки результатов интеграционных тестов, чтобы убедиться в соответствии критериям качества
Все эти этапы и связанные с ними цели не нужно запускать, если вы хотите разрешить зависимости.
Если вы хотите разрешить только зависимости, вы можете использовать dependency:go-offline
цель:
FROM maven:3-jdk-12
WORKDIR /tmp/example/
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src/ src/
RUN mvn package
Ответ 6
После нескольких дней борьбы мне удалось сделать это кеширование позже, используя промежуточный контрайнер, и я хотел бы обобщить мои выводы здесь, так как эта тема настолько полезна и часто показывается на главной странице поиска Google:
- Ответ Kim работает только при определенном условии: pom.xml не может быть изменен, плюс Maven регулярно обновляет ежедневно по умолчанию
- Зависимость mvn: go-offline -B --fail-никогда не имеет подобного недостатка, поэтому, если вам нужно получить свежий код из репо, высока вероятность того, что Maven будет запускать полную проверку каждый раз
- Монтирование тома также не работает, потому что нам нужно разрешить зависимости во время сборки образа
- Наконец, у меня есть совместимое работоспособное решение (может не работать с другими):
- Сначала создайте изображение, чтобы разрешить все зависимости (не промежуточное изображение)
- Создайте еще один Dockerfile с промежуточным изображением, с примерами dockerfile:
#docker build -t dependencies .
From ubuntu
COPY pom.xml pom.xml
RUN mvn dependency:go-offline -B --fail-never
From dependencies as intermediate
From tomcat
RUN git pull repo.git (whatsoever)
RUN mvn package
Идея состоит в том, чтобы сохранить все зависимости в другом изображении, которое Maven может использовать немедленно
Могут быть и другие сценарии, с которыми я еще не сталкивался, но это решение немного облегчает мне загрузку 3ГБ мусора каждый раз, когда я не могу представить, почему Java стала таким жирным китом в современном скудном мире
Ответ 7
У меня была эта проблема только litle. Множество решений в Интернете, но тот, который работал у меня, просто монтирует том для каталога maven modules:
mkdir /opt/myvolumes/m2
затем в файле Docker:
...
VOLUME /opt/myvolumes/m2:/root/.m2
...
Есть лучшие решения, но не так просто.
Это сообщение в блоге добавляет лишнюю милю, помогая вам кэшировать все:
https://keyholesoftware.com/2015/01/05/caching-for-maven-docker-builds/
Ответ 8
Существует два способа кэширования зависимостей maven:
Выполните "mvn verify" как часть выполнения контейнера, НЕ создавайте и убедитесь, что вы монтируете .m2 из тома.
Это эффективно, но плохо работает с облачной сборкой и несколькими ведомыми ведомыми
Используйте "контейнер кэша зависимостей" и периодически обновляйте его. Вот как это делается:
а. Создайте Dockerfile, который копирует pom, и создайте автономные зависимости:
FROM maven:3.5.3-jdk-8-alpine
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline
б. Создавайте его периодически (например, по ночам) как "Deps: latest"
с. Создайте еще один Dockerfile для фактического построения системы в соответствии с фиксацией (желательно с использованием многоэтапного) - и убедитесь, что это FROM Deps.
Используя эту систему, вы будете иметь быстрые, восстанавливаемые сборки с достаточно хорошим кешем.
Ответ 9
Использование BuildKit
Начиная с Docker v18.03
, вы можете использовать BuildKit вместо томов, которые были упомянуты в других ответах. Это позволяет монтировать кэши, которые могут сохраняться между сборками, и вы можете избежать загрузки содержимого соответствующего .m2/repository
каждый раз.
Предполагая, что Dockerfile находится в корне вашего проекта:
# syntax = docker/dockerfile:1.0-experimental
FROM maven:3.6.0-jdk-11-slim AS build
COPY . /home/build
RUN mkdir /home/.m2
WORKDIR /home/.m2
USER root
RUN --mount=type=cache,target=/root/.m2 mvn -f /home/build/pom.xml clean compile
target=/root/.m2
монтирует кеш в указанное место в Dockerfile с изображением maven image docs.
Для сборки вы можете запустить следующую команду:
DOCKER_BUILDKIT=1 docker build --rm --no-cache .
Более подробную информацию о BuildKit можно найти здесь.
Ответ 10
Если зависимости загружаются после того, как контейнер уже вставлен, вам необходимо зафиксировать изменения в этом контейнере и создать новое изображение с загруженными артефактами.