簡體   English   中英

如何在 Docker 中緩存 maven 依賴項

[英]How to cache maven dependencies in Docker

我正在處理一個具有 ~200MB 依賴項的項目,由於帶寬有限,我想避免無用的上傳。

當我推送我的 Dockerfile 時(我稍后會附加它),即使我沒有觸及 pom.xml,我也總是有大約 200MB 的上傳:

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下。 所以我結束了制作一個非常簡單的腳本,如下所示:

構建Docker.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運行它?

如果我正確理解您想要實現的目標,問題是避免在每次 Docker 構建時創建一個包含所有 Maven 依賴項的胖 jar (以減輕重建后要推送的 Docker 層的大小)。

如果是,您可能對Spring Boot Thin Launcher感興趣,它也適用於非 Spring-Boot 項目。 相應 GitHub 存儲庫的README.md中提供了一些綜合文檔: 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 的緩存中受益(有關典型示例,請參見下文)。

為 Java/Maven 項目利用 Docker 的緩存機制

如果僅觸及源代碼文件 ( src/* ),則避免在每次構建時重新下載所有 Maven 依賴項的Dockerfile原型在以下參考中給出:
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-alpineCOPY --from=maven /app/app.jar ./app.jar 。)

在這種方法中,在COPY ./src ./src之前使用RUN mvn dependency:go-offline -B獲取 Maven 依賴COPY ./src ./src (以從 Docker 的緩存中受益)。

但是請注意, dependency:go-offline標准目標並不“完美”,因為一些動態依賴項/插件可能仍會在mvn package步驟觸發一些重新下載。 如果這對您來說是個問題(例如,如果在某個時候您真的想離線工作),您可以查看其他SO 答案,該答案建議使用提供de.qaware.maven:go-offline-maven-plugin:resolve-dependencies的專用插件de.qaware.maven:go-offline-maven-plugin:resolve-dependencies目標。

一般來說,Dockerfile 容器構建是分層工作的,每次構建這些層時都可以在 catch 中使用,並且在沒有更改時使用。 理想情況下,它應該以相同的方式工作。

Maven 通常默認在.m2文件夾中查找依賴項,該文件夾位於 Ubuntu /home/username/中 User 的 Home 目錄中

如果依賴的 jars 不可用,那么它會將這些 jars 下載到 .m2 並使用它。

現在,您可以在 1 次成功構建后壓縮並復制此.m2文件夾,並將其移動到 Docker 容器用戶的主目錄中。

在運行構建命令之前執行此操作

注意:您可能需要替換.m2中現有的.m2文件夾

所以你的 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
...

官方 Maven Docker 鏡像文檔也指出了實現更好的依賴緩存的不同方法。

基本上,他們建議將本地 maven 存儲庫作為卷掛載並在 Docker 映像中使用它,或者使用特殊的本地存儲庫 (/usr/share/maven/ref/),其內容將在容器啟動時復制。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM