专业编程基础技术教程

网站首页 > 基础教程 正文

怎么进行docker多阶段构建(docker使得开发人员构建一次即可实现多平台运行)

ccvgpt 2024-07-19 12:43:34 基础教程 5 ℃

之前的做法

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

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

怎么进行docker多阶段构建(docker使得开发人员构建一次即可实现多平台运行)

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

全部放入一个Dockerfile

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

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

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

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

编写 Dockerfile.one 文件

FROM	golang:1.9-alpine
RUN	apk	--no-cache	add	git	ca-certificates
WORKDIR	/go/src/github.com/go/helloworld/
COPY	app.go	.
RUN	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"]

构建镜像

$	docker	build	-t	go/helloworld:1	-f	Dockerfile.one	.

分散到多个Dockerfile

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

例如

编写 Dockerfile.build 文件

FROM	golang:1.9-alpine
RUN	apk	--no-cache	add	git
WORKDIR	/go/src/github.com/go/helloworld
COPY	app.go	.
RUN	go	get	-d	-v	github.com/go-sql-driver/mysql	\		&&	CGO_ENABLED=0	GOOS=linux	go	build	-a	-installsuffix	cgo	-o	app	.

编写Dockerfile.copy 文件

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

新建build.sh

#!/bin/sh 
echo	Building	go/helloworld:build
docker	build	-t	go/helloworld:build	.	-f	Dockerfile.build
docker	create	--name	extract	go/helloworld:build docker	cp	extract:/go/src/github.com/go/helloworld/app	./app docker	rm	-f	extract
echo	Building	go/helloworld:2
docker	build	--no-cache	-t	go/helloworld:2	.	-f	Dockerfile.copy rm	./app

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

$	chmod	+x	build.sh
$	./build.sh

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

$	docker	image	ls
REPOSITORY			TAG			IMAGE	ID				CREATED			 	SIZE go/helloworld			2			f7cf3465432c		22 seconds ago	 	6.47MB go/helloworld			1			f55d3e16affc		2	minutes	ago			295MB

使用多阶段构建

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

例如

编写 Dockerfile 文件

FROM	golang:1.9-alpine	as	builder
RUN	apk	--no-cache	add	git
WORKDIR	/go/src/github.com/go/helloworld/
RUN	go	get	-d	-v	github.com/go-sql-driver/mysql
COPY	app.go	.
RUN	CGO_ENABLED=0	GOOS=linux	go	build	-a	-installsuffix	cgo	-o	a pp	.
FROM	alpine:latest	as	prod
RUN	apk	--no-cache	add	ca-certificates
WORKDIR	/root/
COPY	--from=0	/go/src/github.com/go/helloworld/app	.
CMD	["./app"]

构建镜像

$	docker	build	-t	go/helloworld:3	.

对比三个镜像大小

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

只构建某一阶段的镜像

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

FROM	golang:1.9-alpine	as	builder

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

$	docker	build	--target	builder	-t	username/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内容,感兴趣的朋友可以关注下~

Tags:

最近发表
标签列表