Docker 镜像体积优化
本文最后更新于 2024年6月7日 下午
在我使用docker对我目前负责的一个项目进行部署时,遇到了这样的一个问题,使用dockerfile构建出的docker镜像的体积太大了。当时我只觉得这是不可避免的,毕竟容器的底层还是运行着一个操作系统,加之Golang的编译环境,再加上项目的代码以及静态资源,最终构建出的镜像体积为1.1G似乎是一件再正常不过的事情。
Version 1
以下是我第一版用于构建docker镜像的dockerfile,可以看到为了保证最终容器的兼容性,我使用1.16版本的Golang镜像作为基础镜像,该镜像的下层为Ubuntu镜像。
1 | |

打包之后,查看一下生成的镜像,发现该镜像的大小为1.1G,显然达到了一个不能接受的大小,但令人难以置信的是一开始我就将这个镜像推送至DockerHub,然后又将这个镜像拉取到部署到服务器上。
Version 2
由于考虑到,上一个版本中我们是以Ubuntu作为底层镜像来进行镜像构建的,但实际上我们并不需要Ubuntu中附加的诸多软件和功能,我们仅仅需要一个能够运行代码的环境,那么有没有这样的操作系统镜像呢?Alpine 操作系统是一个面向安全的轻型 Linux 发行版。它不同于通常 Linux 发行版,Alpine 采用了 musl libc 和 busybox 以减小系统的体积和运行时资源消耗。Alpine Docker镜像也继承了Alpine Linux发行版的这些优势。相比于其他 Docker镜像,它的容量非常小,仅仅只有5 MB左右(对比Ubuntu系列镜像接近200 MB),且拥有非常友好的包管理机制。官方镜像来自 docker-alpine 项目。为了减小docker镜像的体积,这次使用了golang:alpine作为基础镜像进行构建,其中移除了非必要的工具,只提供最基础的功能,因此相比我们第一个版本使用的Ubuntu镜像可以节省很多的体积。
1 | |


如我们所预料的那样,使用这个方法改进后的镜像大小,下降了400M左右,但是700M还是让人无法接受,我们使用命令查看一下镜像构建时每层的大小。可以看到在LABEL命令执行前,文件的体积已经很大了。这是因为我们还是依赖Golang的编译环境,并且在编译Golang时还要通过网络拉取很多的依赖库,导致最终的镜像体积爆炸。
Version 3
我们着手想办法继续减小构建出的镜像体积。我了解到一种分阶段构建的方法,也即先使用一个拥有编译环境的镜像对代码进行编译,然后将编译后的代码拷贝至一个新的alpine镜像中运行我们编译后的二进制文件。
1 | |

通过这个方法,我们发现镜像的大小大幅下降,下降至30M。
Version 4
到这里镜像的大小已经大幅度压缩了,还有什么办法可以进一步减小镜像的大小吗?办法还真有!Alpine相比于Ubuntu是一个大小足够小的操作系统镜像,但是其中还是携带了一部分的包管理工具,这些包管理工具对我们来说还是多余的,能否将他们也去掉呢?
这时候就需要用到scratch镜像,官方说明:该镜像是一个空的镜像,可以用作构建docker镜像的最小基础镜像使用,并且在其上可以运行没有依赖的二进制文件。这个镜像无疑是最满足我们需求的。
1 | |

构建完成,镜像的体积又一次减少了7M。
Version 5
我们再过分一点,在go build阶段添加参数,去掉了调试信息以减小镜像尺寸,并且禁用cgo。
1 | |

镜像的体积又稍微减少了一些,至此我停止继续折腾它了。