写点什么

Docker 多阶段构建实战 (multi-stage builds)(1)

作者:Java高工P7
  • 2021 年 11 月 11 日
  • 本文字数:2295 字

    阅读完需:约 8 分钟

  1. 有些工具在构建过程中会用到,但是最终的镜像是不需要的(例如用 maven 编译构建 java 工程),这要求 Dockerfile 的编写者花更多精力来清理这些工具,清理的过程又可能导致新的 layer;


为了解决上述问题,从 17.05 版本开始 Docker 在构


【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


建镜像时增加了新特性:多阶段构建(multi-stage builds),将构建过程分为多个阶段,每个阶段都可以指定一个基础镜像,这样在一个 Dockerfile 就能将多个镜像的特性同时用到,例如:先用 maven 镜像构建 java 工程,再把构建结果和 jre 合成,就做成了一个可以直接运行 java 工程镜像了;


官方描述如下图所示,地址是:https://docs.docker.com/develop/develop-images/multistage-build/



官方的实例是 golang 的,今天我们以 maven 构建 springboot 工程为例,实战如何使用 multi-stage 特性构建 java 微服务镜像;

环境信息

本次实战的环境信息如下:


  1. 操作系统:Ubuntu 18.04.2 LTS

  2. Docker:18.06.1-ce

  3. Java:1.8.0_191

  4. Maven:3.6.1

实战源码

本次实战用到的源码是个普通 springboot 工程,功能是 SpringCloud 中的注册中心 eureka,您可以在 Github 下载,地址和链接如下所示:


| 名称 | 链接 | 备注 |


| :-- | :-- | :-- |


| 项目主页 | https://github.com/zq2599/blog_demos | 该项目在 GitHub 上的主页 |


| git 仓库地址(https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https 协议 |


| git 仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh 协议 |


这个 git 项目中有多个文件夹,本章源码在 springcloudscaledemo 这个文件夹下,如下图红框所示:



springcloudscaledemo 文件夹内有三个工程,本次实战用到的是 eureka-server,如下图:


准备材料

在能正常运行 docker 的电脑上新建一个目录,例如我这里是 ubuntu 系统上/home/willzhao/temp/201906/02,将 maven 工程 eureka-server 复制到这个目录下;

避免每次构建镜像都下载工程所需的 jar 包

构建镜像过程中会用 maven 构建 springboot 工程,会下载 springboot 工程依赖的 jar 包,此过程很漫长,如果您多次构建 Dockerfile 镜像,那么每次都要经历这个过程,为了避免每次都下载,请做如下操作:


  1. 找一个可以运行 maven 的环境,把 eureka-server 工程复制到这个环境上;

  2. 在 eureka-server 目录下执行命令 mvn clean package -U -DskipTests,开始构建此工程;

  3. 构建成功后,进入本地的 maven 缓存目录,通常是用户的 home 目录下的.m2 文件夹,里面有个名为 repository 的目录;

  4. 将整个 repository 目录复制到前面提到的/home/willzhao/temp/201906/02 目录下,和 eureka-server 放在同一目录,如下图:



这样,在编写 Dockerfile 的时候只要用这个 repository 覆盖镜像中的 maven 缓存,在编译时就不会去 maven 的中央仓库下载 jar 了,会节省很多时间。

编写 Dockerfile

在/home/willzhao/temp/201906/02 目录下创建文件 Dockerfile,内容如下所示:

Docker image for multi stage build

VERSION 0.0.1

Author: bolingcavalry

第一阶段,用 maven 镜像进行编译

FROM maven:3.6.1 AS compile_stage


####################定义环境变量 start####################


#定义工程名称,也是源文件的文件夹名称


ENV PROJECT_NAME eureka-server


#定义工作目录


ENV WORK_PATH /usr/src/$PROJECT_NAME


####################定义环境变量 start####################


#作者


MAINTAINER BolingCavalry zq2599@gmail.com


#将源码复制到当前目录


COPY ./WORK_PATH


#如果前面您已经准备好了 repository 目录,就可以用来替换镜像中的 repository 目录了,先删除镜像中已有的 repository


RUN rm -rf /root/.m2/repository


#将准备好的 repository 文件夹复制进来,这样相当于镜像环境中已经有了 java 工程所需的 jar,可以避免去 maven 中央仓库下载


COPY ./repository /root/.m2/repository


#编译构建


RUN cd $WORK_PATH && mvn clean package -U -DskipTests

第二阶段,用第一阶段的 jar 和 jre 镜像合成一个小体积的镜像

FROM java:8-jre-alpine


####################定义环境变量 start####################


#定义工程名称,也是源文件的文件夹名称


ENV PROJECT_NAME eureka-server


#定义工程版本


ENV PROJECT_VERSION 0.0.1-SNAPSHOT


#定义工作目录


ENV WORK_PATH /usr/src/$PROJECT_NAME


####################定义环境变量 start####################


#安全起见不用 root 账号,新建用户 admin


RUN adduser -Dh /home/admin admin


#工作目录是/app


WORKDIR /app


#从名为 compile_stage 的 stage 复制构建结果到工作目录


COPY --from=compile_stage {PROJECT_NAME}-${PROJECT_VERSION}.jar .


#启动应用


CMD ["sh", "-c", "java -jar /app/{PROJECT_VERSION}.jar --spring.profiles.active=dev"]


上面就是分成了两个阶段构建的 Dockerfile 脚本,请参考每行的注释来理解,有以下几点需要重点关注:


  1. 一共有两次 FROM 指令出现,而最终的镜像是基于最后一个 FROM 生成的;

  2. PROJECT_NAME 这个环境变量被定义了两次(ENV PROJECT_NAME),因为前面阶段定义的环境变量在后面的阶段是用不了的;

  3. COPY --from=compile_stage 这个命令,可以将指定阶段的文件复制到当前阶段来,这一步很关键,第一阶段用 maven 构建出来的 jar 文件,通过该命令复制到后面的阶段来使用了;

  4. 最后一个 FROM 是 java:8-jre-alpine,这是精简版的 java 运行环境镜像,最终镜像的内容就是 jre 和 maven 的构建结果,而前面的 maven 镜像和最终构建结果无关;

构建镜像

在 Dockerfile 所在目录执行以下命令即可构建镜像:


docker build -t bolingcavalry/multi-stage-build:0.0.1-SNAPSHOT .

用户头像

Java高工P7

关注

还未添加个人签名 2021.11.08 加入

还未添加个人简介

评论

发布
暂无评论
Docker多阶段构建实战(multi-stage builds)(1)