两个小技巧,将Docker构建时间减少 40%
2023-11-13 08:1:40 Author: Docker中文社区(查看原文) 阅读量:22 收藏

正如许多公司一样,我们为产品中的所有组件构建 Docker 镜像。随着时间的推移,一些镜像变得越来越大,持续集成(CI)构建时间也越来越长。我的目标是让 CI 构建不超过 5 分钟。这个想法来自于这是一个理想的咖啡休息时间。当构建时间超过这个时限时,它会降低开发人员的工作效率。

造成生产力下降的原因有两个:

  • 开发人员需要等待构建完成,从而浪费时间;
  • 开发人员开始新任务后再回来,这需要更多的上下文切换,通常也会导致效率低下。

在这篇文章中,我想展示我们实施的两个小改变,这些改变显著压缩了我们的构建时间。在关注这些改进之前,请确保你已经遵循了编写 Dockerfile 的最佳实践,如:

  • 最小化层数
  • 使用多阶段构建
  • 使用最小化基础镜像
  • ……

#1

Buildkit 与 Buildx

让我们首先解释一下 Buildkit 和 Buildx,这两个术语经常被混用,但它们并不相同。在写这篇文章之前,我也没有完全理解它们之间的区别。

Builkit

Buildkit 是用来替代传统 Docker 构建器的改进后端。它自 2018 年起就被打包在 Docker 中,并从 Docker Engine 23.0 版开始成为默认构建器。

它提供了许多有趣的特性:

  • 改进的缓存功能
  • 并行构建不同的层
  • 延迟加载基础镜像(从 Buildkit 0.9 版开始)

当使用 Buildkit 时,你会注意到 docker build 命令的输出看起来更清晰、更结构化。

对于 Docker 版本低于 23.0 的情况,使用 Buildkit 的典型方法是设置 Buildkit 参数,如下:

DOCKER_BUILDKIT=1 docker build --platform linux/amd64 . -t someImage:someVersion
DOCKER_BUILDKIT=1 docker push someImage:someVersion

Buildx

Buildx 是 Docker 的一个插件,它使你能够充分利用 Buildkit 在 Docker 中的潜力。它之所以被创造,是因为 Buildkit 支持许多新的配置选项,这些选项不能都以向后兼容的方式集成到 docker build 命令中。

除了构建镜像,Buildx 还支持管理多个构建器。这在 CI 中非常有用,可以定义具有不同配置的作用域环境,因为它们不会修改共享的 Docker daemon。

你可以按照以下步骤开始使用 Buildx:

docker buildx create --bootstrap --name builder
docker buildx use builder

#2

利用远程缓存
加速构建的第一种方法是在远程仓库中缓存你的镜像。这样即使在不同的机器上执行构建,你也可以利用构建缓存,这通常是在 CI 中的情况。作为一种解决方法,许多人在构建新版本镜像之前先拉取镜像的最新版本。好处是你可以缓存未更改的层,但代价是最初需要拉取整个镜像。拉取整个镜像可能需要一段时间,但也不能保证层可以被重用。为了说明这一点,我们使用了以下命令:

docker pull someImage:latest || true
docker build --platform linux/amd64 . \
-t someImage:someVersion \
-f Dockerfile \
--cache-from someImage:latest

使用 Buildx,你可以将缓存信息存储在远程位置(例如 container registry、Blob 存储等)。构建器会检查给定层是否已存在,如果是,它将重用它而不是重新创建。这甚至可以在不拉取该层到本地的情况下完成。为了利用这种机制,我们重新编写了前面的命令:

docker buildx build --platform linux/amd64 . \
-t someImage:someVersion - push \
--cache-to type=registry,ref=someCachedImage:someVersion,mode=max
--cache-from type=registry,ref=someCachedImage:someVersion

“max” 模式意味着我们将存储每个层的构建信息,即使是在最终镜像中未使用的层(例如在使用多阶段构建时)。默认情况下使用 “min” 模式,它只存储最终镜像中存在的层的构建信息。
缓存的一个特殊情况是将缓存数据 “内联” 存储,这意味着它将与镜像一起缓存。当使用 Buildkit 而不是 Buildx 时,也支持这个选项。这是最容易开始的方式,但在使用多阶段构建时更加棘手,它也不提供 artifact 和缓存之间的明确区分。将缓存数据 “内联” 存储的命令如下:

docker buildx build - platform linux/amd64 . \
-t someImage:someVersion --push \
--cache-to type=inline,mode=max \
--cache-from someImage:somePreviousVersion

#3
Docker 镜像中添加文件的新方式

Docker 引入了它的 dockerfile 写作语法的新版本:#syntax=docker/dockerfile:1.4它为 COPY 和 ADD 命令支持了额外的链接选项。

以前,当你使用 COPY 或 ADD 命令时,构建器会创建一个新的快照,它将新文件与已经存在的文件系统合并。这导致父层都需要在此操作之前存在,否则目标目录可能尚不存在。最终,你的镜像(构建命令的结果)将由每层的 tarball 组成,这些 tarball 包含相应快照之间的差异。

FROM baseImage:version
COPY binary /opt/

使用链接选项时,新文件被放在自己的快照中,而不依赖于之前的层。链接的文件存储在它们自己的 tarball 中,并且不同的 tarball 被链接在一起,不依赖于已存在的文件系统。

使用新语法的示例:

# syntax=docker/dockerfile:1.4
FROM baseImage:version
COPY [--chown=<user>:<group>] [--chmod=<perms>] --link binary /opt/

其主要优势是文件不再依赖于先前的层。只要文件没有改变,即使父层改变了,该层也可以被重用。此外,这也可以提高构建速度,因为现在可以并行执行多个复制数据的层。

#4

结论

这篇博客文章描述了我们在优化 CI pipeline 后获得的一些新见解。我讨论了两个小改动,使我们整体 Docker 构建时间减少了 40%:

  1. 在远端存储构建缓存信息。
  2. 在向 Docker 镜像添加、复制文件时使用链接选项。

推荐阅读 点击标题可跳转

《Docker是什么?》

《Kubernetes是什么?》

《Kubernetes和Docker到底有啥关系?》

《教你如何快捷的查询选择网络仓库镜像tag》

《Docker镜像进阶:了解其背后的技术原理》

《教你如何修改运行中的容器端口映射》

《k8s学习笔记:介绍&上手》

《k8s学习笔记:缩扩容&更新》

《Docker 基础用法和命令帮助》

《在K8S上搭建Redis集群》

《灰度部署、滚动部署、蓝绿部署》

《PM2实践指南》

《Docker垃圾清理》

《Kubernetes(k8s)底层网络原理刨析》

《容器环境下Node.js的内存管理》

《MySQL 快速创建千万级测试数据》

《Linux 与 Unix 到底有什么不同?》

《浅谈几种常见 RAID 的异同》

《Git 笔记-程序员都要掌握的 Git》

《老司机必须懂的MySQL规范》

《Docker中Image、Container与Volume的迁移》

《漫画|如何用Kubernetes搞定CICD》

《写给前端的Docker实战教程》

《Linux 操作系统知识地图2.0,我看行》

《16个概念带你入门 Kubernetes》

《程序员因接外包坐牢456天,长文叙述心酸真实经历》

《IT 行业老鸟,有话对你说》

《HTTPS 为什么是安全的?说一下他的底层实现原理?


免责声明:本文内容来源于网络,所载内容仅供参考。转载仅为学习和交流之目的,如无意中侵犯您的合法权益,请及时联系Docker中文社区!



文章来源: http://mp.weixin.qq.com/s?__biz=MzI1NzI5NDM4Mw==&mid=2247496221&idx=1&sn=713cc25e3c724f76808287263a7f581d&chksm=ea1b1b5ddd6c924b1b1252f4446012eb474c47a9d924c7782455d29145c0abd469a6bb266727&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh