优化 Docker-in-Docker 内部的 `docker build` 执行时间

106次阅读
没有评论

问题描述

在 Jenkins 中,我们有一些“发布作业”会构建并推送应用程序的 Docker 镜像到 Docker 注册表,更新各种文件中的项目版本,最后将发布标签推送到相应的 Git 存储库。整个过程都在一个隔离的 Docker-in-Docker(DinD)容器内执行,这意味着每次执行这些 Jenkins 作业时,Docker 缓存都是空白的。

简要来说:Jenkins 实例 –> 启动 DinD 容器 –> 在 DinD 容器内克隆 Git 存储库 –> 构建包括应用程序实际构建过程的 Dockerfile 中的多个层 –> 推送 Docker 镜像到注册表 –> 推送发布标签到 Git。

问题在于,虽然这种隔离一方面有助于避免一些问题,但另一方面使整个 docker build 进程变得特别慢。Docker 拉取和推送过程肯定会对延迟造成一些影响,但这是一个网络速度问题,我们目前无法解决。

然而,导致这种延迟的另一个原因是,由于实际应用程序(Maven 或 Angular)是在一个“干净”的 Docker 容器内构建的,每次都会清空 .m2node_modules 目录,因此每次运行时都必须下载/安装所有依赖项。我们可以从 Jenkins 挂载一个 .m2 存储库到 DinD 容器内,但在该 DinD 容器内构建的镜像将无法访问它。

我们尝试过将 .m2node_modules 目录进行打包,通过 Dockerfile 将它们复制到镜像内部,然后解压缩并移动到正确的路径,但这种方法只能节省 1-2 分钟。我们还尝试过使用 buildkit 缓存 Maven 依赖项,例如:https://www.baeldung.com/ops/docker-cache-maven-dependencies#caching-using-buildkit,但显然这不是我们所需的解决方案。

据我所知,不可能在 docker build 时挂载卷,这在我们的“空白缓存”情况下是理想的解决方案。是否有人遇到过类似的问题并找到了解决方法?总之,我们将非常感谢关于如何最小化发布作业执行时间以及优化整个过程的任何建议。提前感谢您的帮助。

解决方案

方案1:使用 Docker 缓存

  1. 了解 Docker 缓存: Docker 在构建过程中使用缓存以加快构建速度。每当构建上下文中的文件发生更改时,Docker 将会从该文件开始的所有步骤的缓存失效。因此,在 Dockerfile 的顶部放置那些不太可能更改的步骤,可以最大程度地利用缓存。

  2. 多阶段构建: 使用 Docker 多阶段构建可以减小最终镜像的大小并提高构建速度。您可以在一个阶段中构建依赖项,并在另一个阶段中复制这些依赖项,以避免将所有构建工件放入最终镜像中。

  3. 构建缓存层: 将频繁更改的步骤移到 Dockerfile 的后面,这样更稳定的步骤就可以重复使用缓存,从而减少构建时间。

方案2:本地依赖缓存

  1. 预先下载依赖项: 在 DinD 容器内部,首次构建前手动运行一次构建命令,以下载并缓存所有依赖项。

  2. 使用本地缓存目录: 将下载的依赖项放在一个本地目录中,并在 Dockerfile 中通过 COPY 命令将它们复制到合适的位置。这样,当您重新构建镜像时,依赖项将不再需要从远程源下载。

方案3:使用外部构建缓存

  1. 使用外部构建缓存工具: 有一些工具可以帮助您管理 Docker 构建过程中的依赖项。例如,BuildKit 允许您将构建上下文缓存在本地,并与其他构建共享,以便加速构建。

方案4:优化镜像层次

  1. 优化层次结构: 确保您的镜像层次结构合理,避免不必要的层次,从而减少镜像的大小和构建时间。

  2. 最小化层次更改: 尽量不要在构建过程中频繁更改镜像层次结构,这可以减少缓存失效的次数。

结论

通过采取适当的措施,您可以在 Docker-in-Docker 环境中优化 docker build 的执行时间。通过合理使用 Docker 缓存,多阶段构建,本地依赖缓存和外部构建缓存工具,您可以显著减少构建时间,提高效率。

请根据您的具体情况选择最适合您的优化策略,并根据需要进行实验和调整。希望这些建议对您的

正文完