[Docker] 集装箱真是个废物重的Hello, World!镜像,简直能戏剧性轻巧起来
从Docker v17开始,引入了一个名为MultiStageBuild的功能。
以前的情况是,虽然只是输出”Hello,World”的镜像,但基础镜像的大小很大,最终生成的镜像也会达到几百MB大小……而现在可以将构建使用Go的镜像,执行使用alpine镜像,以实现这样的设置(尽管在v17之前也有可能做到,但比较麻烦)。
接下来将详细解释这到底是什么意思。
这次我们要创建一个输出”Hello, World!”的容器。
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
如果不使用MultiStageBuild
Dockerfile 如下所示。
FROM golang:1.13.7-alpine3.11
COPY ./main.go ./
RUN go build -o ./hello ./main.go
ENTRYPOINT ["./hello"]
让我们在同一个目录中尝试输入以下命令。
$ docker build -t hello:0.1 .
如果能构建成功,我会尝试运行。
$ docker run hello:0.1
Hello World!
我成功地建立了main.go并运行了它。现在我们来看看此时的图像大小。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello 0.1 ea6e8238c063 49 seconds ago 361MB
golang 1.13.7-alpine3.11 87eefb76f0a8 9 days ago 359MB
这个原因是因为golang的容器镜像作为基础,导致容器的大小变得很大。
尽管这只是一个输出”Hello, World!”的容器,但是它太重了。。。
如果使用MultiStageBuild
在下面是Dockerfile的内容。
#Stage 1
FROM golang:1.13.7-alpine3.11 as builder
COPY ./main.go ./
RUN go build -o /hello ./main.go
#Stage 2
FROM alpine:3.11
COPY --from=builder /hello .
ENTRYPOINT ["./hello"]
在Stage1阶段,我们使用golang的Image,对main.go进行构建。然后将构建好的可执行文件传输到Stage2阶段进行执行。
$ docker run hello:0.1
Hello World!
这也成功执行了。同样,我们来看一下图片的尺寸。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello 0.1 15f814bf3a87 12 seconds ago 7.6MB
<none> <none> 6f908ead16f8 13 seconds ago 361MB
golang 1.13.7-alpine3.11 87eefb76f0a8 9 days ago 359MB
alpine 3.11 e7d92cdc71fe 2 weeks ago 5.59MB
在这里,是Stage1的映像,则成为Stage2的映像。输出”Hello, World”的映像是,因此该映像的大小为7.6MB!我们成功实现了将映像大小减小了约350MB,与不使用MultiStageBuild相比!!
总结
所以,這次我們試著談談Docker的MultiStageBuild功能。我們提到,通過將體積較大的Build環境分開,可以減少映像的大小,除此之外,通過使用Stage進行區分,還能提高Dockerfile的維護性,增強安全性等,有很多好處!如果您還沒有嘗試過,請不妨一試!