Maven docker cache dependencies
Maven docker cache dependencies
我正在尝试使用Docker来自动化Maven构建。我想要构建的项目需要将所有依赖项下载大约20分钟,所以我尝试构建一个Docker镜像来缓存这些依赖项,但似乎没有保存下来。我的Dockerfile是这样的:
FROM maven:alpine RUN mkdir -p /usr/src/app WORKDIR /usr/src/app ADD pom.xml /usr/src/app RUN mvn dependency:go-offline
镜像构建成功,并且确实下载了所有内容。然而,生成的镜像与基本的`maven:alpine`镜像大小相同,所以似乎没有在镜像中缓存这些依赖项。当我尝试使用镜像来`mvn compile`时,会再次花费整整20分钟重新下载一遍所有内容。
有没有可能构建一个可以缓存我的依赖项的Maven镜像,这样每次使用镜像进行构建时就不再需要重新下载?
我运行以下命令:
docker build -t my-maven . docker run -it --rm --name my-maven-project -v "$PWD":/usr/src/mymaven -w /usr/src/mymaven my-maven mvn compile
我的理解是,在Docker构建过程中,无论`RUN`做了什么,都会成为最终镜像的一部分。
问题原因:在使用的基础镜像中,存在一个父镜像定义了VOLUME "$USER_HOME_DIR/.m2"
,导致在构建过程中所有文件都被写入到$USER_HOME_DIR/.m2
,但由于这个目录被定义为一个卷,所以这些文件不会与容器镜像一起保留。
解决方法:由于Docker目前没有任何方法来注销该卷定义,因此需要构建一个单独的maven镜像,而不是使用官方的maven镜像。可以使用Docker卷,并告诉maven使用不同的路径作为maven仓库缓存,方法如下-Dmaven.repo.local=/mvn/.m2nrepo/repository
。
注意:这些镜像不再挂载~/.m2作为一个卷,相关链接如下:github.com/carlossg/docker-maven/issues/11 github.com/carlossg/docker-maven/issues/36,自2017年12月以来,该卷声明已不再存在,详情请参见github.com/carlossg/docker-maven/pull/57。
自Docker v18.03版本开始,可以使用BuildKit来替代之前提到的卷,BuildKit允许挂载缓存以在构建之间持久存在,并且可以避免每次下载相应的.m2/repository内容。
假设Dockerfile在项目的根目录中:
# syntax = docker/dockerfile:1.0-experimental FROM maven:3.6.0-jdk-11-slim AS build COPY . /home/build RUN mkdir /home/.m2 WORKDIR /home/.m2 USER root RUN --mount=type=cache,target=/root/.m2 mvn -f /home/build/pom.xml clean compile
target=/root/.m2
将缓存挂载到maven镜像Dockerfile中指定的位置。
构建时可以运行以下命令:
DOCKER_BUILDKIT=1 docker build --rm --no-cache .
有关BuildKit的更多信息可以在此处找到:[https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md)。
这个问题是否适用于没有WSL2的Windows环境?
我自己没有在Windows上尝试过。但是根据[此处](https://github.com/moby/buildkit/issues/616)的说明,Windows上使用BuildKit的过程并不是很顺利。
Maven Docker 缓存依赖项的问题出现的原因是,当尝试启动 Docker 镜像构建时,pom.xml
文件通常不会有任何更改,只会有其他源代码的更改。在这种情况下,可以进行以下操作:
1. 添加 pom.xml
文件。
2. 然后执行 mvn verify --fail-never
命令,它将下载 Maven 依赖项。
3. 添加所有源代码文件,并开始编译(mvn package
)。
当 pom.xml
文件发生变化或首次运行此脚本时,Docker 将按照 1 -> 2 -> 3 的顺序执行。当 pom.xml
文件没有变化时,Docker 将跳过步骤 1 和 2,并直接执行步骤 3。
这个简单的技巧在许多其他的包管理情况下(如 Gradle、Yarn、NPM、PIP)也可以使用。
然而,根据其他评论和答案的建议,还应考虑使用 mvn dependency:resolve
或 mvn dependency:go-offline
命令。
值得注意的是,dependency:resolve
命令不会下载插件。而且不幸的是,dependency:resolve-plugins
命令也会漏掉生命周期插件。因此,建议使用 dependency:go-offline
命令。
根据我的经验,dependency:go-offline
命令下载的内容要比 mvn verify --fail-never
命令少得多,而且它们都无法下载我项目所需的所有内容。
当构建推送到 Docker Hub 注册表的 Docker 镜像时,使用 --fail-never
不是一个好主意,特别是通过自动构建。
这个解决方案是一个聪明而优雅的解决方法,可以与 Docker 缓存一起使用,以实现预期的行为。