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


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

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

    多阶构建之前

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

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

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

    Dockerfile.build:

    1. FROM golang:1.7.3
    2. WORKDIR /go/src/github.com/alexellis/href-counter/
    3. COPY app.go .
    4. 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:

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

    build.sh:

    1. #!/bin/sh
    2. echo Building alexellis2/href-counter:build
    3. docker build --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy \
    4. -t alexellis2/href-counter:build . -f Dockerfile.build
    5. docker container create --name extract alexellis2/href-counter:build
    6. docker container cp extract:/go/src/github.com/alexellis/href-counter/app ./app
    7. docker container rm -f extract
    8. echo Building alexellis2/href-counter:latest
    9. docker build --no-cache -t alexellis2/href-counter:latest .
    10. rm ./app

    当您运行build.sh脚本时,它需要构建第一个镜像,并从中创建一个容器来复制中间物,然后构建第二个镜像。这两个镜像都占用了系统上的空间,您的本地磁盘上仍然有app中间物。

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

    使用多阶段构建

    对于多阶段构建,您可以在Dockerfile中使用多个FROM语句。每个FROM指令都可以使用不同的基镜像,并且它们都开始了构建的新阶段。您可以有选择地将中间物从一个阶段复制到另一个阶段,舍弃在最后的镜像中您不想要的所有内容。为了演示这是如何工作的,让我们修改前一节中的Dockerfile,以使用多阶段构建。

    Dockerfile:

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

    您只需要一个Dockerfile。也不需要单独的构建脚本。只需运行docker构建。

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

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

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

    命名你的构建阶段

    默认情况下,这些阶段没有命名,您可以通过它们的整数索引引用它们,第一个FROM指令为0。但是,您可以通过在FROM指令中添加AS 来为阶段命名。这个例子通过命名阶段和在COPY指令中使用名称来改进前面的例子。这意味着即使Dockerfile中的指令稍后被调整顺序,COPY也不会出错。

    1. FROM golang:1.7.3 AS builder
    2. WORKDIR /go/src/github.com/alexellis/href-counter/
    3. RUN go get -d -v golang.org/x/net/html
    4. COPY app.go .
    5. RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
    6. FROM alpine:latest
    7. RUN apk --no-cache add ca-certificates
    8. WORKDIR /root/
    9. COPY --from=builder /go/src/github.com/alexellis/href-counter/app .
    10. 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指令时,可以通过引用前一个阶段结束的地方来进行选择。 例如:

    1. FROM alpine:latest as builder
    2. RUN apk --no-cache add build-base
    3. FROM builder as build1
    4. COPY source1.cpp source.cpp
    5. RUN g++ -o /binary source.cpp
    6. FROM builder as build2
    7. COPY source2.cpp source.cpp
    8. RUN g++ -o /binary source.cpp

  • 相关阅读:
    1.【刷爆LeetCode】替换空格(多方法、多思路解决)
    MAC系统下Xcode连接iOS真机实现iOS App自动化测试(上)
    32. 最长有效括号 java解决
    一看就会的Chromedriver(谷歌浏览器驱动)安装教程
    既然有 HTTP 协议,为什么还要有 RPC
    C++虚基类、虚函数、虚析构函数、纯虚函数
    C++中struct与class区别,C与C++中struct区别
    R语言使用mean函数计算样本(观测)数据中指定变量的相对频数:计算时间序列数据中大于前一个观测值的观测值所占的比例总体的比例
    Nginx一网打尽:动静分离、压缩、缓存、黑白名单、跨域、高可用、性能优化
    精准防疫有“利器”| 芯讯通助力数字哨兵护航复市
  • 原文地址:https://blog.csdn.net/erhaiou2008/article/details/127569723