当前位置: 首页 > 科技观察

Docker镜像大小的常用优化方法

时间:2023-03-12 23:00:15 科技观察

我们构建的Docker镜像通常都比较大,占用磁盘空间比较大。随着容器的大规模部署,宝贵的带宽资源也将被浪费。本文将介绍几种常用的优化Docker镜像大小的方法。这里我们使用DockerHub上的官方Redis镜像来进行说明。我们可以直接想到手动管理的方式就是直接修改Redis官方镜像Dockerfile,在容器运行后手动删除不需要的组件,然后重新构建一个新的镜像。这种方法理论上是可行的,但是容易出错,效果不是特别明显。主要原因是无法与官方镜像实时同步。多阶段构建Docker从17.05版本开始提供了多阶段构建功能来解决这个问题。这种方法是通过丢弃中间层并通过中间层提供有关如何创建最终图像及其内容的信息来实现的,您只需要保留容器化应用程序所需的组件。更高层次的实现看起来是这样的:使用一些图像作为构建运行命令的基础来像往常一样构建你的应用程序将所需的工件复制到另一个单独的图像Distroless在严重依赖容器化技术,特别是Docker之后,谷歌早就意识到了使用臃肿图像的缺点。所以他们提供了自己的方法来解决这个问题,distrolessmirroring。与典型的Linux基础镜像(捆绑了很多软件)不同,将你的应用程序在distroless上进行dockerize,最终镜像只包含应用程序及其运行时依赖项,大多数Linux发行版中包含的标准软件,例如包管理器,甚至贝壳被排除在外。同样,要使用Google的distroless镜像,需要使用我们上面提到的multi-stagebuild,如下:.so.*/opt&&\cp-a--parents/lib/x86_64-linux-gnu/libdl.so.*/opt&&\cp-a--parents/lib/x86_64-linux-gnu/libpthread.so。*/opt&&\cp-a--parents/lib/x86_64-linux-gnu/libc.so.*/opt&&\cp-a--parents/usr/local/bin/redis-server/opt&&\cp-a--parents/usr/local/bin/redis-sentinel/opt&&\cp/usr/share/zoneinfo/${TIME_ZONE:-UTC}/opt/etc/localtimeFROMgcr.io/distroless/baseCOPY--from=build/opt/VOLUME/dataWORKDIR/dataENTRYPOINT["redis-server"]使用redis:latest作为基础镜像,然后保留一些需要的二进制文件(redis-server二进制文件和所有相关依赖),然后使用distroless镜像作为最终镜像build基本上就是将opt目录的内容复制到mirror目录。然后我们只需要重建镜像:$dockerbuild-tredis:distroless.$dockerimagesREPOSITORYTAGIMAGEIDCREATEDSIZEredisdistroless7d50bd873bea15secondsago28.2MBredislatest1319b1eaa0b73daysago104MB我们可以看到镜像从104MB变成了28.2MB,大大减小了镜像的大小。注意:在Linux下,我们可以使用ldd工具来查找指定二进制文件所需要的依赖,比如$ldd$(whichredis-server)。使用distroless镜像来减小Docker镜像的大小是一种非常有效的方法,但是它也有一个明显的缺点就是最终镜像中没有shell程序,这使得Docker容器的调试变得非常非常困难。攻击的危险使其更加安全。如果我们将应用程序部署到Kubernetes集群中,我们可以使用kubectl-debug等工具来辅助调试应用程序。AlpineLinux另一种常用的方式是选择基于AlpineLinux构建应用镜像。AlpineLinux是一个特别适合创建最小Docker镜像的发行版。AplineLinux使用更小的muslC库代替glibc并静态链接它,这意味着为musl编译的程序将成为可重定位(relocatable)的二进制文件,这样就不需要包含共享对象,可以显着减少大小镜像。redis:alpine镜像大约有30MB。这样做的缺点是通常musl的性能不如glibc。当然,还有一个好处,就是与上面的distroless相比,Alpine是一个成熟的Linux发行版,提供了基本的shell访问,使得调试Docker容器应用变得更加容易。几乎所有流行软件的Alpine版本也可以在DockerHub上找到,例如Redis、Nginx、MySQL等。GNUGuix最后,我们可以使用GNUGuix,这是一个多功能的包管理工具,它具有可以创建Docker镜像的功能.Guix区分包的运行时依赖和构建依赖,因此Guix构建的Docker镜像将只包含明确指定的程序,以及它们的运行时依赖,就像distroless方法一样。但与distroless不同的是,distroless需要你自己检查程序的运行时依赖(当然你还需要编写Dockerfile),而Guix只需要运行一条命令:$guixpack-fdockerredis。上述命令创建的Redis镜像大小约为70MB,与原始镜像相比明显缩小。尽管它比使用distroless和Alpine方法创建的镜像略大,但使用Guinx确实提供了一些其他优势。比如你希望你的最终镜像也像Alpine那样包含调试用的shell,只需要在打包Guxi的时候指定:$guixpack-fdockerredisbash,如果你想包含其他软件,也可以以后继续补充。Guix的功能性意味着包构建可以100%重用,因此我们可以将Guix支持添加到我们的CI/CD管道中,以便构建过程非常顺利。有些人可能觉得Guix听起来很酷,但他们不想下载并安装另一个工具来构建更小的Docker镜像,更何况Guix只能在Linux下运行,而且很多开发者还是MacOS用户。挺麻烦的。其实这个大可不必担心。Guix本身在DockerHub上也有一个Docker镜像,所以使用起来并不太复杂。你只需要简单地使用$dockerrunguix命令。除了Guix,值得一提的是还有一个名为Nix的包管理工具,针对Guix陈述的每一点都同样有效,适用于Nix。