Docker多阶段构建镜像multi-stage详解

 更新时间:2025年10月24日 08:48:33   作者:尤达c  
多阶段构建是Docker 17.05及以上版本的新特性,允许在一个Dockerfile中使用多个阶段,每个阶段可以有一个独立的基础镜像,并且可以通过COPY --from命令在阶段之间传递文件,这样可以有效地减少最终镜像的大小,只保留必要的文件

多阶段构建是一个新特性,需要 Docker 17.05 或更高版本的守护进程和客户端。对于那些努力优化 Dockerfiles 并使其易于阅读和维护的人来说,多阶段构建非常有用。

在之前先来学习术语:

  • multi-stage 多阶段
  • build 构建
  • image 镜像
  • stage 阶段

一、使用在多阶段构建之前

构建镜像时最具挑战性的事情之一就是缩小镜像大小。

Dockerfile 中的每一条指令都会在镜像中添加一个层,在进入下一层之前,您需要记住清除所有不需要的工件。

要编写一个真正高效的 Dockerfile,传统上需要使用 shell 技巧和其他逻辑来保持层尽可能小,并确保每一层都有它需要的来自前一层的工件,而没有其他东西。

实际上,有一个 Dockerfile 用于开发环境(包含构建应用程序所需的所有内容,包括编译工具等),同时有一个精简的 Dockerfile 用于生产环境(仅包含应用程序和运行应用程序所需的内容)是非常常见的。这被称为“建造者模式”。维护两个 Dockerfiles 并不理想。

这里一个例子,对比一下 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
  1. 很明显,优化后的 Dockerfile 新增了 FROM AS 这个命令,并出现了两个 FROM。这就是多阶段构建。
  2. 请注意,此示例还使用 Bash 操作符 && 将两个 RUN命令人为压缩在一起,以避免在镜像中创建额外的层。这很容易发生故障,也很难维护。例如,很容易插入另一个命令而忘记使用 \ 字符继续行。

二、了解下多阶段构建

多阶段构建是 Docker 17.05 的新增功能,它可以在一个 Dockerfile 中使用多个 FROM 语句,以创建多个 Stages(阶段)。每个阶段间独立(来源请求),可以通过 COPY --from 来获取其它阶段的文件

看看在 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

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

三、使用多阶段构建

1. 只有最后一个stage才会被纳入image中

多阶段构建的核心命令是 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,其它不会。

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

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

我们可以通过 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。
  • 第二个 FROM 指令用 alpine:3.8 镜像作为基础,开始一个新的构建阶段。
  • COPY --from=0 行只将前一阶段的构建工件复制到这个新阶段;
  • FROM 指令的第一个整数从 0 开始,0代表第一阶段

3. 阶段命名——快速识别

每次使用 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

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

4. 仅构建其中的部分阶段——轻松调试

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

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

$ docker build --target build1 .

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

总结

这就是一些多阶段构建的用法

很显然,利用多阶段无需包含无用的 JDK,JDK 只在编译时起作用,编译完便无用了,只需要 JRE 即可。所以,利用多阶段构建可以隔离编译阶段和运行阶段,以达到镜像最优化。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 解决docker的tls(ssl)证书过期问题

    解决docker的tls(ssl)证书过期问题

    这篇文章主要介绍了解决docker的tls(ssl)证书过期问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-03-03
  • Windows docker的安装和初使用详细教程

    Windows docker的安装和初使用详细教程

    Docker是开源容器引擎,用于打包应用及依赖,安装需Windows10/11专业版/企业版,启用WSL2和Hyper-V,配置镜像和端口映射,支持本地文件挂载,卸载时关闭DockerDesktop并重启系统,本文给大家介绍Windows docker的安装和初使用,感兴趣的朋友跟随小编一起看看吧
    2025-09-09
  • 一文搞定Docker安装ElasticSearch的过程

    一文搞定Docker安装ElasticSearch的过程

    通过本文可以帮助大家快速学习Docker安装ElasticSearch的过程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2021-08-08
  • docker自定义网桥docker0及docker的开启,关闭,重启命令操作

    docker自定义网桥docker0及docker的开启,关闭,重启命令操作

    这篇文章主要介绍了docker自定义网桥docker0及docker的开启,关闭,重启命令操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-03-03
  • 编写最佳的Dockerfile的方法

    编写最佳的Dockerfile的方法

    本文给大家分享的是如何编写最佳的dockerfile的方法,通过具体实例帮助大家快速掌握编写Dockerfile的技巧
    2017-06-06
  • Docker构建镜像的三大优化方法指南

    Docker构建镜像的三大优化方法指南

    这篇文章主要为大家详细介绍了Docker构建镜像的三大优化方法,包括CMD/ENTRYPOINT、多阶段构建与缓存优化,下面就跟随小编一起深入了解下吧
    2025-11-11
  • Docker 中 Crontab 不执行的原因全解析与解决方案

    Docker 中 Crontab 不执行的原因全解析与解决方案

    文章总结了在Docker容器里使用crontab时遇到的问题及解决方法,常见问题包括缺少cron服务、服务未启动、容器销毁后任务丢失等,解决方法包括使用Supervisor管理cron、将任务放在宿主机的crontab、通过挂载方式加载宿主机的cron文件,感兴趣的朋友跟随小编一起看看吧
    2025-12-12
  • 使用Docker构建企业级自定义镜像的方法

    使用Docker构建企业级自定义镜像的方法

    这篇文章主要介绍了使用Docker构建企业级自定义镜像的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • Docker连接宿主Redis的方法步骤

    Docker连接宿主Redis的方法步骤

    本文主要介绍了Docker连接宿主Redis的方法步骤,可以轻松地使用Docker容器与宿主机上的Redis进行交互,实现高效的数据存储和共享,,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • Docker实现镜像优化的实用技巧分享

    Docker实现镜像优化的实用技巧分享

    镜像太大、构建太慢、上传半天,这时是时候掌握一些真正实用的 Docker 镜像优化技巧了,下面小编就带大家从实践角度出发,逐步优化镜像体积,构建速度与运行性能吧
    2026-04-04

最新评论