Как кэшировать зависимости maven в Docker - PullRequest
0 голосов
/ 09 декабря 2018

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

Когда я нажимаю свой Dockerfile (я его сейчас прикреплю), у меня всегда есть загрузка ~ 200 МБ, даже если я не трогал pom.xml:

FROM maven:3.6.0-jdk-8-slim

WORKDIR /app

ADD pom.xml /app

RUN mvn verify clean --fail-never

COPY ./src /app/src

RUN mvn package

ENV CONFIG_FOLDER=/app/config
ENV DATA_FOLDER=/app/data
ENV GOLDENS_FOLDER=/app/goldens
ENV DEBUG_FOLDER=/app/debug

WORKDIR target

CMD ["java","-jar","-Dlogs=/app/logs", "myProject.jar"]

Этот Dockerfile должен создать 200MB fatJAR, включающий все зависимости, поэтому загрузка ~ 200MB происходит каждый раз.Чего я хотел бы добиться, так это создать слой со всеми зависимостями и «сказать» фазе упаковки, чтобы не включать JAR-файлы зависимостей в fatJAR, а искать их внутри заданного каталога.

Мне было интересносоздать скрипт, который выполняет mvn dependency:copy-dependencies перед процессом сборки, а затем скопировать каталог в контейнер;затем создание «обезжиренного» JAR, в котором все эти зависимости только связаны и фактически не скопированы в него.

Возможно ли это?

РЕДАКТИРОВАТЬ: Я обнаружил, чтоЛокальное хранилище Maven контейнера находится под /root/.m2.Поэтому я закончил делать очень простой скрипт, подобный этому:

BuildDocker.sh

mvn verify -clean --fail-never
mv ~/.m2 ~/git/myProjectRepo/.m2

sudo docker build -t myName/myProject:"$1"

И отредактировал Dockerfile , например:

# Use an official Python runtime as a parent image
FROM maven:3.6.0-jdk-8-slim

# Copy my Mavne Local Repository into the container thus creating a new layer
COPY ./.m2 /root/.m2

# Set the working directory to /app
WORKDIR /app

# Copy the pom.xml
ADD pom.xml /app

# Resolve and Download all dependencies: this will be done only if the pom.xml has any changes
RUN mvn verify clean --fail-never

# Copy source code and configs 
COPY ./src /app/src

# create a ThinJAR
RUN mvn package


# Run the jar
...

После процесса сборки я заявил, что /root/.m2 имеет все каталоги, которые у меня есть, но как только я запускаю JAR, я получаю:

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/log4j/Priority
    at myProject.ThreeMeans.calculate(ThreeMeans.java:17)
    at myProject.ClusteringStartup.main(ClusteringStartup.java:7)
Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Priority
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 2 more

Может быть, мне не следует запускать его через java -jar

Ответы [ 2 ]

0 голосов
/ 09 декабря 2018

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

Если да, вас может заинтересовать Spring Boot Thin Launcher , который также применим для не-Spring-Boot проектов.Некоторая исчерпывающая документация доступна в README.md соответствующего репозитория GitHub: https://github.com/dsyer/spring-boot-thin-launcher#readme

Чтобы подвести итог, достаточно добавить следующую декларацию плагина в pom.xml:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <!--<version>${spring-boot.version}</version>-->
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot.experimental</groupId>
                    <artifactId>spring-boot-thin-layout</artifactId>
                    <version>1.0.19.RELEASE</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

В идеале, это решение должно сочетаться со стандартной Dockerfile настройкой для использования кеша Docker (типичный пример приведен ниже).

Использование механизма кеширования Docker для проекта Java / Maven

Архетип Dockerfile, который избегает повторной загрузки всех зависимостей Maven при каждой сборке, если были затронуты только файлы исходного кода (src/*), приведен в следующей ссылке:
https://whitfin.io/speeding-up-maven-docker-builds/

Точнее, предлагаемый Dockerfile выглядит следующим образом:

# our base build image
FROM maven:3.5-jdk-8 as maven

WORKDIR /app

# copy the Project Object Model file
COPY ./pom.xml ./pom.xml

# fetch all dependencies
RUN mvn dependency:go-offline -B

# copy your other files
COPY ./src ./src

# build for release
# NOTE: my-project-* should be replaced with the proper prefix
RUN mvn package && cp target/my-project-*.jar app.jar


# smaller, final base image
FROM openjdk:8u171-jre-alpine
# OPTIONAL: copy dependencies so the thin jar won't need to re-download them
# COPY --from=maven /root/.m2 /root/.m2

# set deployment directory
WORKDIR /app

# copy over the built artifact from the maven image
COPY --from=maven /app/app.jar ./app.jar

# set the startup command to run your binary
CMD ["java", "-jar", "/app/app.jar"]

Обратите внимание, что он опирается на так называемую многоступенчатую функцию сборки Docker (наличие двух FROM директив), подразумевая, что конечное изображение будет намного меньше самого базового образа maven.
(Если вы не интересуетесь этой функцией во время разработкиНа этом этапе вы можете удалить строки FROM openjdk:8u171-jre-alpine и COPY --from=maven /app/app.jar ./app.jar.)

. При таком подходе зависимости Maven извлекаются с RUN mvn dependency:go-offline -B перед строкой COPY ./src ./src (дляпользуйтесь кешем Docker).

Обратите внимание, что стандартная цель dependency:go-offline не является "идеальной", поскольку некоторые динамические зависимости / плагины могут по-прежнему вызывать повторную загрузку на шаге mvn package.Если это проблема для вас (например, если в какой-то момент вы действительно хотите работать в автономном режиме), вы можете взглянуть на этот другой SO ответ , который предлагает использовать специальный плагин, который предоставляет de.qaware.maven:go-offline-maven-plugin:resolve-dependencies цель.

0 голосов
/ 09 декабря 2018

Как правило, сборка контейнера Dockerfile работает в слоях, и каждый раз, когда вы создаете эти слои, они доступны в catch и используются, если нет изменений.В идеале это должно было работать так же.

Maven обычно ищет зависимости по умолчанию в папке .m2, расположенной в домашнем каталоге пользователя в Ubuntu /home/username/

Если зависимые файлы не доступны, то они загружают эти файлы в .m2 ииспользует его.

Теперь вы можете заархивировать и скопировать эту папку .m2 после 1 успешной сборки и переместить ее в домашний каталог пользователя контейнера Docker.

Сделайте это перед запуском команды сборки

Примечание: Возможно, вам потребуется заменить существующую папку .m2 в Docker

Таким образом, ваш файл Docker будет выглядеть примерно так

FROM maven:3.6.0-jdk-8-slim

WORKDIR /app

COPY .m2.zip /home/testuser/

ADD pom.xml /app

RUN mvn verify clean --fail-never

COPY ./src /app/src

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