优化 Docker 镜像大小常见的方式
平时我们构建的Docker镜像通常比较大,占用大量的磁盘空间,随着容器的大规模部署,同样也会浪费宝贵的带宽资源。本文将介绍几种常用的方法来优化Docker镜像大小,这里我们使用DockerHub官方上的Redis镜像进行说明。
手动管理
我们能够直接想到的方法就是直接修改官方的Redis镜像Dockerfile文件,手动删除容器运行后不需要的组件,然后重新构建一个新镜像。这种方法理论上是可行的,但是容易出错,而且效果也不是特别明显。主要是不能和官方的镜像实时同步。
多阶段构建
Docker在17.05版本起提供了多阶段构建的功能来解决这个问题,这种方法是通过丢弃中间层来实现的,并通过中间层来提供有关如何创建最终镜像及其内容信息来完成的,只需要保留容器化应用所需的组件即可。在更上层的实现如下所示:
- 以一些镜像作为构建的基础
- 和平常一样运行命令来构造你的应用
- 将所需的制品复制到另外一个单独的镜像
Distroless
在严重依赖容器化技术,尤其是Docker之后,谷歌早就意识到了使用臃肿镜像的弊端。所以他们提供了自己的方法来解决这个问题,即distroless镜像。与典型的Linux基础镜像(绑定了很多软件)不同,在distroless上对你的应用进行docker化,最终的镜像只包含应用及其运行时的依赖项,大多数Linux发行版中包含的标准软件,如包管理器,甚至shell都被会被排除在外。同样的,要使用Google的distroless镜像,需要使用上面我们提到的多阶段构建,如下所示:
FROMredis:latestASbuild ARGTIME_ZONE RUNmkdir-p/opt/etc&&\ cp-a--parents/lib/x86_64-linux-gnu/libm.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/localtime FROMgcr.io/distroless/base COPY--from=build/opt/ VOLUME/data WORKDIR/data ENTRYPOINT["redis-server"]
使用redis:latest为基础镜像,然后保留需要的一些二进制文件(redis-server二进制文件以及所有的相关依赖),然后使用distroless镜像作为构建的最终镜像的基础,将opt目录内容复制到该镜像目录中来。
然后我们只需要重新构建镜像即可:
$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容器应用更为方便。在DockerHub上面也可以找到几乎所有流行软件的Alpine版本,比如Redis、Nginx、MySQL等等。
GNUGuix
最后,我们可以使用GNUGuix,一个多功能的软件包管理工具,其中就有一项可以创建Docker镜像的功能。Guix区分了包的运行时依赖与构建依赖,所以Guix构建的Docker镜像将只包含明确指定的程序,加上他们的运行时依赖,就像distroless的方法一样。但和distroless不同的时候,distroless需要你自己去查程序的运行时依赖关系(当然也要写Dockerfile),而Guix只需要运行一条命令即可:$guixpack-fdockerredis。
通过上面的命令创建的Redis镜像大小约为70MB,和原本的镜像相比有明显的减少,虽然比distroless和Alpine方法创建的镜像稍大,但使用Guinx确实提供了一些其他的优点。比如,如果你想让你的最终镜像也包含一个shell,以便像Alpine那样去调试,那么只需要在Guxi打包的时候指定上就可以了:$guixpack-fdockerredisbash,如果你想包含其他软件,也可以继续在后面添加即可。
Guix的功能特性意味着包的构建可以100%复用,所以我们可以在CI/CD流水线管道中加入Guix支持,这样构建过程就非常顺畅了。
有的人可能会觉得Guix听起来很酷,但是并不想为了构建更小的Docker镜像而去下载安装另外一个工具,更何况Guix只在Linux下面工作,很多开发者还是MacOS用户,去配置Guix也挺麻烦。其实这点并不用担心,Guix本身也有Docker镜像在DockerHub上,所以使用起来也并不会太复杂,只需要简单的使用$dockerrunguix命令即可。
除了Guix之外,值得一提的还有一个名为Nix的软件包管理工具,对Guix所述的每一点都同样有效并且适用于Nix。
以上就是优化Docker镜像大小常见的方式的详细内容,更多关于优化Docker镜像大小的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。