首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

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

1. 遇到的问题

项目介绍:

文件大小 5.6 GB

文件数量 529352

Dockerfile

构建命令及输入如下:

其中比较花时间的是:

10s,load build context

26s,执行 COPY 操作

67s,导出镜像,镜像大小 5.79GB

以下也是按照这个思路进行逐一排查,测试验证,寻找构建时的 IO 瓶颈。

2. 自制 go client 直接提交给 Dockerd 构建效果不佳

工程 https://github.com/shaowenchen/demo/tree/master/buidl-cli 实现的功能就是将本地的 Dockerfile 及上下文提交给 Dockerd 进行构建,从而测试 Docker CLI 是否有提交文件上的瓶颈。

2.1 编译生成二进制文件

2.2 自制二进制提交构建任务

使用 Go 写的 cli 工具,将构建上下文提交给 Dockerd 进行构建,时长急剧增加;与此同时,构建机的负载飙升。

也可能还有其他优化点,需要慢慢调试。而 Docker CLI 其实也有相关的参数可以用于减少 IO 占用时间。

3. 构建参数 compress、stream 参数优化效果不佳

compress 会将上下文压缩为 gzip 格式进行传输,而 stream 会以流的形式传输上下文。

3.1 使用 compress 优化

3.2 使用 stream 优化

这两个参数对缩短构建时间,并没有什么效果。但需要注意的是测试项目的文件大而且数量多,如果测试用例发生变化,可能产生不同的效果。接着,我们一起看看文件数量、文件大小对 Dockerd 构建镜像的影响。

4. 文件数量对 COPY 影响远不及文件大小

4.1 准备测试文件

在 data 目录下放置了一个 119MB 的文件,通过复制该文件不断增加 build context 的大小。

4.2 测试 Dockerfile

4.3 构建命令

4.4 测试文件大小对 COPY 影响明显

文件大小对 COPY 影响明显,接近线性增长。

4.5 测试文件数量对 COPY 影响甚微

文件数量对 COPY 影响不大。这是由于在 Docker CLI 将 build context 发送给 Dockerd 时,会对 context 进行 tar 打包,并不是一个一个文件传输。

4.6 构建并发数的瓶颈在磁盘IO

5.6G 529341个

通过 可以实时观测到磁盘写速度,最快能达到 200MB/s,与文件系统 4K 随机写速度最接近。

由于公用一个 Dockerd,并发时 Dockerd 吞吐会有瓶颈,系统磁盘 IO 也会成为瓶颈。

5. 不清理 Buildkit 缓存对新的构建影响甚微

如果提示找不到 ,则需要开启 或者没有 buildx,需要下载 到 目录。

查看 build 缓存

清理全部 build 缓存

仅当开启 BuildKit 时,才会产生 Build cache。生产环境的缓存大小达到 1.408TB,但比较清理前后,对于新项目的构建并没有发现明显构建速度变化;对于老项目,如果没有变动,命中缓存后速度很快。可能的原因是缓存虽大但条目不多,查询是否有缓存的时间开销很小。

但定期定理缓存,有利于预防磁盘被占满的风险。

定时清理远期的构建缓存

清理掉 72h 之前的缓存

6. 构建不会限制 CPU 但 IO 速度很慢

6.1 测试 CPU 限制

Dockerfile 文件

构建机有 40C,构建时机器 CPU 负载能达到 95%,说明构建时,Dockerd 默认不会对 CPU 消耗进行限制。在生产环境下,出现过 占用 十几个 GB 内存的场景,因此我判断 Dockerd 默认也不会对内存消耗进行限制。

6.2 在 Dockerfile 中测试 IO

Dockerfile 文件

6.3 在容器中测试 IO

6.4 在容器的存储卷中测试 IO

6.5 在主机上试 IO

Dockerd 在构建 Dockerfile 时,遇到 Run 命令会启动一个容器运行,然后提交镜像。从测试结果,可以看到 Dockerfile 中的 IO 速度远达不到主机的,与容器中的 IO 速度一致;主机存储卷的 IO 速度与主机的 IO 速度一致。

7. 直接使用 buildkitd 构建效果不佳

虽然可以通过 开启 Buildkit 构建,但如果直接使用 buildkitd 效果不错,用于替换 Dockerd 构建也是一个不错的选择。

7.1 安装 buildkit

7.2 部署 buildkitd

查看到 buildkitd 正常运行即可。

7.3 测试 buildctl 提交构建

使用 buildctl 提交给 buildkitd 进行构建,需要的时间更多,达到 4min,较之前增加一倍。

8. 当前存储驱动下读写镜像有瓶颈

8.1 查看 Dockerd 处理逻辑

在代码 https://github.com/moby/moby/blob/8d193d81af9cbbe800475d4bb8c529d67a6d8f14/builder/dockerfile/dispatchers.go 可以找到处理 Dockerfile 的逻辑。

1,Add 和 Copy 都是调用 performCopy 函数2,performCopy 中调用 NewRWLayer() 新建层,调用 exportImage 写入数据

因此,怀疑的是 Dockerd 写镜像层速度慢。

8.2 测试镜像层写入速度

准备一个镜像,大小 16GB,一共 18 层。

导入镜像

保存镜像

和 速度差不多,对镜像层的处理速度大约为 100 MB/s。这个速度比磁盘 4K 随机写速度少了近 30%。在我看来,如果是个人使用勉强接受;如果用于对外提供构建服务的平台产品,这块磁盘显然是不合适的。

8.3 存储驱动怎么选

下面是从 https://docs.docker.com/storage/storagedriver/select-storage-driver/ 整理得出的一个比较表格:

排除不维护和非生产适用的,可选项其实没几个。正好有一台机器,前段时间初始化时,将磁盘格式化成 Btrfs 文件格式,可以用于测试。zfs 存储驱动推荐用于高密度 PaaS 系统。

8.4 测试 Btrfs 存储驱动

在主机上

容器下的测试命令

运行容器

执行测试

测试 overlay2 存储驱动

测试 btrfs 存储驱动

可以明显看到 btrfs 存储驱动在速度上优于 overlay2。

9. 总结

本篇主要是记录在生产环境下碰到的 Dockerfile 构建 IO 慢问题排查过程。

通过设计各种测试案例排查问题,对各个要素进行一一验证,需要极大耐心,也特别容易走错方向,得出错误结论。

本篇主要观点如下:

compress、stream 参数对构建速度不一定有效

减少构建上下文大小,有利于缓解构建 IO 压力

Buildkit 的缓存可以不用频繁清理

构建 Dockerfile 执行命令时,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/

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20230210A08SQU00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券