排查构建镜像时 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 影响明显

文件大小 构建时长 文件个数
119M 0.3s 1个
237M 0.4s 2个
355M 0.5s 3个
473M 0.6s 4个
1.3G 3.7s 11个
2.6G 9.0s 22个

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

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

文件大小 构建时长 文件个数
2.9G 13.8s 264724个
5.6G 37.1s 529341个

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

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

5.6G 529341个

并发量 构建时长
1 37.1s
2 46s
3 81s

通过 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/ 整理得出的一个比较表格:

存储驱动 文件系统要求 高频写入性能 稳定性 其他
overlay2 xfs、ext4 当前首选
fuse-overlayfs 无限制 - - 适用 rootless 场景
btrfs btrfs - -
zfs zfs - -
vfs 无限制 - - 不建议生产
aufs xfs、ext4 - Docker 18.06 及之前版本首选,不维护
devicemapper direct-lvm 不维护
overlay xfs、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

猜你喜欢

转载自blog.csdn.net/qq_50573146/article/details/128882294