在Docker中更快地构建Maven项目

目录
  • 一. 概述
  • 二. 常规多阶段构建镜像
  • 三. 使用 Buildkit 构建镜像
  • 四. 使用依赖分层的方式构建镜像
  • 五. 在 Buildkit 构建期间使用卷挂载
  • 六. 使用 Maven 守护进程构建镜像
  • 七. 结论
  • 参考文章

一. 概述

本文将通过如下几个方式来构建 docker 镜像,通过记录每种方式的构建时间,从而得到在 Docker 中构建 Maven 项目最快的方式:

  • 常规多阶段构建镜像
  • 使用 Buildkit 构建镜像
  • 使用依赖分层的方式构建镜像
  • 在 Buildkit 构建期间使用卷挂载
  • 使用 Maven 守护进程构建镜像

在每次运行之间,我们通过添加一个空行来更改源代码;在每个部分之间,我们删除所有构建的镜像,包括作为多阶段构建结果的中间镜像,这样是为了避免重复使用以前构建的镜像,以便得到每种方式更准确的构建时间。下面使用一个简单的 spring boot 项目进行测试。

二. 常规多阶段构建镜像

这是相关的Dockerfile:

FROM openjdk:11-slim-buster as build                         

COPY .mvn .mvn
COPY mvnw .
COPY pom.xml .
COPY src src                                                 

RUN ./mvnw -B package                                        

FROM openjdk:11-jre-slim-buster                              

COPY --from=build target/fast-maven-builds-1.0.jar .         

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "fast-maven-builds-1.0.jar"]     

让我们执行构建:

time DOCKER_BUILDKIT=0 docker build -t fast-maven:1.0 .

暂时忘记环境变量,我将在下一节中解释

以下是五次运行的结果:

* 0.36s user 0.53s system 0% cpu 1:53.06 total
* 0.36s user 0.56s system 0% cpu 1:52.50 total
* 0.35s user 0.55s system 0% cpu 1:56.92 total
* 0.36s user 0.56s system 0% cpu 2:04.55 total
* 0.38s user 0.61s system 0% cpu 2:04.68 total

三. 使用 Buildkit 构建镜像

前面的命令行使用了DOCKER_BUILDKIT环境变量,这是告诉 Docker 使用旧引擎的方式。如果你有一段时间没有更新 Docker,那是你正在使用的引擎。如今,BuildKit已取代它,成为新的默认设置。

BuildKit 带来了多项性能改进:

  • 自动垃圾收集
  • 并发依赖解析
  • 高效的指令缓存
  • 构建缓存导入/导出
  • 等等。

让我们在新引擎上重新执行之前的命令:

time docker build -t fast-maven:1.1 .

这是第一次运行的控制台日志的摘录:

...
 => => transferring context: 4.35kB
 => [build 2/6] COPY .mvn .mvn
 => [build 3/6] COPY mvnw .
 => [build 4/6] COPY pom.xml .
 => [build 5/6] COPY src src
 => [build 6/6] RUN ./mvnw -B package
...

0.68s user 1.04s system 1% cpu 2:06.33 total

相同命令的以下执行具有稍微不同的输出:

...
 => => transferring context: 1.82kB
 => CACHED [build 2/6] COPY .mvn .mvn
 => CACHED [build 3/6] COPY mvnw .
 => CACHED [build 4/6] COPY pom.xml .
 => [build 5/6] COPY src src
 => [build 6/6] RUN ./mvnw -B package
...

请记住,我们在两次运行之间更改了源代码。我们不会更改的文件,即.mvn,mvnw和pom.xml,由 BuildKit 缓存。但是这些资源很小,因此缓存不会显着改善构建时间。

* 0.69s user 1.01s system 1% cpu 2:05.08 total
* 0.65s user 0.95s system 1% cpu 1:58.51 total
* 0.68s user 0.99s system 1% cpu 1:59.31 total
* 0.64s user 0.95s system 1% cpu 1:59.82 total

快速浏览日志发现构建中的最大瓶颈是所有依赖项(包括插件)的下载。每次我们更改源代码时都会发生这种情况,这就是 BuildKit 没有提高性能的原因。

四. 使用依赖分层的方式构建镜像

我们应该将精力集中在依赖关系上。为此,我们可以利用层并将构建分为两个步骤:

  • 第一步,我们下载依赖
  • 在第二个,我们做适当的包装

每一步都会创建一个层,第二个取决于第一个。
通过分层,如果我们在第二层更改源代码,则第一层不受影响,可以重复使用。我们不需要再次下载依赖项。新的Dockerfile看起来像:

FROM openjdk:11-slim-buster as build

COPY .mvn .mvn
COPY mvnw .
COPY pom.xml .

RUN ./mvnw -B dependency:go-offline                          

COPY src src

RUN ./mvnw -B package                                        

FROM openjdk:11-jre-slim-buster

COPY --from=build target/fast-maven-builds-1.2.jar .

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "fast-maven-builds-1.2.jar"]

注意:go-offline不会下载所有内容。如果您尝试使用该-o选项(用于离线),该命令将不会成功运行。这是一个众所周知的老错误。在所有情况下,它都“足够好”。

让我们运行构建:

time docker build -t fast-maven:1.2 .

第一次运行比常规构建花费更多的时间:

0.84s user 1.21s system 1% cpu 2:35.47 total

但是,后续构建要快得多。更改源代码仅影响第二层,不会触发(大多数)依赖项的下载:

* 0.23s user 0.36s system 5% cpu 9.913 total
* 0.21s user 0.33s system 5% cpu 9.923 total
* 0.22s user 0.38s system 6% cpu 9.990 total
* 0.21s user 0.34s system 5% cpu 9.814 total
* 0.22s user 0.37s system 5% cpu 10.454 total

五. 在 Buildkit 构建期间使用卷挂载

分层构建大大缩短了构建时间,不过还有一个问题,更改单个依赖项会使镜像依赖的层无效,因此我们需要再次下载所有依赖项。

幸运的是,BuildKit在构建期间(而不仅仅是在运行期间)引入了卷挂载。有多种类型的挂载可用,但我们感兴趣的一种是缓存挂载。这是一项实验性功能,因此您需要明确选择加入:

Dockerfile看起来像:

# syntax=docker/dockerfile:experimental
FROM openjdk:11-slim-buster as build

COPY .mvn .mvn
COPY mvnw .
COPY pom.xml .
COPY src src

# 使用缓存构建
RUN --mount=type=cache,target=/root/.m2,rw ./mvnw -B package 

FROM openjdk:11-jre-slim-buster

COPY --from=build target/fast-maven-builds-1.3.jar .

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "fast-maven-builds-1.3.jar"]

其中 # syntax=docker/dockerfile:experimental 用来开启实验性功能。
使用如下命令构建镜像:

time docker build -t fast-maven:1.3 .

构建时间高于常规构建,但仍低于分层构建:

0.71s user 1.01s system 1% cpu 1:50.50 total

以下构建与层相当:

* 0.22s user 0.33s system 5% cpu 9.677 total
* 0.30s user 0.36s system 6% cpu 10.603 total
* 0.24s user 0.37s system 5% cpu 10.461 total
* 0.24s user 0.39s system 6% cpu 10.178 total
* 0.24s user 0.35s system 5% cpu 10.283 total

六. 使用 Maven 守护进程构建镜像

使用 Maven 守护进程构建镜像的 Dockerfile 文件内容如下:

FROM openjdk:11-slim-buster as build
# 下载最新版本的 Maven 守护进程
ADD https://github.com/mvndaemon/mvnd/releases/download/0.6.0/mvnd-0.6.0-linux-amd64.zip .
# 更新包索引
RUN apt-get update \
# 安装 unzip
 && apt-get install unzip \
# 创建专用文件夹
 && mkdir /opt/mvnd \
# 提取我们在前面下载的 mvnd
 && unzip mvnd-0.6.0-linux-amd64.zip \
# 将提取的存档内容移动到之前创建的文件夹
 && mv mvnd-0.6.0-linux-amd64/* /opt/mvnd                    

COPY .mvn .mvn
COPY mvnw .
COPY pom.xml .
COPY src src
# 使用 mvnd 代替 Maven 包装器
RUN /opt/mvnd/bin/mvnd -B package                            

FROM openjdk:11-jre-slim-buster

COPY --from=build target/fast-maven-builds-1.4.jar .

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "fast-maven-builds-1.4.jar"]

使用下面的命令构建镜像:

time docker build -t fast-maven:1.4 .

日志输出如下:

* 0.70s user 1.01s system 1% cpu 1:51.96 total
* 0.72s user 0.98s system 1% cpu 1:47.93 total
* 0.66s user 0.93s system 1% cpu 1:46.07 total
* 0.76s user 1.04s system 1% cpu 1:50.35 total
* 0.80s user 1.18s system 1% cpu 2:01.45 total

与常规构建镜像相比没有显着改善。

七. 结论

以下是所有执行时间的汇总:

基线 构建工具包 图层 卷挂载 MVND
#1 (S) 113.06 125.08 155.47 110.5 111.96
#2 (S) 112.5 118.51 9.91 9.68 107.93
#3 (S) 116.92 119.31 9.92 10.6 106.07
#4 (S) 124.55 119.82 9.99 10.46 110.35
#5 (S) 124.68 9.81 10.18 121.45
#6 (S) 10.45 10.28
#7 (S) 44.71
平均(秒) 118.34 120.68 9.91 10.24 111.55
偏差 28.55 6.67 0.01 0.10 111.47
基线增益 (S) 0 -2.34 108.43 108.10 6.79
% 获得 0.00% -1.98% 91.63% 91.35% 5.74%

在 Docker 中加快 Maven 构建的性能与常规构建有很大不同,限制因素是依赖项的下载速度,需要使用层来缓存依赖项。
对于 BuildKit,建议使用新的缓存挂载功能,以避免在层失效时下载所有依赖项。

参考文章

https://blog.frankel.ch/faster-maven-builds/2/

到此这篇关于在Docker中更快地构建Maven项目的文章就介绍到这了,更多相关Docker构建Maven项目内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Maven插件构建Docker镜像的实现步骤

    背景 微服务架构下,微服务在带来良好的设计和架构理念的同时,也带来了运维上的额外复杂性,尤其是在服务部署和服务监控上.单体应用是集中式的,就一个单体跑在一起,部署和管理的时候非常简单,而微服务是一个网状分布的,有很多服务需要维护和管理,对它进行部署和维护的时候则比较复杂. 下面从Dev的角度来看一下Ops的工作.从Dev提交代码,到完成集成测试的一系列步骤如下: 首先是开发人员把程序代码更新后上传到Git,然后其他的事情都将由Jenkins自动完成. 然后Git在接收到用户更新的代码后,会把消息

  • docker搭建jenkins+maven代码构建部署平台

    目录 Docker基本概念 Docker安装过程(Centos6.9) 升级内核 安装docker-io 基于Docker的jenkins安装 拉取应用 运行容器 跟踪应用log 关闭打开容器 tomcat账户配置 配置jenkins,构建部署war应用 配置JDK.Maven等参数 新建任务构建部署 通过ssh脚本部署maven项目到指定Tomcat中 配置Publish Over SSH插件 编写部署ssh脚本 项目构建配置 由于项目开发,经常需要在本地搭一些环境进行开发或测试,大家知道搭环

  • Docker构建Maven+Tomcat基础镜像的实现

    前言 在 Java 编程中,大多数应用都是是基于 Maven 构建的,而交付的结果大多是采用的 Tomcat 的 war 包形式,所以,构建一个基于 Maven 和 Tomcat 的基础镜像很有必要,不仅能帮助我们提升平时自主实验研究分析的效率,还可以在一定程度能减少运维减少编写 Dockerfile 的复杂度,提升整体项目交付效率. 1.创建编译目录 mkdir -p build_docker cd build_docker vim Dockerfile 2.编写 Dockerfile 首先,

  • 浅谈使用Maven插件构建Docker镜像的方法

    本文介绍了使用Maven插件构建Docker镜像的方法,分享给大家,具体如下: 工具 工欲善其事,必先利其器.笔者经过调研,有以下几款Docker的Maven插件进入笔者视野: 插件名称 官方地址 docker-maven-plugin https://github.com/spotify/docker-maven-plugin docker-maven-plugin https://github.com/fabric8io/docker-maven-plugin docker-maven-pl

  • Docker使用 Maven 插件构建镜像的方法

    通过 Maven 的 Docker 插件可以构建 Docker 镜像 快速入门 在 pom.xml 中添加 Docker 插件 <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>0.4.13</version> <configuration> <imageName>

  • 在Docker中更快地构建Maven项目

    目录 一. 概述 二. 常规多阶段构建镜像 三. 使用 Buildkit 构建镜像 四. 使用依赖分层的方式构建镜像 五. 在 Buildkit 构建期间使用卷挂载 六. 使用 Maven 守护进程构建镜像 七. 结论 参考文章 一. 概述 本文将通过如下几个方式来构建 docker 镜像,通过记录每种方式的构建时间,从而得到在 Docker 中构建 Maven 项目最快的方式: 常规多阶段构建镜像 使用 Buildkit 构建镜像 使用依赖分层的方式构建镜像 在 Buildkit 构建期间使用

  • Android中使用Gradle来构建App项目的入门指南

    gradle是Android开发中引入的全新的构建系统,因为全新的构建系统主要是出于下面的目的: 1. 方便复用代码和资源 2. 构建多种版本的apk更见简单,不论是为多渠道构建不同的apk还是构建不同环境的apk(debug,release) 3. 方便配置,扩展,自定义构建过程 4. 良好的IDE集成 为什么选择Gradle? Gradle主要有以下几个有点: 1. 使用领域驱动语言(DSL)来描述构建逻辑 2. 构建脚本使用Groovy,可以方便的定制构建逻辑 3. 内建的依赖管理系统,使

  • Eclipse 使用Maven构建SpringMVC项目

    首先Eclipse需要安装Maven的插件,地址:http://m2eclipse.sonatype.org/sites/m2e. 用MyEclipse安装Maven插件,建出的Maven项目有些问题.一是,发布tomcat的时候resources总是不会被发布到tomcat下:二是,把WEB-INF下的classes改到target下的classes,但是不知道为什么MyEclipse要么仍然在WEB-INF下生成class.要么真不在WEB-INF生成classes了但是发布tomcat的时

  • Eclipse+Maven构建Hadoop项目的方法步骤

    Maven 翻译为"专家"."内行",是 Apache 下的一个纯 Java 开发的开源项目.基于项目对象模型(Project Object Model 缩写:POM)概念,Maven利用一个中央信息片断能管理一个项目的构建.报告和文档等步骤.Maven 是一个项目管理工具,可以对 Java 项目进行构建.依赖管理. 在开发一些大型项目的时候,需要用到各种各样的开源包jar,为了方便管理及加载jar,使用maven开发项目可以节省大量时间且方便项目移动至新的开发环境

  • eclipse构建和发布maven项目的教程

    对于maven的部署和安装插件不熟的同学可以看一下上两篇文章maven的部署和安装: 此笔记已经集成了maven的插件. 一.构建Maven项目 用eclipse构建maven项目之前我们需要为eclipse做一些必要的配置 一些必要的配置 1.点击eclipse菜单栏的Window下的preferences,选择Maven菜单 2.选择Installations,选择Add,添加你本地安装的maven安装目录,如下所示. 3.点击Apply应用,然后选择左侧菜单的User Settings,为

  • maven项目在svn中的上传与检出的方法

    前言 企业开发中经常使用svn来为我们控制代码版本,也经常使用maven来管理项目.下面将介绍一下如何将maven项目上传到svn中,如何将项目从svn中检出. 上传到svn maven项目上传与普通项目上传并无区别.这里做一下简单介绍: 右击项目:选择Team:选择Share Project: 仓库类型选择svn 点击next 选择你要分享的资源库.点击next 选择使用指定的模块名.然后在浏览中选择要上传的位置.然后点击finish. 点击finish后进入同步视图界面.这个界面需要我们选择

  • 解决Maven项目加载spring bean的配置xml文件会提示找不到问题

    Maven 加载spring bean的配置xml文件会提示找不到 如果你也在开发spring项目时用的是maven项目,如果出现运行是: ***xml can not open ,because it does not exist. 解决方法 很简单,因为maven需要将你的配置文件即***.xml放到根目录下,就是/src/main/java/这个目录下. 如果你把配置文件放到了自己新建的config文件夹中,记住也要放到这个目录里面,然后在 ApplicationContext ctx =

  • Java Maven构建工具中mvnd和Gradle谁更快

    目录 1.mvnd 简介 2.Gradle 简介 2.1 Gradle 优点简述 3.Gradle 使用 3.1 更换 Gradle 为国内源 3.2 项目依赖文件对比 3.3 settings.gradle VS build.gradle 3.4 打包项目 4.mvnd 5.性能对比 5.1 Maven 打包性能 5.2 mvnd 打包性能 5.3 Gradle 打包性能 6.扩展:Gradle 打包文件存放目录 总结 前言; Maven 作为经典的项目构建工具相信很多人已经用很久了,但如果体

  • Docker中镜像构建文件Dockerfile与相关命令的详细介绍

    前言 使用docker build命令或使用Docker Hub的自动构建功能构建Docker镜像时,都需要一个Dockerfile文件.Dockerfile文件是一个由一系列构建指令组成的文本文件,docker build命令会根据这些构建指令完成Docker镜像的构建.本文将会介绍Dockerfile文件,及其中使用的构建指令. 1. Dockerfile文件使用 docker build命令会根据Dockerfile文件及上下文构建新Docker镜像.构建上下文是指Dockerfile所在

  • maven项目在实践中的构建管理之路的方法

    前言 最近一个月参与了公司几个项目的脚手架构建,适当总结下经验.之前见过太多项目依赖,构建,管理混乱不堪,导致后续的维护性差,甚至出现由此引发的事故.当时就有一个规范管理的想法. 依赖管理 依赖管理,其实就是依赖范围的管理.这里我叫他 依赖池.也就是 所有相关项目的依赖只能从这个池子里拿,不能超出其范围.池子里的依赖我们定义为都是久经考验的同志.以maven工程为例,我们可以定义 一个名为ooxx-dependencies 的 pom 类型的工程.这里用来存放我们经过技术选型并测试通过的依赖.每

随机推荐