Bootstrap

通过Dockerfile对镜像文件压缩

编写了一个go程序:

package main

import "fmt"

func main () {
  fmt.Println("Hello, world!")
}

编写Dockerfile文件:

FROM golang # 基础镜像
COPY hello.go # 复制内容到镜像
RUN go build hello.go # 运行go命令
CMD ["./hello"] # 执行生成的hello文件

执行Dockerfile

docker build -f dockerfile -t myhellogo .

-f:指定Dockerfile文件名;
-t:指定生成的新镜像的镜像名
.:表示当前目录

运行生成的这个镜像:

docker run -it myhellogo

我们会发现,编译hello.go生成的可执行文件的大小只有2MB。
在这里插入图片描述
然而所生成镜像的大小:852MB!!!
在这里插入图片描述
这是因为我们FROM golang,该镜像包含了整个golang镜像的内容。解决办法两个:1. 多阶段构建;2.

多阶段构建

B站学习视频
主要思想:我不想在最终的镜像中包含一堆例如Go编译器和整个编译工具链,我只需要一个编译好的可执行文件。

多阶段构建实际上是由多个FROM指令识别,每一个FROM语句表示一个新的构建阶段,同时可以用AS参数指定构建阶段名。
如下:

FROM golang
WORKDIR /src
COPY hello.go .
RUN go build hello.go

FROM ubuntu
COPY --from=0 /src/hello .
CMD ["./hello"]

docker build之后会发现生成了一个新的镜像,我们来看一下这两个镜像大小的对比:
在这里插入图片描述
使用多阶段构建构建出来的镜像单阶段构建的镜像少了约91%。

另外,建议在第一阶段使用经典的基础镜像,例如Ubuntu、Centoos、Debian、Fedora。(Alpine虽然很小但是还是存在一些坑的)。

COPY --from,使用的是绝对路径

从上一个构建阶段拷贝文件时,使用的路径是相对于上一阶段的根目录的。
最好的方法是在第一阶段指定 WORKDIR,在第二阶段使用绝对路径拷贝文件,这样即使基础镜像修改了 WORKDIR,也不会影响到镜像的构建。

FROM scratch

scratch 是一个虚拟镜像,不能被 pull,也不能运行,因为它表示空、nothing!这就意味着新镜像的构建是从零开始,不存在其他的镜像层。

FROM golang
WORKDIR /src
COPY hello.go .
RUN go build hello.go

FROM scratch
COPY --from=0 /src/hello .
CMD ["./hello"]

docker build之后我们查看镜像:
在这里插入图片描述
本来hello.go这个文件生成的可执行文件只有2MB,我们使用这个方法构建出来的镜像最终大小变成了2MB。压缩到极致了。

但是使用scratch仍有许多不便:缺少shell。

这就意味着 CMD/RUN 语句中不能使用字符串。解决方法:使用 JSON 语法取代字符串语法
如下是错误的:

FROM scratch
COPY --from=0 /go/hello .
CMD ./hello

镜像中并不包含 /bin/sh,所以无法运行程序。这是因为当你在 CMD/RUN 语句中使用字符串作为参数时,这些参数会被放到 /bin/sh 中执行( = CMD /bin/sh -c “./hello”)。

改成如下就正确了:

FROM scratch
COPY --from=0 /go/hello .
CMD ["./hello"]

这样 Docker 就会直接运行程序,不会把它放到 shell 中运行。

参考

;