github编辑

7.17 多阶段构建

在 Docker 17.05 版本之前,我们构建 Docker 镜像时,通常会采用两种方式:

7.17.1 全部放入一个 Dockerfile

一种方式是将所有的构建过程包含在一个 Dockerfile 中,包括项目及其依赖库的编译、测试、打包等流程,这里可能会带来的一些问题:

  • 镜像层次多,镜像体积较大,部署时间变长

  • 源代码存在泄露的风险

例如,编写 app.go 文件,该程序输出 Hello World!

package main

import "fmt"

func main(){
    fmt.Printf("Hello World!");
}

编写 Dockerfile.one 文件

FROM golang:alpine

RUN apk --no-cache add git ca-certificates

WORKDIR /go/src/github.com/go/helloworld/

COPY app.go .

RUN go mod init helloworld \
  && go get -d -v github.com/go-sql-driver/mysql \
  && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . \
  && cp /go/src/github.com/go/helloworld/app /root

WORKDIR /root/

CMD ["./app"]

构建镜像

7.17.2 分散到多个 Dockerfile

另一种方式,就是我们事先在一个 Dockerfile 将项目及其依赖库编译测试打包好后,再将其拷贝到运行环境中,这种方式需要我们编写两个 Dockerfile 和一些编译脚本才能将其两个阶段自动整合起来,这种方式虽然可以很好地规避第一种方式存在的风险,但明显部署过程较复杂。

例如,编写 Dockerfile.build 文件

编写 Dockerfile.copy 文件

新建 build.sh

现在运行脚本即可构建镜像

对比两种方式生成的镜像大小

7.17.3 使用多阶段构建

为解决以上问题,Docker v17.05 开始支持多阶段构建 (multistage builds)。使用多阶段构建我们就可以很容易解决前面提到的问题,并且只需要编写一个 Dockerfile

例如,编写 Dockerfile 文件

构建镜像

对比三个镜像大小

很明显使用多阶段构建的镜像体积小,同时也完美解决了上边提到的问题。

7.17.4 只构建某一阶段的镜像

我们可以使用 as 来为某一阶段命名,例如

例如当我们只想构建 builder 阶段的镜像时,增加 --target=builder 参数即可

7.17.5 构建时从其他镜像复制文件

上面例子中我们使用 COPY --from=0 /go/src/github.com/go/helloworld/app . 从上一阶段的镜像中复制文件,我们也可以复制任意镜像中的文件。

最后更新于