对于刚接触容器的人来说,他们很容易被自己构建的 Docker 镜像体积吓到,我只需要一个几 MB 的可执行文件而已,为何镜像的体积会达到 1 GB 以上?本文将会介绍几个奇技淫巧来帮助你精简镜像,同时又不牺牲开发人员和运维人员的操作便利性。
01. 万恶之源
我敢打赌,每一个初次使用自己写好的代码构建 Docker 镜像的人都会被镜像的体积吓到,来看一个例子。
让我们搬出那个屡试不爽的 hello world C 程序:
/* hello.c */
int main () {
puts("Hello, world!");
return 0;
}
并通过下面的 Dockerfile 构建镜像:
FROM gcc
COPY hello.c .
RUN gcc -o hello hello.c
CMD ["./hello"]
然后你会发现构建成功的镜像体积远远超过了 1 GB。。。因为该镜像包含了整个 gcc 镜像的内容。
如果使用 Ubuntu 镜像,安装 C 编译器,最后编译程序,你会得到一个大概 300 MB 大小的镜像,比上面的镜像小多了。但还是不够小,因为编译好的可执行文件还不到 20 KB:
$ ls -l hello
-rwxr-xr-x 1 root root 16384 Nov 18 14:36 hello
类似地,Go 语言版本的 hello world 会得到相同的结果:
package main
import "fmt"
func main () {
fmt.Println("Hello, world!")
}
使用基础镜像 golang 构建的镜像大小是 800 MB,而编译后的可执行文件只有 2 MB 大小:
$ ls -l hello
-rwxr-xr-x 1 root root 2008801 Jan 15 16:41 hello
还是不太理想,有没有办法大幅度减少镜像的体积呢?往下看。
为了更直观地对比不同镜像的大小,所有镜像都使用相同的镜像名,不同的标签。例如:hello:gcc,hello:ubuntu,hello:thisweirdtrick
等等,这样就可以直接使用命令 docker images hello 列出所有镜像名为 hello 的镜像,不会被其他镜像所干扰。
02. 多阶段构建
要想大幅度减少镜像的体积,多阶段构建是必不可少的。多阶段构建的想法很简单:“我不想在最终的镜像中包含一堆 C 或 Go 编译器和整个编译工具链,我只要一个编译好的可执行文件!”
多阶段构建可以由多个 FROM 指令识别,每一个 FROM 语句表示一个新的构建阶段,阶段名称可以用 AS 参数指定,例如:
FROM gcc AS mybuildstage
COPY hello.c .
RUN gcc -o hello hello.c
FROM ubuntu
COPY --from=mybuildstage hello .
CMD ["./hello"]
本例使用基础镜像 gcc 来编译程序 hello.c,然后启动一个新的构建阶段,它以 ubuntu 作为基础镜像,将可执行文件 hello 从上一阶段拷贝到最终的镜像中。最终的镜像大小是 64 MB,比之前的 1.1 GB 减少了 95%:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)