尝试使用Docker 17.05的多阶段构建,将镜像大小缩小到原来的30分之一

引子

Kubernetes最佳实践:如何以及为何构建小型容器镜像,我实际尝试了一些减小Docker镜像大小的技巧。

我认为在 Kubernetes 中,小型的镜像大小对性能有所帮助,但在其他用例中,我也认为小型镜像的好处很多。

因为我会很快忘记,所以基本上是为了自己的备忘录而保存。

在这之前,多阶段构建是什么意思?

Docker文档中详细描述了相关内容。

从Docker 17.05版本开始引入的功能是能够在一个Dockerfile中使用多个基础镜像,并最终创建一个镜像。这样可以保持维护性的同时,进行Dockerfile的优化等操作。

简单地说,可以通过不使用 Builder-Pattern 来减小 Docker 镜像的尺寸。

本次为了方便起见,我们将创建一个名为 single-stage 的 Dockerfile,并从单一的基础映像构建。

这次的构成

Docker 17.09.0-ce(Mac版Docker)

.
├── multi-stage
│   ├── Dockerfile
│   └── main.go
└── single-stage
    ├── Dockerfile
    └── main.go

我将本次构建的配置放在了这个链接中:https://github.com/ight-reco/go-docker-multistage。

试试看实际操作一下

单阶段

我会试着从单阶段的Docker镜像开始制作。

# golang app build & 実行用
FROM golang:alpine
WORKDIR /app
ADD . /app
RUN cd /app && go build -o goapp

EXPOSE 8080
ENTRYPOINT ./goapp
package main

import (
        "fmt"
        "net/http"
)

func main() {
        http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) {
                fmt.Fprintln(w, "Hello, World")
        });

        fmt.Println("Running http://localhost:8080");
        http.ListenAndServe(":8080", nil);
}
$ docker build -t single-stage ./single-stage
...
Successfully tagged single-stage:latest

$ docker images --format "table {{.Repository}}\t{{.Size}}"
REPOSITORY          SIZE
single-stage        382MB
...

我成功构建了。即使使用了golang:alpine,大小也是大约382MB。 我会预先执行一下,以确保。

$ docker run -it -p 8080:8080 builder-pattern
Running http://localhost:8080
$ curl localhost:8080
Hello, World

好像一切顺利进行!

多阶段的 (duō jiē de)

我们将创建一个多阶段的镜像,同样地需要出现两次 FROM,由于 main.go 和 single 相同,所以我们将省略它。

# golang app build 用
FROM golang:alpine AS build-env
WORKDIR /app
ADD . /app
RUN cd /app && go build -o goapp

# golang app 実行用 (ここのベース image は最小限に)
FROM alpine
WORKDIR /app

# build した goapp を実行用に COPY
COPY --from=build-env /app/goapp /app
EXPOSE 8080
ENTRYPOINT ./goapp
$ docker build -t multi-stage ./multi-stage
...
Successfully tagged multi-stage:latest

$ docker images --format "table {{.Repository}}\t{{.Size}}"
REPOSITORY          SIZE
multi-stage         10.8MB
single-stage        382MB
...

10.8MB变得非常小了!

为什么会那样呢?

在Golang的情况下,进行build后会生成一个单一的可执行文件,因此基本上只要有该编译好的二进制文件,就可以运行。(在一些情况下,比如HTTPS通信等,这个说法不一定适用。)
因此,可以通过在构建时不包含仅在构建时需要的文件来降低镜像的大小。

总结

我认为我们可以采用多段构建的方式来实现,而不需要使用Multi-stage构建功能,这种方式被称为Builder-Pattern,它将构建和执行分开。然而,由于增加了复杂性,我认为还是采用Multi-stage构建比较好!

由于这是一个非常极端的例子,所以我认为实际上图像尺寸可能会更大。

广告
将在 10 秒后关闭
bannerAds