docker多阶段构建
docker多阶段构建
ivansli多阶段的构建对于那些努力优化 Dockerfiles 的开发者来说非常有用,同时也使 Dockerfiles 更易于阅读和维护。
1.多阶段 build 之前的 build
build镜像 最具挑战性的事情之一是使镜像变得更小。Dockerfile 中的每条指令都会为镜像添加一层,需要记住的是要清理任何不需要的内容,然后再进入下一层。为了编写一个非常有效的 Dockerfile,传统上需要采用shell技巧和其他逻辑来保持每一层尽可能小,并确保每一层都具有上一层中所需的内容,而没有其他多余内容。
实际上,一个用于生产环境的 Dockerfile(其中包含构建应用程序所需的一切),其中仅包含您的应用程序以及运行它所需的内容,并尽可能的小。这被称为 “builder pattern”。而维持两个 Dockerfiles 也不是理想的选择。
下面举一个使用 Dockerfile.build 与 Dockerfile 进行构建的例子。
Dockerfile.build:
1 | syntax=docker/dockerfile:1 |
注意,此示例使用了shell的 &&运算符 人为地将多个运行命令一起压缩,以避免在图像中创建附加层,这是容易失败且难以维护。例如,很容易插入另一个命令,而忘记使用换行 \
字符。
Dockerfile:
1 | syntax=docker/dockerfile:1 |
build.sh:
1 | !/bin/sh |
运行 build.sh脚本时,需要经过几个主要步骤:
① 构建第一个镜像
② 基于第一个镜像生成一个容器,然后把容器中所需要的文件拷贝到本地
③ 构建第二个镜像,并把第一个镜像容器中拷贝出来的文件拷贝进去
④ 删除从容器中拷贝到本地的中间文件
最终发现,两个镜像不仅占用系统空间,还需要把容器中所需要的文件拷贝到本地磁盘,比较繁琐并占用空间。
2.多阶段 builds
使用多阶段构建,可以在Dockerfile中使用多个 FROM 语句。每个 FROM 指令都可以使用不同的基础镜像,并开始了构建的新阶段。同时,可以选择性地将文件从一个build阶段复制到另一个build阶段,最终镜像中只保留想要的文件内容。
为了展示其工作原理,我们调整上面的 Dockerfile,以进行多阶段构建。
Dockerfile:
1 | syntax=docker/dockerfile:1 |
最终的构建只要一个 Dockerfile 文件,并且不再需要 build.sh,仅仅是执行一个 docker build
命令而已。
1 | docker build -t alexellis2/href-counter:latest . |
最终结果是得到了比以前更小的镜像,其复杂性也显着降低。同时无需创建任何中间镜像,也无需在本地存储任何临时文件。
它是如何工作的呢?
从第二个 FROM 指令开始了一个新的构建阶段,该阶段使用镜像 alpine:latest 作为其基础。使用 COPY --from=0
从第一阶段构建拷贝所需要的文件。最终 GO SDK 和任何中间文物都被丢弃,并且没有保存在最终镜像中。
2.1 为构建阶段命名
默认情况下阶段未命名,以其整数编号为单位,第一个 FROM 指令从 0 开始。但是,您可以通过在 FROM 后面添加 AS
下面将演示如何通过 构建阶段命名
来改善上面一个例子。这意味着,即使 Dockerfile 中构建阶段稍后会进行重排序,COPY 指令也不会因为构建阶段重排序而导致拷贝错误文件。
1 | syntax=docker/dockerfile:1 |
2.2 终止特定的构建阶段
构建镜像时,不一定需要构建包括每个阶段在内的整个 Dockerfile。您可以指定目标构建阶段。
以下命令使用上面的 Dockerfile,在构建过程时最终停止在名为 Builder 的阶段。
1 | docker build --target builder -t alexellis2/href-counter:latest . |
一些可能非常有用的情况:
- 调试特定的构建阶段
- 使用启用所有调试符号或工具的调试阶段,以及精益生产阶段
- 使用测试阶段,其中应用程序填充了测试数据,但使用其他阶段构建生产的阶段,该阶段使用真实数据
2.3 使用外部镜像作为一个构建
当使用多阶段构建时,不仅限于从同一个 Dockerfile 中赋值前面构建阶段产生的文件。也可以使用 COPY --from
从单独的镜像中复制,也可以是本地镜像名称、本地tag名称 或者 Docker仓库 或者 一个 tag ID。Docker 客户端将会在必要时拉取镜像,并从那里复制文件内容。语法如下:
1 | COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf |
2.4 用前一个build阶段用作为新的build阶段
可以在使用 FROM 时引用前面 build 阶段,例如:
1 | syntax=docker/dockerfile:1 |
3.版本兼容
多阶段构建是在 Docker 17.05开始引入的。
本文翻译自 docker 官方文档