Dockerfile多阶段构建(multi-stage builds)

       在应用了容器技术的软件开发过程中,控制容器镜像的大小可是一件费时费力的事情。如果我们构建的镜像既是编译软件的环境,又是软件最终的运行环境,这是很难控制镜像大小的。所以常见的配置模式为:分别为软件的编译环境和运行环境提供不同的容器镜像。比如为编译环境提供一个 Dockerfile.build,用它构建的镜像包含了编译软件需要的所有内容,比如代码、SDK、工具等等。同时为软件的运行环境提供另外一个单独的 Dockerfile,它从 Dockerfile.build 中获得编译好的软件,用它构建的镜像只包含运行软件所必须的内容。这种情况被称为构造者模式(builder pattern)

多阶段构建对优化Dockerfile很有用,同时还保持了Dockerfile的可读性和可维护性。

多阶构建之前

保持镜像尽可能的小是构建镜像最重要的一件事情,Dockerfile中每个指令都会在镜像中创建一个层。我们每创建完一层都要把不再需要的东西清除掉,想要写出高效的Dockerfile,您除了需要掌握shell技巧,还需要从逻辑上保待层尽可能的小,并且只保留下一个层必需的目标。

有一种常见的做法,一个Dockerfile用于开发(包含构建您的应用的所有东西),一个简化的用于生产,只包含你的应用和运行它需要的东西。这通常被称为构建模式。维护两个Dockerfile并不是个好主意。

下面的例子使用了上面讲的构建模式,两个Dockerfile.build和Dockerfile

Dockerfile.build:

FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
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命令用Bush&&操作压缩,避免添加更多的层到镜像中。然而这样容易发生故障且难以维护。例如,很容易插入另一个命令而忘记使用\字符继续行。

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 container create --name extract alexellis2/href-counter:build  
docker container cp extract:/go/src/github.com/alexellis/href-counter/app ./app  
docker container 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构建。

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

最终的结果是与以前一样小的生产镜像,并且显著降低了复杂性。您不需要创建任何中间镜像,也不需要将任何中间物提取到本地系统中。

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

命名你的构建阶段

默认情况下,这些阶段没有命名,您可以通过它们的整数索引引用它们,第一个FROM指令为0。但是,您可以通过在FROM指令中添加AS <NAME>来为阶段命名。这个例子通过命名阶段和在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"]  

在特定的构建阶段停止

构建镜像时,不需要构建整个Dockerfile中包括的每个阶段。 您可以指定目标构建阶段。 以下命令假定您正在使用以前的Dockerfile,但在名为builder的阶段停止:

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

一些场景中这可能非常强大:

  • 调试特定的构建阶段。
  • 使用debug阶段——启用了所有调试符号或工具,和干净的production阶段。
  • 使用testing阶段,在该阶段中,您的应用填充了测试数据,但生产构建使用另一个使用真实数据的阶段。

将外部镜像用作“阶段”

使用多阶段构建时,您不仅限于从Dockerfile中之前创建的阶段进行复制。 您可以使用COPY --from指令从单独的镜像中复制,可以是本地镜像名称,本地或Docker注册中心上可用的标签,或一个标签ID。 Docker客户端在必要时拉取镜像并从中复制构件。 语法为:

COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf

将上一个阶段用作新阶段

在使用FROM指令时,可以通过引用前一个阶段结束的地方来进行选择。 例如:

FROM alpine:latest as builder
RUN apk --no-cache add build-base

FROM builder as build1
COPY source1.cpp source.cpp
RUN g++ -o /binary source.cpp

FROM builder as build2
COPY source2.cpp source.cpp
RUN g++ -o /binary source.cpp

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

网络飞鸥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值