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

Docker Build是Docker Engine最常用的特性之一。无论何时创建映像,都是在使用Docker Build。构建是软件开发生命周期的关键部分,它允许您打包和捆绑代码并将其发布到任何地方。
官网: https://docs.docker.com/build/
Docker引擎使用client-server 架构,由多个组件和工具组成。执行构建的最常用方法是发出docker build命令。CLI将请求发送给Docker Engine,然后由Docker Engine执行构建。
现在Engine中有两个组件可用于构建图像。从18.09版本开始,Engine随Moby BuildKit一起发布,这是默认情况下用于执行构建的新组件。

新的客户端Docker Buildx是一个CLI插件,它扩展了Docker命令,完全支持BuildKit builder工具包提供的功能。docker buildx build命令提供了与docker build相同的用户体验,并增加了许多新功能,如创建作用域构建器实例、针对多个节点并发构建、输出配置、内联构建缓存和指定目标平台。此外,Buildx还支持常规docker构建尚不具备的新特性,比如构建清单列表、分布式缓存以及将构建结果导出到OCI图像压缩文件中。

Docker Build不仅仅是一个简单的构建命令,也不仅仅是打包你的代码。它是一个完整的工具和功能生态系统,不仅支持常见的工作流任务,而且还提供对更复杂和高级场景的支持。

遇到的问题

https://mp.weixin.qq.com/s/z5IOKq5YsqryQSUS3sLuvA
项目介绍:
• 文件大小 5.6 GB
• 文件数量 529352
构建命令及输入如下:

[root@zhz ~]# time DOCKER_BUILDKIT=1 docker build --no-cache -t test:v3 -f Dockerfile .  --progress=plain
#1 [internal] load build definition from Dockerfile
#1 sha256:00d41d11fa30b55c96cb3d12599075673c30354282df2f2e4f02fd2138de4db1
#1 DONE 0.0s

#2 [internal] load .dockerignore
#2 sha256:8f5fff378ef13e49470500e3bbe204878fe0d9da50b5d6c66f3702b582321902
#2 transferring context: 2B 0.0s done
#2 DONE 1.9s

#1 [internal] load build definition from Dockerfile
#1 sha256:00d41d11fa30b55c96cb3d12599075673c30354282df2f2e4f02fd2138de4db1
#1 transferring dockerfile: 75B done
#1 DONE 2.1s

#3 [internal] load metadata for docker.io/library/golang:1.13
#3 sha256:2c97baf25610dce8d75f6fe7c664b39752ce4ec43302705df03e31d2db8ad0f5
#3 DONE 27.7s

#4 [1/2] FROM docker.io/library/golang:1.13@sha256:8ebb6d5a48deef738381b56b1d4cd33d99a5d608e0d03c5fe8dfa3f68d41a1f8
#4 sha256:667fc4869c642aaf07d5bca8a3dcb5c8115d171ffaa8c20ebdc7ec605b3d4a14
#4 resolve docker.io/library/golang:1.13@sha256:8ebb6d5a48deef738381b56b1d4cd33d99a5d608e0d03c5fe8dfa3f68d41a1f8
#4 resolve docker.io/library/golang:1.13@sha256:8ebb6d5a48deef738381b56b1d4cd33d99a5d608e0d03c5fe8dfa3f68d41a1f8 0.6s done
#4 ...

#5 [internal] load build context
#5 sha256:cdb274159471e9afb8cfe96680623be579cd4d519b9d06039895079d1c6fdaca
#5 transferring context: 99B done
#5 DONE 0.7s

#4 [1/2] FROM docker.io/library/golang:1.13@sha256:8ebb6d5a48deef738381b56b1d4cd33d99a5d608e0d03c5fe8dfa3f68d41a1f8
#4 sha256:667fc4869c642aaf07d5bca8a3dcb5c8115d171ffaa8c20ebdc7ec605b3d4a14
#4 sha256:8ebb6d5a48deef738381b56b1d4cd33d99a5d608e0d03c5fe8dfa3f68d41a1f8 2.36kB / 2.36kB done
#4 sha256:24bd48a274920bf47ead96c5a2db8e6a3fbe26e8ae27557c2caa9aeae562a998 1.79kB / 1.79kB done
#4 sha256:d6f3656320fe38f736f0ebae2556d09bf3bde9d663ffc69b153494558aec9a79 6.19kB / 6.19kB done
#4 sha256:edaf0a6b092f5673ec05b40edb606ce58881b2f40494251117d31805225ef064 0B / 10.00MB 0.3s
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 0B / 68.67MB 0.6s
#4 sha256:16b5038bccc853e96f534bc85f4f737109ef37ad92d877b54f080a3c86b3cb3a 0B / 126B 0.7s
#4 sha256:edaf0a6b092f5673ec05b40edb606ce58881b2f40494251117d31805225ef064 2.10MB / 10.00MB 0.9s
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 0B / 50.40MB 0.9s
#4 sha256:edaf0a6b092f5673ec05b40edb606ce58881b2f40494251117d31805225ef064 3.15MB / 10.00MB 1.0s
#4 sha256:c958d65b3090aefea91284d018b2a86530a3c8174b72616c4e76993c696a5797 0B / 7.81MB 1.0s
#4 sha256:edaf0a6b092f5673ec05b40edb606ce58881b2f40494251117d31805225ef064 4.19MB / 10.00MB 1.1s
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 0B / 120.17MB 1.1s
#4 sha256:edaf0a6b092f5673ec05b40edb606ce58881b2f40494251117d31805225ef064 6.29MB / 10.00MB 1.3s
#4 sha256:16b5038bccc853e96f534bc85f4f737109ef37ad92d877b54f080a3c86b3cb3a 126B / 126B 1.3s done
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 0B / 51.83MB 1.3s
#4 sha256:edaf0a6b092f5673ec05b40edb606ce58881b2f40494251117d31805225ef064 7.34MB / 10.00MB 1.4s
#4 sha256:edaf0a6b092f5673ec05b40edb606ce58881b2f40494251117d31805225ef064 8.39MB / 10.00MB 1.5s
#4 sha256:edaf0a6b092f5673ec05b40edb606ce58881b2f40494251117d31805225ef064 9.44MB / 10.00MB 1.7s
#4 sha256:edaf0a6b092f5673ec05b40edb606ce58881b2f40494251117d31805225ef064 10.00MB / 10.00MB 1.8s
#4 sha256:edaf0a6b092f5673ec05b40edb606ce58881b2f40494251117d31805225ef064 10.00MB / 10.00MB 2.0s done
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 3.15MB / 50.40MB 2.5s
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 3.15MB / 51.83MB 2.5s
#4 sha256:c958d65b3090aefea91284d018b2a86530a3c8174b72616c4e76993c696a5797 1.05MB / 7.81MB 3.1s
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 6.29MB / 51.83MB 3.3s
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 6.29MB / 50.40MB 3.8s
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 6.29MB / 120.17MB 4.0s
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 9.44MB / 51.83MB 4.1s
#4 sha256:c958d65b3090aefea91284d018b2a86530a3c8174b72616c4e76993c696a5797 2.10MB / 7.81MB 4.4s
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 4.19MB / 68.67MB 4.9s
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 12.58MB / 51.83MB 4.9s
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 9.44MB / 50.40MB 5.0s
#4 sha256:c958d65b3090aefea91284d018b2a86530a3c8174b72616c4e76993c696a5797 3.15MB / 7.81MB 5.4s
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 15.73MB / 51.83MB 5.6s
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 12.58MB / 50.40MB 6.1s
#4 sha256:c958d65b3090aefea91284d018b2a86530a3c8174b72616c4e76993c696a5797 4.19MB / 7.81MB 6.3s
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 12.58MB / 120.17MB 6.3s
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 18.87MB / 51.83MB 6.5s
#4 sha256:c958d65b3090aefea91284d018b2a86530a3c8174b72616c4e76993c696a5797 5.24MB / 7.81MB 7.1s
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 15.73MB / 50.40MB 7.3s
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 22.02MB / 51.83MB 7.6s
#4 sha256:c958d65b3090aefea91284d018b2a86530a3c8174b72616c4e76993c696a5797 6.29MB / 7.81MB 7.8s
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 18.87MB / 50.40MB 8.2s
#4 sha256:c958d65b3090aefea91284d018b2a86530a3c8174b72616c4e76993c696a5797 7.34MB / 7.81MB 8.5s
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 18.87MB / 120.17MB 8.5s
#4 sha256:c958d65b3090aefea91284d018b2a86530a3c8174b72616c4e76993c696a5797 7.81MB / 7.81MB 8.8s
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 25.17MB / 51.83MB 8.8s
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 8.39MB / 68.67MB 9.1s
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 22.02MB / 50.40MB 9.1s
#4 sha256:c958d65b3090aefea91284d018b2a86530a3c8174b72616c4e76993c696a5797 7.81MB / 7.81MB 9.0s done
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 24.77MB / 50.40MB 9.7s
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 28.31MB / 51.83MB 10.0s
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 28.31MB / 50.40MB 10.5s
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 25.17MB / 120.17MB 10.5s
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 31.46MB / 50.40MB 11.3s
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 31.46MB / 51.83MB 11.3s
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 12.58MB / 68.67MB 11.9s
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 34.01MB / 50.40MB 12.0s
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 34.60MB / 51.83MB 12.4s
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 36.70MB / 50.40MB 12.6s
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 31.46MB / 120.17MB 12.6s
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 39.85MB / 50.40MB 13.3s
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 37.75MB / 51.83MB 13.6s
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 16.78MB / 68.67MB 14.2s
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 42.99MB / 50.40MB 14.2s
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 37.75MB / 120.17MB 14.7s
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 40.89MB / 51.83MB 14.8s
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 46.14MB / 50.40MB 15.0s
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 49.28MB / 50.40MB 15.7s
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 44.04MB / 51.83MB 16.0s
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 20.97MB / 68.67MB 16.2s
#4 sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 50.40MB / 50.40MB 16.3s done
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 44.04MB / 120.17MB 16.6s
#4 extracting sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 47.19MB / 51.83MB 16.8s
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 25.17MB / 68.67MB 17.5s
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 50.33MB / 51.83MB 17.6s
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 50.33MB / 120.17MB 18.0s
#4 sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 51.83MB / 51.83MB 18.4s done
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 29.36MB / 68.67MB 18.6s
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 56.62MB / 120.17MB 19.2s
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 33.55MB / 68.67MB 19.4s
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 37.75MB / 68.67MB 20.3s
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 62.91MB / 120.17MB 20.3s
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 41.94MB / 68.67MB 21.1s
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 69.21MB / 120.17MB 21.3s
#4 extracting sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 5.2s
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 47.19MB / 68.67MB 22.0s
#4 extracting sha256:d6ff36c9ec4822c9ff8953560f7ba41653b348a9c1136755e653575f58fbded7 5.6s done
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 76.55MB / 120.17MB 22.5s
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 51.38MB / 68.67MB 22.8s
#4 extracting sha256:c958d65b3090aefea91284d018b2a86530a3c8174b72616c4e76993c696a5797
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 55.57MB / 68.67MB 23.6s
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 82.84MB / 120.17MB 23.6s
#4 extracting sha256:c958d65b3090aefea91284d018b2a86530a3c8174b72616c4e76993c696a5797 1.0s done
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 59.77MB / 68.67MB 24.4s
#4 extracting sha256:edaf0a6b092f5673ec05b40edb606ce58881b2f40494251117d31805225ef064
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 89.13MB / 120.17MB 24.7s
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 63.96MB / 68.67MB 25.2s
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 68.16MB / 68.67MB 25.9s
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 95.42MB / 120.17MB 25.9s
#4 extracting sha256:edaf0a6b092f5673ec05b40edb606ce58881b2f40494251117d31805225ef064 1.2s done
#4 sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 68.67MB / 68.67MB 26.7s done
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 101.71MB / 120.17MB 26.8s
#4 extracting sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 0.1s
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 108.00MB / 120.17MB 27.7s
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 114.29MB / 120.17MB 28.6s
#4 sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 120.17MB / 120.17MB 30.5s done
#4 extracting sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 5.2s
#4 extracting sha256:80931cf6881673fd161a3fd73e8971fe4a569fd7fbb44e956d261ca58d97dfab 8.1s done
#4 extracting sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551
#4 extracting sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 5.0s
#4 extracting sha256:813643441356759e9202aeebde31d45192b5e5e6218cd8d2ad216304bf415551 9.7s done
#4 extracting sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e
#4 extracting sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 5.1s
#4 extracting sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 10.2s
#4 extracting sha256:799f41bb59c9731aba2de07a7b3d49d5bc5e3a57ac053779fc0e405d3aed0b9e 14.6s done
#4 extracting sha256:16b5038bccc853e96f534bc85f4f737109ef37ad92d877b54f080a3c86b3cb3a 0.0s done
#4 DONE 69.6s

#6 [2/2] COPY ./ /go/src/code
#6 sha256:4fb2c7a142759d1231d57f99d2dde0a38650d35409abd08c8bb59df3157ba6f3
#6 DONE 5.9s

#7 exporting to image
#7 sha256:e8c613e07b0b7ff33893b694f7759a10d42e180f2b4dc349fb57dc6b71dcab00
#7 exporting layers
#7 exporting layers 0.7s done
#7 writing image sha256:a7b65ac37cbb785095718e41913051baee075e28b0d30221ea990b2cfd7b6db5 0.1s done
#7 naming to docker.io/library/test:v3
#7 naming to docker.io/library/test:v3 0.1s done
#7 DONE 1.2s

real	1m49.168s
user	0m0.520s
sys	0m0.357s

其中比较花时间的是:
69s,load build context
6s,执行 COPY 操作
1.2s,导出镜像,镜像大小 5.79GB
以下也是按照这个思路进行逐一排查,测试验证,寻找构建时的 IO 瓶颈。

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

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

编译生成二进制文件

GOOS=linux GOARCH=amd64 go build  -o build main.go

自制二进制提交构建任务

time ./build ./ test:v3

real    5m12.758s
user    0m2.182s
sys     0m14.169s

使用 Go 写的 cli 工具,将构建上下文提交给 Dockerd 进行构建,时长急剧增加;与此同时,构建机的负载飙升。
也可能还有其他优化点,需要慢慢调试。而 Docker CLI 其实也有相关的参数可以用于减少 IO 占用时间。

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

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

使用 compress 优化

time DOCKER_BUILDKIT=1 docker build --no-cache -t test:v3 -f Dockerfile . --compress

real    1m46.117s
user    0m18.551s
sys     0m7.803s

使用 stream 优化

time DOCKER_BUILDKIT=1 docker build --no-cache -t test:v3 -f Dockerfile . --stream

real    1m51.825s
user    0m19.399s
sys     0m7.657s

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

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

1.准备测试文件

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

du -h --max-depth=1

119M    ./data
119M    

2.测试 Dockerfile

FROM golang:1.13
COPY ./ /go/src/code

3.构建命令

DOCKER_BUILDKIT=1 docker build --no-cache -t test:v3 -f Dockerfile .

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

文件大小构建时长文件个数
119M0.3s1个
237M0.4s2个
355M0.5s3个
473M0.6s4个
1.3G3.7s11个
2.6G9.0s22个

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

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

文件大小构建时长文件个数
2.9G13.8s264724个
5.6G37.1s529341个

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

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

5.6G 529341个

并发量构建时长
137.1s
246s
381s

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

Rand_Write_Testing: (groupid=0, jobs=1): err= 0: pid=30436
  write: IOPS=37.9k, BW=148MiB/s (155MB/s)(3072MiB/20752msec); 0 zone resets

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

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

如果提示找不到 docker build,则需要开启 EXPERIMENTAL 或者没有 buildx,需要下载 docker-buildx 到 /usr/libexec/docker/cli-plugins/ 目录。
查看 build 缓存

[root@zhz ~]# docker system df  -v
Images space usage:
···
Containers space usage:
···
Local Volumes space usage:
···
Build cache usage: 64B

清理全部 build 缓存

DOCKER_BUILDKIT=1 docker builder prune -f 

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

定时清理远期的构建缓存
清理掉 72h 之前的缓存

DOCKER_CLI_EXPERIMENTAL=enabled docker buildx prune --filter "until=72h" -f

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

测试 CPU 限制

Dockerfile 文件

FROM ubuntu
RUN apt-get update -y
RUN apt-get install -y stress
RUN stress -c 40
DOCKER_BUILDKIT=1 docker build --no-cache -t test:v3 -f Dockerfile .

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

在 Dockerfile 中测试 IO

Dockerfile 文件

FROM ubuntu
RUN apt-get update -y
RUN apt-get install -y fio
RUN fio -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_Testing
DOCKER_BUILDKIT=1 docker build --no-cache -t test:v3 -f Dockerfile . 

输出结果:
Rand_Write_Testing: (groupid=0, jobs=1): err= 0
   write: IOPS=17.4k, BW=67.9MiB/s (71.2MB/s)(3072MiB/45241msec); 0 zone resets

在容器中测试 IO

docker run -it shaowenchen/demo-fio bash

Rand_Write_Testing: (groupid=0, jobs=1): err= 0
  write: IOPS=17.4k, BW=68.1MiB/s (71.4MB/s)(3072MiB/45091msec); 0 zone resets

在容器的存储卷中测试 IO

docker run -v /tmp:/tmp -it shaowenchen/demo-fio bash

Rand_Write_Testing: (groupid=0, jobs=1): err= 0
  write: IOPS=39.0k, BW=152MiB/s (160MB/s)(3072MiB/20162msec); 0 zone resets

在主机上试 IO

Rand_Write_Testing: (groupid=0, jobs=1): err= 0
  write: IOPS=38.6k, BW=151MiB/s (158MB/s)(3072MiB/20366msec); 0 zone resets

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

直接使用 buildkitd 构建效果不佳

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

安装 buildkit

wget https://github.com/moby/buildkit/releases/download/v0.11.2/buildkit-v0.11.2.linux-amd64.tar.gz
tar xvf buildkit-v0.11.2.linux-amd64.tar.gz
mv bin/* /usr/local/bin/

部署 buildkitd

cat > /usr/lib/systemd/system/buildkitd.service <<EOF
[Unit]
Description=/usr/local/bin/buildkitd
ConditionPathExists=/usr/local/bin/buildkitd
After=containerd.service

[Service]
Type=simple
ExecStart=/usr/local/bin/buildkitd
User=root
Restart=on-failure
RestartSec=1500ms

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl restart buildkitd
systemctl enable buildkitd

systemctl status buildkitd			# 查看到 buildkitd 正常运行即可。

测试 buildctl 提交构建

buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=. --no-cache --output type=docker,name=test:v4 | docker load

[+] Building 240.8s (7/7) FINISHED

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

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

查看 Dockerd 处理逻辑

在代码 https://github.com/moby/moby/blob/8d193d81af9cbbe800475d4bb8c529d67a6d8f14/builder/dockerfile/dispatchers.go 可以找到处理 Dockerfile 的逻辑。
1,Add 和 Copy 都是调用 performCopy 函数
2,performCopy 中调用 NewRWLayer() 新建层,调用 exportImage 写入数据
因此,怀疑的是 Dockerd 写镜像层速度慢。

测试镜像层写入速度

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

time docker load < /tmp/16GB.tar
real    2m43.288s

☑ 保存镜像

time docker save 0d08de176b9f > /tmp/16GB.tar
real    2m48.497s

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

存储驱动怎么选

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

存储驱动文件系统要求高频写入性能稳定性其他
overlay2xfs、ext4当前首选
fuse-overlayfs无限制--适用 rootless 场景
btrfsbtrfs--
zfszfs--
vfs无限制--不建议生产
aufsxfs、ext4-Docker 18.06 及之前版本首选,不维护
devicemapperdirect-lvm不维护
overlayxfs、ext4差,但好于 overlay2-不维护

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

测试 Btrfs 存储驱动

** 在主机上**

Rand_Write_Testing: (groupid=0, jobs=1): err= 0
  write: IOPS=40.0k, BW=160MiB/s (168MB/s)(3072MiB/19191msec); 0 zone resets

☑ 容器下的测试命令

# 运行容器
docker run -it shaowenchen/demo-fio bash

# 执行测试
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 存储驱动

[root@zhz ]# docker info
Server Version: 20.10.12
Storage Driver: overlay2
  Backing Filesystem: btrfs

Rand_Write_Testing: (groupid=0, jobs=1): err= 0: pid=78: Thu Feb  2 02:41:48 2023
  write: IOPS=21.5k, BW=84.1MiB/s (88.2MB/s)(3072MiB/36512msec); 0 zone resets

☑ 测试 btrfs 存储驱动

[root@zhz ]# docker info
Server Version: 20.10.12
Storage Driver: btrfs
  Build Version: Btrfs v5.4.1 

Rand_Write_Testing: (groupid=0, jobs=1): err= 0
  write: IOPS=39.8k, BW=156MiB/s (163MB/s)(3072MiB/19750msec); 0 zone resets

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

多阶段构建

删除构建依赖项

为什么要使用多阶段构建?
✔ 将构建与运行时环境分开
✔ DRY方式
✔ 具有开发,测试等环境的不同详细信息
✔ 线性化依赖关系
✔ 具有特定于平台的阶段

FROM maven:3.6-jdk-8-alpine AS builder  
WORKDIR /app  
COPY pom.xml .  
RUN mvn -e -B dependency:resolve  
COPY src ./src  
RUN mvn -e -B package  
FROM openjdk:8-jre-alpine  
COPY --from=builder /app/target/my-app-1.0-SNAPSHOT.jar /  
CMD [“java”, “-jar”, “/my-app-1.0-SNAPSHOT.jar”] 

不同的镜像风格

下面的Dockerfile显示了基于Debian和基于Alpine的镜像的不同阶段。

FROM maven:3.6-jdk-8-alpine AS builder  
…  
FROM openjdk:8-jre-jessie AS release-jessie  
COPY --from=builder /app/target/my-app-1.0-SNAPSHOT.jar /  
CMD [“java”, “-jar”, “/my-app-1.0-SNAPSHOT.jar”]  
FROM openjdk:8-jre-alpine AS release-alpine  
COPY --from=builder /app/target/my-app-1.0-SNAPSHOT.jar /  
CMD [“java”, “-jar”, “/my-app-1.0-SNAPSHOT.jar”] 

要构建特定的镜像,我们可以使用--target参数:
time docker build --no-cache --target release-jessie .

不同的镜像风格(DRY /全局ARG)

ARG flavor=alpine  
FROM maven:3.6-jdk-8-alpine AS builder  
…  
FROM openjdk:8-jre-$flavor AS release  
COPY --from=builder /app/target/my-app-1.0-SNAPSHOT.jar /  
CMD [“java”, “-jar”, “/my-app-1.0-SNAPSHOT.jar”] 

ARG命令可以指定要构建的镜像。在上面的例子中,我们指定alpine为默认的镜像,但我们也可以在docker build命令中,通过--build-arg flavor=参数指定镜像。
time docker build --no-cache --target release --build-arg flavor=jessie .

总结

本篇主要观点如下:
✔ compress、stream 参数对构建速度不一定有效
✔ 减少构建上下文大小,有利于缓解构建 IO 压力
✔ Buildkit 的缓存可以不用频繁清理
✔ 构建 Dockerfile 执行命令时,CPU、Mem 不会受到限制,但 IO 速度慢
✔ 使用 buildkitd 构建速度不如 Dockerd 开启 DOCKER_BUILDKIT
✔ 使用 Btrfs 存储有利于获得更好的 IO 速度
但最简单的还是使用 4K 随机读写快的磁盘,在拿到新的环境用于生产之前,务必先进行测试,仅当满足需求时,再执行后续计划。

There is eternal light in the eye and eternal hope in the heart

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星光落入你灰蒙蒙的眼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值