docker - Docker 中关于多阶段的并发构建
问题描述
我有一个包含我所有项目的整体存储库。我当前的设置是启动一个构建容器,安装我的整体存储库,然后按顺序构建我的项目。复制二进制文件,并按顺序构建它们各自的运行时(生产)容器。
我发现这个过程很慢,想提高速度。我想采取的两种主要方法是
在构建容器中,同时构建我的项目二进制文件。而不是顺序。
与第 1 步一样,同时构建我的运行时(生产)容器。
我做了一些研究,似乎有两个我感兴趣的 Docker功能:
多级建筑。这让我不必担心构建容器并将所有内容都放在一个
Dockerfiles
.--parallel
选项docker-compose
,这将解决方法#2,允许我同时构建我的运行时容器。
但是,仍然存在两个主要问题:
如何将这两个功能粘合在一起?
如何在构建 Docker 中同时构建我的二进制文件?换句话说,我怎样才能实现方法#1?
澄清
无论是否使用多阶段,都有两个逻辑阶段。
首先是二进制构建阶段。在此阶段,工件是来自构建容器的已编译可执行文件(二进制文件)。由于我没有使用多阶段构建,因此我将这些二进制文件复制到主机,因此主机用作中间暂存区。目前,二进制文件正在按顺序构建,我想在构建容器中同时构建它们。因此方法#1。
二是形象建设阶段。在此阶段,上一阶段的二进制文件现在存储在主机上,用于构建我的生产映像。我还想同时构建这些图像,因此方法#2。
多阶段允许我消除对中间暂存区(主机)的需要。并--parallel
允许我同时构建生产图像。
我想知道的是如何使用多阶段和--parallel
. 因为对于每个项目,我都可以定义一个单独的多阶段Dockerfiles
并调用--parallel
它们来分别构建它们的图像。这将实现方法#2,但这会为每个项目生成一个单独的构建容器并占用大量资源(我对所有项目都使用相同的构建容器,它是 6 GB)。另一方面,我可以编写一个脚本来在构建容器中同时构建我的项目二进制文件。这将实现方法#1,但是如果我想同时构建生产图像,我就不能使用多阶段。
我真正想要的是Dockerfiles
这样的:
FROM alpine:latest AS builder
RUN concurrent_build.sh binary_a binary_b
FROM builder AS prod_img_a
COPY binary_a .
FROM builder AS prod_img_b
COPY binary_b .
并且能够运行这样的docker-compose
命令(我正在编造这个):
docker-compose --parallel prod_img_a prod_img_b
进一步说明
运行时二进制文件和运行时容器不是独立的东西。我只想能够并行构建二进制文件和生产图像。
--parallel
不使用不同的主机,但我的构建容器很大。如果我使用多阶段构建并在我的本地开发机器上并行运行 15 个这样的构建容器可能会很糟糕。
我也在考虑分别编译二进制和运行时容器,但我没有找到一种简单的方法来做到这一点。我从来没有使用过docker commit
,那会牺牲docker缓存吗?
解决方案
结果
我的 mono-repo 容器有 16 个项目,有些是几 MB 的微服务,有些是大约 300 到 500 MB 的更大服务。
该构建包含两个先决条件的编译,一个是gRPC
,另一个是XDR
。两者都很小,只需 1 或 2 秒即可构建。
该构建包含一个node_modules
安装阶段。NPM 安装和构建是项目的瓶颈,也是迄今为止最慢的。
我使用的策略是将构建分为两个阶段:
第一阶段是启动一个整体构建 docker,将 mono-repo 安装到其上,并
cache
作为绑定卷具有一致性。并使用 Goroutines 在其中并行构建我的容器的所有二进制依赖项。每个 Goroutine 都在调用一个 build.sh bash 脚本来进行构建。生成的二进制文件将写入相同的已安装卷。缓存以安装的 docker 卷的形式使用,并且二进制文件在运行中被保留以尽最大努力。第二阶段是并行构建图像。这是使用此处记录的 docker 的 Go SDK 完成的。这也是使用 Goroutine 并行完成的。除了一些基本的优化之外,这个阶段没有什么特别之处。
我没有关于旧构建系统的性能数据,但是构建所有 16 个项目很容易花费了 30 分钟的上限。这个构建非常基础,没有并行构建图像或使用任何优化。
新版本非常快。如果所有内容都已缓存并且没有任何更改,则构建大约需要 2 分钟。换句话说,启动构建系统、检查缓存和构建相同的缓存 docker 映像的开销大约需要 2 分钟。如果根本没有缓存,则新版本大约需要 5 分钟。对旧版本的巨大改进。
感谢@halfer 的帮助。
推荐阅读
- azure-functions - 使用 ARM 模板将 Web 部署到 Azure Functions 失败并出现 ERROR_INSUFFICIENT_ACCESS_TO_SITE_FOLDER
- kotlin - Jackson Databind 未设置默认值
- google-cloud-storage - 从托管在谷歌存储上的静态网站发送电子邮件(使用谷歌服务)
- javascript - 如何检测视频 Vimeo 何时完成播放反应?
- apache-spark - 为 Apche Spark 2.4.3 ( Pyspark ) 安装 delta Lake 包
- c# - MS Access oledb 连接打开“未指定错误”错误消息
- sql - 输入 id 获取地址。SQL 或 PL/SQL
- azure-devops - 如何查看有关 Azure DevOps 拉取请求的评论历史记录?
- circleci - CircleCI Slack Orb 和状态通知
- scala - 在 spark scala 中将 CDT 时间戳转换为 UTC 格式