©
This document uses PHP Chinese website manual Release
多阶段构建是Docker 17.05中的一项新功能,对于那些努力优化Docker文件的人来说,他们会很激动,同时让他们易于阅读和维护。
致谢:特别感谢Alex Ellis授予他使用他的博客文章构建器模式与Docker中的多阶段构建的权限,作为以下示例的基础。
关于构建图像最具挑战性的事情之一是保持图像的大小。Dockerfile中的每条指令都会为图像添加一个图层,并且您需要记住在移动到下一图层之前清理不需要的任何工件。为了编写一个非常高效的Dockerfile,传统上需要使用shell技巧和其他逻辑来尽可能地减小图层,并确保每个图层都具有它从上一图层需要的构件,而不是其他任何东西。
实际上,有一个Dockerfile用于开发(其中包含构建应用程序所需的所有内容)以及一个用于生产的瘦客户端,它只包含您的应用程序以及运行它所需的内容。这被称为“建造者模式”。维护两个Dockerfiles并不理想。
这里有一个例子Dockerfile.build
和Dockerfile
它遵守上面建造者模式:
Dockerfile.build
*
FROM golang:1.7.3WORKDIR /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.3WORKDIR /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"]
查看博客文章Builder模式与Docker中的多阶段构建,了解完整的源代码以及这些示例的演练。