七叶笔记 » golang编程 » 怎么进行docker多阶段构建

怎么进行docker多阶段构建

之前的做法

关于构建镜像最具挑战性的事情之一是保持镜像体积小巧。 docker file 中的每条指令都会在镜像中增加一层,并且在移动到下一层之前,需要记住清除不需要的构件。要编写一个非常高效的 Dockerfile,你通常需要使用 shell 技巧和其它方式来尽可能地减少层数,并确保每一层都具有上一层所需的构件,而其它任何东西都不需要。

实际上最常见的是,有一个 Dockerfile 用于开发(其中包含构建应用程序所需的所有内容),而另一个裁剪过的用于生产环境,它只包含您的应用程序以及运行它所需的内容。这被称为“构建器模式”。但是维护两个 Dockerfile 并不理想。

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

全部放入一个Dockerfile

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

  1. Dockerfile特别长,可维护性降低
  2. 镜像层次多,镜像体积较大,部署时间变长
  3. 源代码存在泄露的风险

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

packagemain
import"fmt"
funcmain(){
fmt.Printf("HelloWorld!"); 
}
 

编写 Dockerfile.one 文件

FROMgolang:1.9-alpine
RUNapk--no-cacheadd git ca-certificates
WORKDIR/go/src/ github .com/go/ helloworld /
COPYapp.go.
RUNgoget-d-vgithub.com/go-sql-driver/mysql\&&CGO_ENABLED=0GOOS= linux gobuild-a-installsuffixcgo-oapp.\&&cp/go/src/github.com/go/helloworld/app/root
WORKDIR/root/
CMD["./app"]
 

构建镜像

$dockerbuild-tgo/helloworld:1-fDockerfile.one.
 

分散到多个Dockerfile

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

例如

编写 Dockerfile.build 文件

FROMgolang:1.9-alpine
RUNapk--no-cacheaddgit
WORKDIR/go/src/github.com/go/helloworld
COPYapp.go.
RUNgoget-d-vgithub.com/go-sql-driver/mysql\&&CGO_ENABLED=0GOOS=linuxgobuild-a-installsuffixcgo-oapp.
 

编写Dockerfile.copy 文件

FROMalpine:latest
RUNapk--no-cacheaddca-certificates
WORKDIR/root/
COPYapp.
CMD["./app"]
 

新建build.sh

#!/bin/sh 
echoBuildinggo/helloworld:build
dockerbuild-tgo/helloworld:build.-fDockerfile.build
dockercreate--name extract go/helloworld:build dockercpextract:/go/src/github.com/go/helloworld/app./app dockerrm-fextract
echoBuildinggo/helloworld:2
dockerbuild--no-cache-tgo/helloworld:2.-fDockerfile.copy rm./app
 

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

$chmod+xbuild.sh
$./build.sh
 

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

$dockerimagels
REPOSITORYTAGIMAGEIDCREATED SIZE go/helloworld2f7cf3465432c22 seconds ago 6.47MB go/helloworld1f55d3e16affc2minutesago295MB
 

使用多阶段构建

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

例如

编写 Dockerfile 文件

FROMgolang:1.9-alpineasbuilder
RUNapk--no-cacheaddgit
WORKDIR/go/src/github.com/go/helloworld/
RUNgoget-d-vgithub.com/go-sql-driver/mysql
COPYapp.go.
RUNCGO_ENABLED=0GOOS=linuxgobuild-a-installsuffixcgo-oa pp.
FROMalpine:latestasprod
RUNapk--no-cacheaddca-certificates
WORKDIR/root/
COPY--from=0/go/src/github.com/go/helloworld/app.
CMD["./app"]
 

构建镜像

$dockerbuild-tgo/helloworld:3.
 

对比三个镜像大小

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

只构建某一阶段的镜像

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

FROMgolang:1.9-alpineasbuilder
 

例如当我们只想构建 builder 阶段的镜像时,我们可以在使用 dockerbuild命令时加上 –target 参数即可

$dockerbuild--targetbuilder-tusername/imagename:tag.
 

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

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

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

篇幅有限,关于docker多阶段构建就介绍到这了,感兴趣的朋友可以在本地虚拟机尝试下哦~

后面会分享更多关于devops和DBA内容,感兴趣的朋友可以关注下~

相关文章