在告诉你我如何处理之前,先解释一下你遇到的问题。
你的 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更改都重新下载所有依赖项是可接受的。
例如,通过使用你的脚本开始:
FROM maven:3.5-jdk-8 as maven_build
WORKDIR /app
COPY pom.xml .
RUN mvn clean package -Dmaven.main.skip -Dmaven.test.skip && rm -r target
COPY src ./src
RUN mvn clean package -Dmaven.test.skip
FROM java:8
WORKDIR /app
COPY --from=maven_build /app/target/*.jar
ENV JAVA_OPTS ""
CMD [ "bash", "-c", "java ${JAVA_OPTS} -jar *.jar -v"]
那种解决方案的缺点是什么?
任何pom.xml中的更改都意味着重新创建整个层以下载并存储maven依赖项。
对于具有许多依赖项的应用程序来说,这通常是不可接受的,尤其是在镜像构建期间不使用maven存储库管理器的情况下。
2)为maven依赖项显式设置缓存:需要更多配置和使用buildkit,但这更有效,因为只会下载所需的依赖项。
唯一变化的是,在docker构建器缓存中缓存了maven依赖项的下载:
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
FROM java:8
WORKDIR /app
COPY --from=maven_build /app/target/*.jar
ENV JAVA_OPTS ""
CMD [ "bash", "-c", "java ${JAVA_OPTS} -jar *.jar -v"]
为了启用BuildKit,必须设置环境变量
DOCKER_BUILDKIT=1
(您可以在任何地方进行设置:bashrc、命令行、Docker守护程序JSON文件...)。