Docker 17
引擎 | Engine

使用多阶段构建(引擎) | Use multi-stage builds (Engine)

使用多阶段构建

多阶段构建是Docker 17.05中的一项新功能,对于那些努力优化Docker文件的人来说,他们会很激动,同时让他们易于阅读和维护。

致谢:特别感谢Alex Ellis授予他使用他的博客文章构建器模式与Docker中的多阶段构建的权限,作为以下示例的基础。

在多阶段构建之前

关于构建图像最具挑战性的事情之一是保持图像的大小。Dockerfile中的每条指令都会为图像添加一个图层,并且您需要记住在移动到下一图层之前清理不需要的任何工件。为了编写一个非常高效的Dockerfile,传统上需要使用shell技巧和其他逻辑来尽可能地减小图层,并确保每个图层都具有它从上一图层需要的构件,而不是其他任何东西。

实际上,有一个Dockerfile用于开发(其中包含构建应用程序所需的所有内容)以及一个用于生产的瘦客户端,它只包含您的应用程序以及运行它所需的内容。这被称为“建造者模式”。维护两个Dockerfiles并不理想。

这里有一个例子Dockerfile.buildDockerfile它遵守上面建造者模式:

Dockerfile.build*

FROM golang:1.7.3 WORKDIR /go/src/github.com/alexellis/href-counter/ RUN go get -d -v golang.org/x/net/html COPY app.go . RUN go get -d -v golang.org/x/net/html \ && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

请注意,此示例还RUN使用Bash &&运算符人为地压缩两个命令,以避免在图像中创建额外的图层。这很容易失败并且很难维护。例如,插入另一个命令并忘记继续使用该\字符行很容易。

Dockerfile*

FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY app . CMD ["./app"]

build.sh*

#!/bin/sh echo Building alexellis2/href-counter:build docker build --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy \ -t alexellis2/href-counter:build . -f Dockerfile.build docker create --name extract alexellis2/href-counter:build docker cp extract:/go/src/github.com/alexellis/href-counter/app ./app docker rm -f extract echo Building alexellis2/href-counter:latest docker build --no-cache -t alexellis2/href-counter:latest . rm ./app

当你运行这个build.sh脚本时,它需要构建第一个图像,从中创建一个容器以便将该构件复制出来,然后构建第二个图像。这两个图像都占用了系统空间,并且您app的本地磁盘上仍然存在工件。

多阶段构建大大简化了这种情况!

使用多阶段构建

使用多阶段构建,您可以在Dockerfile中使用多个FROM语句。每条FROM指令都可以使用不同的基础,并且每条指令都开始构建的新阶段。您可以选择性地将工件从一个阶段复制到另一个阶段,在最终图像中留下不需要的所有内容。为了演示这是如何工作的,让我们修改上一节中的Dockerfile以使用多阶段构建。

Dockerfile*

FROM golang:1.7.3 WORKDIR /go/src/github.com/alexellis/href-counter/ RUN go get -d -v golang.org/x/net/html COPY app.go . RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=0 /go/src/github.com/alexellis/href-counter/app . CMD ["./app"]

你只需要单个Dockerfile。您也不需要单独的构建脚本。就跑吧docker build

$ docker build -t alexellis2/href-counter:latest .

最终的结果是与以前相同的小型生产映像,并显着降低了复杂性。您不需要创建任何中间图像,也不需要将任何工件提取到本地系统。

它是如何工作的?第二FROM条指令以alpine:latest图像为基础开始新的构建阶段。该COPY --from=0行只将前一阶段构建的工件复制到这个新阶段。Go SDK和任何中间工件都被留下,并未保存在最终图像中。

为您的构建阶段命名

默认情况下,这些阶段未被命名,并且您可以用它们的整数来引用它们,从第一FROM条指令的第一个0开始。但是,您可以通过as <NAME>在FROM指令中添加一个名称来命名您的阶段。此示例通过命名阶段并在COPY指令中使用名称来改进前一个示例。这意味着即使Dockerfile中的指令稍后重新排序,COPY也不会中断。

FROM golang:1.7.3 as builder WORKDIR /go/src/github.com/alexellis/href-counter/ RUN go get -d -v golang.org/x/net/html COPY app.go . RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /go/src/github.com/alexellis/href-counter/app . CMD ["./app"]

下一步

图像容器最佳实践