当前位置: 首页 > 知识库问答 >
问题:

如何在 Docker 中缓存 maven 依赖项

阎庆
2023-03-14

我正在做一个有大约200MB依赖项的项目,我希望避免由于带宽有限而导致的无用上传

当我推我的Dockerfile(我一会儿会附上)时,我总是有一个~200MB的上传,即使我没有碰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依赖项:复制依赖项,然后将目录复制到容器;然后构建一个“非脂肪”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

也许我不应该通过爪哇 - 罐子运行它?

共有3个答案

康烨伟
2023-03-14

官方 Maven Docker 映像的文档还指出了实现更好的依赖项缓存的不同方法。

基本上,他们建议将本地 maven 存储库作为卷挂载并跨 Docker 映像使用它,或者使用一个特殊的本地存储库(/usr/share/maven/ref/),其内容将在容器启动时复制。

谈琛
2023-03-14

通常,Dockerfile容器构建在层中工作,每次构建时,这些层在catch中可用,如果没有更改,则使用。理想情况下,它应该以同样的方式工作。

默认情况下,Maven 通常会在 Ubuntu /主页/用户名/ 中用户的主页目录中的 .m2 文件夹中查找依赖项

如果依赖的 jar 不可用,则它会将这些 jar 下载到 .m2 并使用它。

现在您可以压缩并复制此<code>。m2文件夹,并将其移动到Docker容器用户的主目录中。

在运行构建命令之前执行此操作

注意:您可能需要替换docker中现有的< code>.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
...
申屠项明
2023-03-14

如果我正确地理解了你想要实现的目标,那么问题是避免在每个Docker构建中创建一个包含所有Maven依赖项的胖罐(以减轻重建后要推送的Docker层的大小)。

如果是,您可能对Spring启动器薄型启动器感兴趣,它也适用于非Spring启动项目。相应的 GitHub 存储README.md 提供了一些全面的文档:https://github.com/dsyer/spring-boot-thin-launcher#readme

总之,在您的< code>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的缓存中受益(见下文的典型示例)。

在下面的参考文献中给出了< code>Dockerfile的原型,它可以避免在每次构建时重新下载所有Maven依赖项,前提是只接触了源代码文件(< code>src/*)

更准确地说,建议的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 基础映像本身小得多。
(如果您在开发阶段对该功能不感兴趣,可以从 openjdk:8u171-jre-alpineCOPY 中删除这些行,这些行来自=maven /app/app.jar ./app.jar

在这种方法中,Maven依赖项是通过<code>RUN mvn dependency:go-offline-B

然而,请注意,依赖项:离线标准目标并不“完美”,因为一些动态依赖项/插件仍可能在mvn包步骤触发一些重新下载。如果这是你的问题(例如,如果在某个时候你真的想离线工作),你可以看看另一个答案,该答案建议使用提供<code>de.qaware的专用插件。maven:离线maven插件:解决依赖关系目标。

 类似资料:
  • 我正在尝试在 Docker 容器中构建/部署Spring启动。 如您所见,我用第一个mvn命令缓存了所有的依赖项,这样我的代码应用程序中的每一个变化都不会触发新的大量依赖项下载。它适用于大多数依赖项,但仍有一些需要下载(即使缓存)。这是第二个mvn命令(包)的日志: (顺便说一句,(0 B在0 B / s)有点奇怪...只是一张支票? 如果我基于第一个maven命令之后的步骤启动一个容器(mvn依

  • 我正在使用sh文件,其中包含所有专家配置,密钥,专家命令。当我运行容器时,它每次都会一次又一次地下载依赖项,它无法缓存依赖项。 下面是我的Dockerfile的外观: 下面是Test.sh文件 当我创建docker映像并运行容器时,每当我运行它时,它就会一次又一次地下载依赖项。

  • 我想在Docker多阶段构建的构建阶段的一层中缓存Maven依赖项。 我的泊坞文件如下所示: ``` 我基于Docker多阶段构建博文(也可以在Github上获得)中提供的例子编写了这个Docker文件。 当我运行构建时,我看到的不是下载一次依赖项,然后由,而是通过两个步骤下载的依赖项。 有人让这个工作吗?我在这里做错了什么?

  • 对于我的一些模块,Intellij的想法不是导入依赖项和插件。只导入活循环。 当我在命令行编译时,POM是有效的。我已经删除了模块并重新导入它们。我删除了他们的项目,并重新导入它。 所以我想知道Intellij在模块上的想法信息被删除后是否有一些秘密缓存。

  • 我有一个简单的Quarkus应用程序,我尝试使用以下多级Dockerfile构建它。 还有pom.xml 构建工作正常,它下载maven依赖项,然后它创建. jar并在最终容器中运行. jar。但是如果我更改源代码中的某些内容并保持pom.xml不变,依赖项会再次下载。似乎并没有下载所有依赖项。 有没有办法以这种方式加速docker构建?例如,我对Spring Boot做了同样的事情,那里一切都很

  • 根据JUnit新发布的V.5.2,现在有一个BOM: JUnit BOM:为了使用Maven或Gradle简化依赖性管理,现在在org.JUnit:jUnit-BOM:5.2.0 Maven坐标下提供了一个物料清单POM。 但是有了这个IntelliJ IDEA(v.2018.1.3Ultimate x64,Maven v.3.5.3,target JDK 10.0.1)似乎不知道标记是什么,也不