在告诉你我将如何处理之前,我将解释你遇到的问题。
您的 Dockerfile 依赖于构建多阶段功能。
这里的阶段被认为是中间层,在最终图像中不作为层保留。要在图层之间保留文件/文件夹,您必须在完成时显式复制它们。
因此,具体而言,这意味着在以下说明中:maven 会解析您的 pom.xml 中指定的所有依赖项,并将它们存储在位于该阶段层的本地存储库中:
FROM maven:3.5-jdk-8 as mavenDeps
COPY pom.xml pom.xml
RUN mvn dependency:resolve
但如前所述,默认情况下不保留舞台内容。因此,本地 maven 存储库中所有下载的依赖项都将丢失,因为您永远不会在下一阶段复制它:
FROM mavenDeps as mavenBuild
RUN mvn install
由于该图像的本地存储库为空:mvn install 重新下载所有依赖项。
如何处理?
你真的有很多很多方法。
最佳选择取决于您的要求。
但无论如何,docker 层的构建策略看起来像:
构建阶段(Maven 映像):
- pom 复制到图片
- 依赖项和插件下载。
关于这一点,mvn dependency:resolve-plugins 链接到 mvn dependency:resolve 可能会完成这项工作,但并非总是如此。
为什么 ?因为这些插件和package 执行可能依赖于不同的工件/插件,甚至对于相同的工件/插件,这些可能仍会拉取不同的版本。
因此,一种更安全但可能更慢的方法是通过完全执行 mvn package 命令(这将完全提取您需要的依赖项)来解决依赖关系,但跳过源编译并删除目标文件夹以加快处理速度并防止任何该步骤的不良层变化检测。
- 源代码复制到图片
- 打包应用程序
运行阶段(JDK 或 JRE 映像):
1) 没有显式缓存 maven 依赖项:直接但在 pom 频繁更改时很烦人
如果在每次 pom.xml 更改时重新下载所有依赖项是可以接受的。
从你的脚本开始的例子:
########build stage########
FROM maven:3.5-jdk-8 as maven_build
WORKDIR /app
COPY pom.xml .
# To resolve dependencies in a safe way (no re-download when the source code changes)
RUN mvn clean package -Dmaven.main.skip -Dmaven.test.skip && rm -r target
# To package the application
COPY src ./src
RUN mvn clean package -Dmaven.test.skip
########run stage########
FROM java:8
WORKDIR /app
COPY --from=maven_build /app/target/*.jar
#run the app
ENV JAVA_OPTS ""
CMD [ "bash", "-c", "java ${JAVA_OPTS} -jar *.jar -v"]
该解决方案的缺点?
pom.xml 中的任何更改都意味着重新创建下载和存储 maven 依赖项的整个层。
对于具有许多依赖项的应用程序来说,这通常是不可接受的,总体而言,如果您在映像构建期间不使用 maven 存储库管理器。
2) maven 依赖项的显式缓存:需要更多配置和使用 buildkit,但这更有效,因为只下载所需的依赖项
这里唯一改变的是 maven 依赖项下载缓存在 docker builder 缓存中:
# syntax=docker/dockerfile:experimental
########build stage########
FROM maven:3.5-jdk-8 as maven_build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN --mount=type=cache,target=/root/.m2 mvn clean package -Dmaven.test.skip
########run stage########
FROM java:8
WORKDIR /app
COPY --from=maven_build /app/target/*.jar
#run the app
ENV JAVA_OPTS ""
CMD [ "bash", "-c", "java ${JAVA_OPTS} -jar *.jar -v"]
要启用 buildkit,必须设置环境变量 DOCKER_BUILDKIT=1(您可以在任何地方进行设置:bashrc、命令行、docker daemon json 文件...)