如何使用Docker多阶段构建来减小镜像大小

这篇文章主要为大家展示了“如何使用Docker多阶段构建来减小镜像大小”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何使用Docker多阶段构建来减小镜像大小”这篇文章吧。

发展壮大离不开广大客户长期以来的信赖与支持,我们将始终秉承“诚信为本、服务至上”的服务理念,坚持“二合一”的优良服务模式,真诚服务每家企业,认真做好每个细节,不断完善自我,成就企业,实现共赢。行业涉及办公空间设计等,在成都网站建设成都全网营销、WAP手机网站、VI设计、软件开发等项目上具有丰富的设计经验。

如何通过 Docker 的多阶段构建功能来大幅度减小镜像大小,适用于需要在 Dockerfile 中构建程式(如 javac),且需要另外安装编译工具链的镜像。(如 Java)

先来学习单词(本文全部采用中文词汇,如需查询外文文档可对照该词汇表。理论上个人不赞成翻译术语):

  • multi-stage 多阶段

  • build 构建

  • image 镜像

  • stage 阶段

再来看一下效果: 原 110M+,现 92M。

对比一下 Dockerfile

优化前 Dockerfile:

FROM openjdk:8u171-jdk-alpine3.8

ADD . /app
WORKDIR /app

RUN apk add maven \
  && mvn clean package \
  && apk del maven \
  && mv target/final.jar / \
  && cd / \
  && rm -rf /app \
  && rm -rf /root/.m2

ENTRYPOINT java -jar /final.jar

优化后 Dockerfile:

FROM openjdk:8u171-jdk-alpine3.8 as builder

ADD . /app
WORKDIR /app

RUN apk add maven \
  && mvn clean package \
  && apk del maven \
  && mv target/final.jar /

FROM openjdk:8u181-jre-alpine3.8 as environment
WORKDIR /
COPY --from=builder /final.jar .
ENTRYPOINT java -jar /final.jar

很明显,优化后的 Dockerfile 新增了 FROM AS 这个命令,并出现了两个 FROM。这就是多阶段构建。

了解一下多阶段构建

多阶段构建是 Docker 17.05 的新增功能,它可以在一个 Dockerfile 中使用多个 FROM 语句,以创建多个 Stages(阶段)。每个阶段间独立(来源请求),可以通过 COPY --from 来获取其它阶段的文件。我们来打个比方,把最终镜像比作一盘菜(炒青椒)。把原料青椒炒完后上桌。

# 对比清单
镜像 -> 一盘菜
第一个阶段 -> 炒
第二个阶段 -> 上桌

两个阶段的目标是做好(生成)最终的菜(镜像)。我们要做的是将第一个阶段「炒」出来的食物进行「上桌」。我们的目标是 做出菜,且 菜盘子(盛菜和中间产物)最轻。

可视化流程如下:

# 做菜流程
... 省略原料
原料 -> [第一个阶段——炒] # 此时盘子里有炒的工具、炒的结果和中间产物
# 这时候开启第二个阶段,只保留炒的结果,而不再需要其它。
-> 炒的结果 -> [开始上桌,只保留结果] # 把炒出来的青椒拿来(COPY --from),其它不要
-> 最终是一盘菜。

现在应该大致理解多阶段构建的流程了吧。我们把话筒交给 Java,看看在 Dockerfile 中使用编译工具构建一个 JAR,并只保留构建完的 JAR 和运行时交给 Image,其它则扔掉应该怎么做:

# 第一阶段——编译(炒)
FROM openjdk:8u171-jdk-alpine3.8 as builder # 自带编译工具

ADD . /app
WORKDIR /app

RUN ... 省略编译和清理工作...

# 现在,JAR 已经出炉。JDK 不再需要,所以不能留在镜像中。
# 所以我们开启第二阶段——运行(上桌),并扔掉第一阶段的所有文件(包括编译工具)
FROM openjdk:8u181-jre-alpine3.8 as environment # 只带运行时

# 目前,编译工具等上一阶段的东西已经被我们抛下。目前的镜像中只有运行时,我们需要把上一阶段(炒)的结果拿来,其它不要。
COPY --from=0 /final.jar .

# 好了,现在镜像只有必要的运行时和 JAR 了。
ENTRYPOINT java -jar /final.jar

如上就是多阶段构建的介绍。

使用多阶段构建

多阶段构建的核心命令是 FROM。FORM 对于身经百战的你来说已经不用多讲了。在多阶段构建中,每次 FROM 都会开启一个新的 Stage(阶段),可以看作一个新的 Image(不够准确、来源请求),与其它阶段隔离(甚至包括环境变量)。只有最后的 FROM 才会被纳入 Image 中。

我们来做一个最 simple 的多阶段构建例子:

# Stage 1
FROM alpine:3.8
WORKDIR /demo
RUN echo "Hello, stage 1" > /demo/hi-1.txt

# Stage 2
FROM alpine:3.8
WORKDIR /demo
RUN echo "Hello, stage 2" > /demo/hi-2.txt

可以自己构建一下这个 Dockerfile,然后 docker save > docker.tar 看看其中的内容。不出意外应该只有 /demo/hi-2.txt 和 Alpine。

在这个 Dockerfile 中,我们创建了两个阶段。第一个阶段创建 hi-1.txt,第二个阶段创建 hi-2.txt,且第二个阶段会被加入最终 Image,其它不会。

复制文件——阶段间的桥梁

如果阶段间完全隔离,那么多阶段就没有意义——上一个阶段的结果会被完全抛弃,并进入全新的下一阶段。

我们可以通过 COPY 命令来获取其它阶段的文件。在多阶段中使用 COPY 和普通应用完全一致,仅需要添加 –form ` 即可。那么,我们修正上一个例子,使最终镜像包含两个阶段的产物:

# Stage 1
FROM alpine:3.8
WORKDIR /demo
RUN echo "Hello, stage 1" > /demo/hi-1.txt

# Stage 2
FROM alpine:3.8
WORKDIR /demo
COPY --from=0 /demo/hi-1.txt /demo
RUN echo "Hello, stage 2" > /demo/hi-2.txt

重新构建并保存(Save),你会发现多了一层 Layer,其中包含 hi-1.txt。

阶段命名——快速识别

对于只有七秒记忆的我们来说,每次使用 stage index 并不是一件很妙的事情。这时候,可以通过阶段命名的方式给它们赋予名字,以方便识别。

为阶段添加名字很简单,只需要在 FROM 后加上 as 即可。

现在,我们更新 Dockerfile,给予阶段名称并使用名称来 COPY。

# Stage 1, it's name is "build1"
FROM alpine:3.8 as build1
WORKDIR /demo
RUN echo "Hello, stage 1" > /demo/hi-1.txt

# Stage 2, it's name is "build2"
FROM alpine:3.8 as build2
WORKDIR /demo
# No longer use indexes
COPY --from=build1 /demo/hi-1.txt /demo
RUN echo "Hello, stage 2" > /demo/hi-2.txt

重新构建并保存,结果应该同上次相同。

仅构建部分阶段——轻松调试

Docker 还为我们提供了一个很方便的调试方式——仅构建部分阶段。它可以使构建停在某个阶段,并不构建后面的阶段。这可以方便我们调试;区分生产、开发和测试。

仍然沿用上次的 Dockerfile,但使用 --target 参数进行构建:

$ docker build --target build1 .

再次 Save,你会发现只有 build1 的内容。

以上是“如何使用Docker多阶段构建来减小镜像大小”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注创新互联行业资讯频道!


新闻标题:如何使用Docker多阶段构建来减小镜像大小
URL链接:http://hbruida.cn/article/pjjdde.html