一个 MonoRepo 复杂系统的部署问题

4 小时 29 分钟前
 xhawk

我搞了一套财税系统,采用的是 MonoRepo 的构建方式。也就是说,在 APPs 目录下有几十个应用。前端全部用的是 Next.js ,后端用的是 FastAPI 。现在的问题是,代码托管在 GitHub ,镜像则通过阿里云的 ACR 来构建。

核心问题是构建速度非常慢。目前有 11 个应用,其中 4-5 个是前端,6-7 个是后端服务, 后续还在增长。问题在于,有时候我可能只修改了其中一个应用,但很难控制每次只自动构建被修改的那一个。

目前我在 GitHub 上构建时,只有一个分支,也就是 main 分支。如果单独构建每个 APP ,通常只需要 1 到 2 分钟。但一旦有十几个 APP ,每次都要全部构建一遍,速度就会越来越慢,而且我的 APP 数量还在不断增加。我在想,有没有办法在保持 GitHub 上只有一个分支的情况下,也能获得比较快速的构建体验?不知道大家在这方面有没有相关的实践经验?

512 次点击
所在节点    程序员
20 条回复
lujiaosama
4 小时 22 分钟前
我选择不用 MonoRepo 。
xhawk
4 小时 19 分钟前
@lujiaosama 那有啥建议的不, 如果全部分开代码管理, 也很复杂。 因为涉及到系统间的各种调用。
XTTX
3 小时 58 分钟前
vercel 可以选忽略构建步骤, 阿里的你应该问问 AI. 应该是很容易解决的问题
fgwmlhdkkkw
3 小时 54 分钟前
写一个简单的解析 commit msg 的脚本,然后按照约定写 commit msg 。
milkleeeeee
3 小时 53 分钟前
netnr
3 小时 50 分钟前
按 yml 变动构建(增加环境变量 改注释);每个配置里面的矩阵是并行构建
is
3 小时 44 分钟前
建个 ci 的项目把规则写进去,不懂的地方让 agent 帮忙就好了。项目依赖图什么的都好做,可能不太确认的就是 cache 机制。commit 消息也可以做个规范,方便规则匹配
Reficul
3 小时 6 分钟前
上增量编译,bazel 之类的。
xhawk
3 小时 0 分钟前
这个 cursor ai 给的建议, 虽然不是很满意, 但是似乎也只能这么干, 要么就是上面有人建议的采用付费的方式。
ai 的建议, 大概意思意思 acr 构建镜像采用过滤的方式, 代码改了啥, 就构建啥就好了。

**结论先说:仓库不要拆。慢的不是 Monorepo ,是「 main 一推就串行重打全部镜像」。**

源码继续单仓单分支是对的。行业里 Google / Meta / Shopify 、以及 Nx / Turborepo 的做法都是:**代码合在一起,构建按依赖图只打受影响的那几个。** App 越多,越要走这条路,而不是拆仓。


**推荐方向(这就是业内标准解):Affected Build + 远程缓存 + 只发变更。**

1. **关掉 ACR 当 CD 引擎。** 个人版不适合 10+ 服务。构建改到你们已有的 self-hosted Actions (可并行),结果推杭州 ACR ; Dokploy 只 Redeploy 本次变更的服务。
2. **按依赖图决定打谁,不要按「整个仓库变了」。** 例如改 `apps/fis-open-api` 只打 open-api ;改 `apps/fis-api` 打 fis-api **以及**依赖它的 open-api ;改 `packages/*` 只打引用它的前端。用 `paths-filter` 或一份很小的 affected 映射就够,不必上 Bazel 。
3. **缓存比拆 context 更值钱。** Dockerfile 已经分层了(先 requirements / package.json )。接上 BuildKit registry cache 后,没改依赖的镜像应是秒级命中。根 context 可以留——你们前端是 pnpm workspace ,强行 per-app context 收益不大。
4. **中期把「构建期互相 COPY 」收掉。** open-api 不要整份拷贝 fis-api/agents ,改运行时 HTTP / 抽出 `packages` 共享库; fis-web 的共享 UI 进 `packages/fis-shared`。耦合一收,affected 扇出就小,速度才会随 App 增加而近似持平。
5. **发版钉 digest ,不要 8 个 `*-latest` 整波拉。** 未重建的服务保持旧 digest ,避免「构建快了、发布又全量」。
rocmax
2 小时 45 分钟前
建议是每次都全部构建,使用 cache 来解决重复构建耗时的问题。monorepo 强制同步整个项目的版本,防止依赖版本错位,如果选择性构建一部分就回到老路上了。

我们用 turborepo 管理项目,我的经验是:

如果构建产物是 docker 镜像的话使用 layer cache 可以解决大部分问题。需要注意的是构建之前需要用 turbo prune xxx --docker 来提取构建 context 以免混入其它的修改。

如果想直接缓存 nextjs 的构建产物,turborepo 官方有付费构建缓存服务。
也有开源版本比如
https://github.com/ducktors/turborepo-remote-cache
需要自己借助 s3 等来搭建。

另外的一点是在部署 k8s 时全部构建的话产物有共同的 tag ,helm chart 中可以将它统一填到所有子项目中。如果部分购进的话又得管理版本依赖。
members
2 小时 40 分钟前
问题在于,有时候我可能只修改了其中一个应用,但很难控制每次只自动构建被修改的那一个。
---
难在哪里?
我们的方案是构建过程中运行一个脚本,git diff merge-base 有哪些文件改动,构建对应的服务。

有一说一 monorepo 是真的恶心。
perfectlife
2 小时 35 分钟前
这个我有经验,我是根据代码变更判断触发构建 monorepo 仓库下的那个项目,有一说一前端的 monorepo 仓库做 cicd 有点恶心,cicd 要求是项目最好是要标准化,monorepo 仓库感觉是恰恰相反
xhawk
2 小时 35 分钟前
@members 这是一个很核心的问题。虽然我改了一个应用,但在自动构建时,不一定只构建这一个应用,因为应用之间是存在依赖关系的。AI 给我的建议与你的思路类似,就是通过 GitHub 的 Actions 去计算这次应该构建哪个服务。

monorepo 在代码管理以及多项目协同与调度方面,我觉得它的表现相当优秀。但构建速度慢的问题确实让人头疼,尤其是 Next.js 的慢,尤其令人烦躁。
rocmax
2 小时 33 分钟前
@members 如果子项目各自独立的话没有问题,但是这样就不用 monorepo 了。子项目之间有依赖关系的话不分析依赖图就不知道该构建谁。
xhawk
2 小时 21 分钟前
@rocmax 对, 感谢, 也就是这个问题的核心关键。 当然, 我也只是抛出问题, 希望能获得其他的灵感和做法。 若有的话, 欢迎给建议,我来做实验, 同时, 我也会把我最后的做法分享出来。
members
38 分钟前
@rocmax #14
我认为子项目之间不应该有代码和部署上的依赖,如果是这样就不应该拆成独立项目。

但 monorepo 内会存在共同依赖的基础包,这是合理的。我们是配置了一个依赖关系,依赖包有改动会重新构建所有依赖了这个包的项目。

@xhawk #13
chongzi
30 分钟前
应用之间不应该存在依赖关系吧?
johnhom
22 分钟前
可以尝试用一些 monorepo 的管理工具,比如说 nx ,turborepo ,这些都会自动计算某个版本内被 effect 到的包,还会自动计算依赖路径,能决定先构建哪个后构建哪个。这个应该才是最优解
zengxs
17 分钟前
monorepo 是相关的模块才放一起啊
如果 repo 内都是相关模块,每次都重新构建很合理啊

而且你是 nodejs + python ,这 ci build 应该也不会特别慢吧

> 单独构建每个 APP ,通常只需要 1 到 2 分钟

那十几个项目也就 20 分钟左右吧

好多 c++ 项目跑一次 build 还要几个小时呢,我觉得挺正常的
zengxs
15 分钟前
@zengxs #19 如果不同 app 构建没有依赖关系,可以用 github action matrix 并发构建
最后用一个 merge job 合并为最终 image 产物就行了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1239730

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX