购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

3.6 多阶段构建

上一节我们通过更改基础镜像的方式优化了镜像体积,选择一些精简的镜像作为基础镜像比其他优化方式更立竿见影,但是基础镜像的选择只是镜像优化的开始。接下来我们使用多阶段构建进一步优化镜像体积。

通俗来讲,多阶段构建就是在Dockerfile中定义多个FROM,每个FROM下有多个不同的指令,一般可简单分为构建步骤和生成业务应用镜像步骤,也就是说前面一个或多个阶段用于构建,产生业务应用的包或其他产物,之后的阶段将上述阶段产生的包或其他产物再次构建成镜像。这样一来,最后一步就没有了构建时产生的缓存文件,也起到了优化镜像体积的作用。

假如有一个用Go语言开发的应用,此处用Hello World代替:

首先使用单个步骤构建,看一下制作出来的镜像大小,此时的Dockerfile如下:

     # build step
     FROM golang:1.14.4-alpine
     WORKDIR /opt
     COPY hw.go /opt
     RUN go build /opt/hw.go
     CMD "./hw"

执行构建并运行测试:

     # docker build -t hw:one .
     Successfully built e036098fbb2e
     Successfully tagged hw:one
     # docker run --rm hw:one
     Hello World!

查看此时由一个阶段构建的镜像大小:

可以看到此时镜像大小为372MB,但是上述代码我们只需要构建步骤产生的二进制文件hw即可,这个文件大小可以进入容器内部看一下:

真正需要的二进制文件只有2MB,如果使用上述方法执行构建并生成业务镜像,那么生成的镜像将会多用370MB空间,所以我们需要使用多阶段构建,将构建步骤和生成业务镜像的步骤拆分,之后将二进制包放置在某个可以运行该二进制包的基础镜像中即可。假如我们使用上一节提到的Scratch镜像制作业务应用镜像,此时的多阶段Dockerfile如下:

     # cat Dockerfile
     # 构建过程
     FROM golang:1.14.4-alpine as builder
     WORKDIR /opt
     COPY hw.go /opt
     RUN go build /opt/hw.go
     # CMD "./hw"
     
     # 生成应用镜像过程
     FROM scratch
     COPY --from=builder /opt/hw .
注意

ROM xxx as xxx用于给某个阶段起一个别名,在其他阶段使用--from=别名进行引用,如果不用别名,可以从上往下用0/1/2/x代替。

Scratch镜像为空镜像,里面没有任何东西,不能被拉取和推送,一般可以用在静态语言的应用中,比如这次举例的Hello World没有用到任何动态库,就可以使用这个镜像,但是在生产环境中,一般很少用该镜像直接作为生成业务镜像的基础镜像,因为该镜像没有任何可供使用的工具包,出了问题很难排查。

再次构建后,查看此时的镜像大小和运行镜像的效果:

使用多阶段构建不仅实现了相同的效果,而且还节省了300多兆字节(MB)的磁盘空间,所以在生产环境中构建自己的业务镜像时,可以根据不同的语言制定不同的构建过程,拆分代码编译过程和生成业务应用镜像过程。我们在本书第18章持续集成持续部署部分也会根据这个原理将代码编译和docker build步骤拆开,每个步骤各司其职,不仅可以优化我们的流水线,也可以降低因为构建步骤设计不合理造成的磁盘空间的浪费。 MbY1RJ59BvqOvRrNKW5pPbVFFh+4ANPOYX0Fs+3speExAUGBo4fkuiN1w1hbAxZ3

点击中间区域
呼出菜单
上一章
目录
下一章
×

打开