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

排查构建镜像时IO慢的问题

时间:2023-03-20 19:34:17 科技观察

1.遇到的问题项目介绍:文件大小5.6GB文件数量529352DockerfileFROMgolang:1.13COPY.//go/src/code构建命令及输入如下:timeDOCKER_BUILDKIT=1dockerbuild--no-cache-ttest:v3-f文件。--progress=plain#1[internal]从Dockerfile#1sha256:2a154d4ad813d1ef3355d055345ad0e7c5e14923755cea703d980ecc1c576ce7#1transferring#internal3n1B#dockerfile:[.]load.dockerignore#2sha256:9598c0ddacf682f2cac2be6caedf6786888ec68f009c197523f8b1c2b5257b34#2transferringcontext:2Bdone#2DONE0.2s#3[internal]loadmetadataforgolang:1.13#3sha256:0c7952f0b4e5d57d371191fa036da65d51f4c4195e1f4e1b080eb561c3930497#3DONE0.0s#4[1/2]FROMgolang:1.13#4sha256:692ef5b58e708635d7cbe3bf133ba934336d80cde9e2fdf24f6d1af56d5469ed#4CACHED#5[internal]loadbuildcontext#5sha256:f87f36fa1dc9c0557ebc53645f7ffe404ed3cfa3332535260e5a4a1d7285be3c#5transferringcontext:18.73MB4.8s#5transferringcontext:38.21MB9.8sdone#5DONE10.5s#6[2/2]COPY.//go/src/code#6sha256:2c63806741b84767def3d7cebea3872b91d7ef00bd3d524f48976077cce3849a#6DONE26.8s#7exportingtoimage#7sha256:e8c613e07b0b7ff33893b694f7759a10d42e180f2b4dc349fb57dc6b71dcab00#7exportinglayers#7exportinglayers67.5sdone#7writingimagesha256:03b278543ab0f920f5af0540d93c5e5340f5e1f0de2d389ec21a2dc82af96754done#7namingtodocker.io/library/test:v3done#7DONE67.6sreal1m45.411suser0m18.374ssys0m7.344s其中比较花时间的是:10s,loadbuildcontext26s,执行COPY操作67s,导出镜像,5.79GB以下的镜像大小也是按照这个思路一一检查,测试验证,寻找构建时的IO瓶颈2.自制的goclient直接提交给Dockerd构建效果不是很好的项目https://img.ydisp.cn/news/20230210/0n5knj0s5o4实现的功能是将本地的Dockerfile和context提交给Dockerd构建,测试DockerCLI提交文件是否有瓶颈。2.1编译生成二进制文件GOOS=linuxGOARCH=amd64gobuild-obuildmain.go2.2自制二进制提交构建任务时./build./test:v3real5m12.758suser0m2.182ssys0m14.169s使用cliGo编写的工具,将构建上下文提交给Dockerd进行构建,时间急剧增加;与此同时,构建机器的负载猛增。可能还有其他优化点需要慢慢调试。其实DockerCLI也有相关的参数可以用来减少IO时间。3.构建参数compress和stream参数优化不佳。compress会将上下文压缩成gzip格式进行传输,而stream会将上下文以流的形式传输。3.1使用压缩优化时间DOCKER_BUILDKIT=1dockerbuild--no-cache-ttest:v3-fDockerfile。--compressreal1m46.117suser0m18.551ssys0m7.803s3.2使用流优化时间DOCKER_BUILDKIT=1dockerbuild--no-cache-ttest:v3-fDockerfile。--streamreal1m51.825suser0m19.399ssys0m7.657s这两个参数对缩短构建时间没有影响。但是需要注意的是,测试项目的文件比较大,而且很多。如果测试用例发生变化,可能会产生不同的效果。接下来我们看看文件数量和文件大小对Dockerd镜像构建的影响。4.文件数量对COPY的影响远小于文件大小4.1准备测试文件du-h--max-depth=1119M./data119M。数据目录下放了一个119MB的文件,通过复制文件不断增加buildcontext的大小。4.2测试DockerfileFROMgolang:1.13COPY.//go/src/code4.3构建命令DOCKER_BUILDKIT=1dockerbuild--no-cache-ttest:v3-fDockerfile.4.4测试文件大小对COPY文件大小构建影响明显time文件数量为119M0.3s1237M0.4s2355M0.5s3473M0.6s41.3G3.7s112.6G9.0s22文件大小对COPY影响显着,接近线性增长。4.5测试文件个数对COPY影响不大。文件大小和构建时间。文件数量为2.9G13.8s264724和5.6G37.1s529341。文件数量对COPY影响不大。这是因为当DockerCLI将构建上下文发送给Dockerd时,它将使用tar打包上下文,而不是一个一个地传输文件。4.6构建并发瓶颈在磁盘IO5.6G529341并发构建时间为137.1s246s381s。通过iotop可以实时观察磁盘写入速度,最快可以达到200MB/s,最接近文件系统的4K随机写入速度。Rand_Write_Testing:(groupid=0,jobs=1):err=0:pid=30436write:IOPS=37.9k,BW=148MiB/s(155MB/s)(3072MiB/20752msec);0区由于一个普通的Dockerd重置,并发时,Dockerd的吞吐量会成为瓶颈,系统盘IO也会成为瓶颈。5.不清除Buildkit缓存对新构建影响不大。如果提示找不到dockerbuild,需要开启EXPERIMENTAL或者没有buildx,需要下载docker-buildx到/usr/libexec/docker/cli-plugins/目录下。查看构建缓存dockersystemdf-vclearallbuildcachesDOCKER_BUILDKIT=1dockerbuilderprune-f只有在启用BuildKit时才会生成构建缓存。生产环境缓存大小达到1.408TB,但清理前后,新建项目的构建速度没有明显变化;对于老项目,如果没有变化,命中缓存后的速度是非常快的。可能的原因是缓存虽然大,但是条目不多,查询是否有缓存的时间开销很小。但是,定期定理缓存有利于防止磁盘已满的风险。定期清理long-termbuildcache,在72hDOCKER_CLI_EXPERIMENTAL=enableddockerbuildxprune--filter"until=72h"-f6之前清除缓存。build不会限制CPU但IO速度很慢6.1测试CPU限制测试:v3-fDockerfile。构建机器有40C,构建过程中机器的CPU负载可以达到95%,说明构建,Dockerd默认不限制CPU消耗。在生产环境中,曾经出现过npmrunbuild占用十几GB内存的场景,所以我判断Dockerd默认是不限制内存消耗的。6.2测试Dockerfile中的IODockerfileFROMubuntuRUNapt-getupdate-yRUNapt-getinstall-yfioRUNfio-direct=1-iodepth=128-rw=randwrite-ioengine=libaio-bs=4k-size=3G-numjobs=1-runtime=1000-group_reporting-filename=/tmp/test.file--allow_mounted_write=1-name=Rand_Write_TestingDOCKER_BUILDKIT=1dockerbuild--no-cache-ttest:v3-fDockerfile。Rand_Write_Testing:(groupid=0,jobs=1):err=0write:IOPS=17.4k,BW=67.9MiB/s(71.2MB/s)(3072MiB/45241msec);0zoneresets6.3在容器中测试IODockerrun-itshaowenchen/demo-fiobashRand_Write_Testing:(groupid=0,jobs=1):err=0write:IOPS=17.4k,BW=68.1MiB/s(71.4MB/s)(3072MiB/45091毫秒);0zoneresets6.4存储在容器中TestIODockerrun-v/tmp:/tmp-itshaowenchen/demo-fiobashRand_Write_Testing:(groupid=0,jobs=1):err=0write:IOPS=39.0k,BW=152MiB/秒(160MB/秒)(3072MiB/20162毫秒);0zoneresets6.5测试IORand_Write_Testingonhost:(groupid=0,jobs=1):err=0write:IOPS=38.6k,BW=151MiB/s(158MB/s)(3072MiB/20366毫秒);0区resetsDockerd在构建Dockerfile时,遇到Run命令时,会启动一个容器运行,然后提交镜像。从测试结果可以看出,Dockerfile中的IO速度远低于宿主机,与容器中的IO不同。一致的速度;主机存储卷的IO速度与主机的IO速度一致。7、直接使用buildkitd构建效果不好。虽然DOCKER_BUILDKIT=1也可以开启Buildkit进行构建,但是如果直接使用buildkitd并且效果不错,替代Dockerd进行构建也是一个不错的选择。7.1安装buildkitwgethttps://github.com/moby/buildkit/releases/download/v0.11.2/buildkit-v0.11.2.linux-amd64.tar.gztarxvfbuildkit-v0.11.2.linux-amd64.tar.gzmvbin/*/usr/local/bin/7.2deploybuildkitdcat>/usr/lib/systemd/system/buildkitd.service</tmp/16GB.tarreal2m48.497sdockerload速度和dockersave差不多,镜像层的处理速度是大约100MB/秒。这个速度比磁盘4K随机写入速度低了近30%。在我看来,如果是自用,勉强可以接受;如果用于对外提供构建服务的平台产品,这个盘显然不适合。8.3如何选择存储驱动下面是编译自https://img.ydisp.cn/news/20230210/tdbqflicmow的对比表:存储驱动文件系统要求高频写入性能和稳定性其他overlay2xfs和ext4较差目前首选fuse-overlayfsUnlimited--适用于rootless场景btrfs好--zfszfs好--vfsUnlimited--不推荐制作aufsxfs,ext4-goodDocker18.06及之前版本优先,不维护devicemapperdirect-lvm不维护overlayxfs,ext4差,但比overlay2好——不维护排除非维护非生产适用,选项不多。刚好有一台机器,前段时间初始化的时候,把磁盘格式化成了Btrfs文件格式,可以用来测试。高密度PaaS系统推荐使用zfs存储驱动。8.4在主机上测试Btrfs存储驱动Rand_Write_Testing:(groupid=0,jobs=1):err=0write:IOPS=40.0k,BW=160MiB/s(168MB/s)(3072MiB/19191msec);0区重置容器下面的测试命令运行容器dockerrun-itshaowenchen/demo-fiobash执行测试fio-direct=1-iodepth=128-rw=randwrite-ioengine=libaio-bs=4k-size=3G-numjobs=1-runtime=1000-group_reporting-filename=/data/test.file--allow_mounted_write=1-name=Rand_Write_Testing测试overlay2存储驱动程序dockerinfoServer版本:20.10.12存储驱动程序:overlay2支持文件系统:btrfsRand_Write_Testing:(groupid=0,jobs=1):err=0:pid=78:ThuFeb202:41:482023write:IOPS=21.5k,BW=84.1MiB/s(88.2MB/s)(3072MiB/36512msec);0区域重置测试btrfs存储驱动程序dockerinfoServer版本:20.10.12存储驱动程序:btrfs构建版本:Btrfsv5.4.1Rand_Write_Testing:(groupid=0,jobs=1):err=0write:IOPS=39.8k,BW=156MiB/秒(163MB/秒)(3072MiB/19750毫秒);0zoneresets可以清楚的看到btrfs存储驱动比overlay2快。9.小结本文主要记录排查生产环境中遇到的Dockerfile构建慢IO问题的过程。通过设计各种测试用例,逐一验证每一个要素来排查问题需要很大的耐心,特别容易走错方向,得出错误的结论。这篇文章的要点如下:compress和stream参数不一定对构建速度有效。减小构建上下文的大小,有利于缓解构建IO的压力。构建Dockerfile时可以经常清理Buildkit的缓存。执行命令时,CPU和Mem不会受到限制,但IO速度较慢。使用buildkitd没有Dockerd快。开启DOCKER_BUILDKIT并使用Btrfs存储以获得更好的IO速度,但最简单的方法是使用具有快速随机读写的4K磁盘。在拿到新的生产环境之前,一定要先进行测试,满足要求后才执行后续计划。10.参考https://docs.docker.com/engine/reference/commandline/build/https://docs.docker.com/build/install-buildx/https://flyer103.com/2022/08/20220806-buildkitd-usage/https://pepa.holla.cz/2019/11/18/how-build-own-docker-image-in-golang/