拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 使用Docker缓存Maven依赖项

使用Docker缓存Maven依赖项

白鹭 - 2022-03-07 2343 0 2

一、简介

在本教程中,我们将展示如何在Docker 中构建Maven 项目。首先,我们将从一个简单的单模块Java 项目开始,并展示如何利用Docker 中的多阶段构建来对构建过程进行docker 化。接下来,我们将展示如何使用Buildkit 来缓存多个构建之间的依赖关系。最后,我们将介绍如何在多模块应用程序中利用层缓存。

2.多阶段分层构建

在本文中,我们将使用Guava 作为依赖项创建一个简单的Java 应用程序。我们将使用maven-assembly 插件创建一个胖JAR。代码和Maven 配置将从本文中省略,因为它们不是主要主题。

多阶段构建是优化Docker 构建过程的好方法。它们使我们能够将整个过程保存在一个文件中,还可以帮助我们保持Docker 映像尽可能小在第一阶段,我们将运行Maven 构建并创建我们的胖JAR,在第二阶段,我们将复制JAR 并定义一个入口点:

FROM maven:alpine as build
 ENV HOME=/usr/app
 RUN mkdir -p $HOME
 WORKDIR $HOME
 ADD . $HOME
 RUN mvn package
 FROM openjdk:8-jdk-alpine
 COPY --from=build /usr/app/target/single-module-caching-1.0-SNAPSHOT-jar-with-dependencies.jar /app/runner.jar
 ENTRYPOINT java -jar /app/runner.jar

这种方法可以让我们保持最终的Docker 镜像更小,因为它不包含Maven 可执行文件或我们的源代码。

让我们创建Docker 镜像:

docker build -t maven-caching .

接下来,让我们从镜像中启动一个容器:

docker run maven-caching

当我们更改代码中的某些内容并重新运行构建时,我们会注意到Mavenpackage任务之前的所有命令都被缓存并立即执行。由于我们的代码更改比项目依赖项更频繁,我们可以使用Docker 层缓存将依赖项下载和代码编译分开来缩短构建时间

FROM maven:alpine as build
 ENV HOME=/usr/app
 RUN mkdir -p $HOME
 WORKDIR $HOME
 ADD pom.xml $HOME
 RUN mvn verify --fail-never
 ADD . $HOME
 RUN mvn package
 FROM openjdk:8-jdk-alpine
 COPY --from=build /usr/app/target/single-module-caching-1.0-SNAPSHOT-jar-with-dependencies.jar /app/runner.jar
 ENTRYPOINT java -jar /app/runner.jar

当我们只更改代码时运行后续构建会快得多,因为Docker 将从缓存中获取层。

3. 使用BuildKit 进行缓存

Docker 版本18.09 引入了BuildKit 作为对现有构建系统的大修大修背后的想法是提高性能、存储管理和安全性。我们可以利用BuildKit 来保持多个构建之间的状态。这样,Maven 不会每次都下载依赖项,因为我们有永久存储。要在我们的Docker 安装中启用BuildKit,我们需要编辑daemon.json文件:

...
 {
 "features": {
 "buildkit": true
 }}
 ...

启用BuildKit 后,我们可以将Dockerfile 更改为:

FROM maven:alpine as build
 ENV HOME=/usr/app
 RUN mkdir -p $HOME
 WORKDIR $HOME
 ADD . $HOME
 RUN --mount=type=cache,target=/root/.m2 mvn -f $HOME/pom.xml clean package
 FROM openjdk:8-jdk-alpine
 COPY --from=build /usr/app/target/single-module-caching-1.0-SNAPSHOT-jar-with-dependencies.jar /app/runner.jar
 ENTRYPOINT java -jar /app/runner.jar

当我们更改代码或pom.xml文件时,Docker 将始终执行ADD 和RUN Maven 命令。首次运行时构建时间将是最长的,因为Maven 必须下载依赖项。随后的运行将使用本地依赖项并执行得更快。

这种方法需要维护Docker 卷作为依赖项的存储。有时,我们必须强制Maven 使用Dockerfile 中的-U标志更新我们的依赖项。

4. 多模块Maven 项目的缓存

在前面的部分中,我们展示了如何利用不同的方法来加快单模块Maven 项目的Docker 映像的构建时间。对于更复杂的应用,这些方法不是最佳的。多模块Maven 项目通常有一个模块作为我们应用程序的入口点。一个或多个模块包含我们的逻辑并被列为依赖项。

由于子模块被列为依赖项,它们将阻止Docker 进行层缓存并触发Maven 再次下载所有依赖项。BuildKit 的这个解决方案在大多数情况下都很好但正如我们所说,它可能需要不时强制更新以获取更新的子模块。为了避免这种情况,我们可以将项目分层并使用Maven 增量构建:

FROM maven:alpine as build
 ENV HOME=/usr/app
 RUN mkdir -p $HOME
 WORKDIR $HOME
 ADD pom.xml $HOME
 ADD core/pom.xml $HOME/core/pom.xml
 ADD runner/pom.xml $HOME/runner/pom.xml
 RUN mvn -pl core verify --fail-never
 ADD core $HOME/core
 RUN mvn -pl core install
 RUN mvn -pl runner verify --fail-never
 ADD runner $HOME/runner
 RUN mvn -pl core,runner package
 FROM openjdk:8-jdk-alpine
 COPY --from=build /usr/app/runner/target/runner-0.0.1-SNAPSHOT-jar-with-dependencies.jar /app/runner.jar
 ENTRYPOINT java -jar /app/runner.jar

在这个Dockerfile 中,我们复制所有pom.xml文件并增量构建每个子模块,最后打包整个应用程序。经验法则是我们构建的子模块在链的后期更频繁地更改。

5. 结论

在本文中,我们介绍了如何使用Docker 构建Maven 项目。首先,我们介绍了如何利用分层来缓存不经常更改的部分。接下来,我们介绍了如何使用BuildKit 来保持构建之间的状态。最后,我们展示了如何使用增量构建构建多模块Maven 项目。



标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *