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

xhawk 2026-09-05 21:29 1

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


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


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

最新回复 (24)
  • lujiaosama 09-05 21:37
    1
    我选择不用 MonoRepo 。
  • xhawk 楼主 09-05 21:40
    2
    @lujiaosama 那有啥建议的不, 如果全部分开代码管理, 也很复杂。 因为涉及到系统间的各种调用。
  • XTTX 09-05 22:01
    3
    vercel 可以选忽略构建步骤, 阿里的你应该问问 AI. 应该是很容易解决的问题
  • fgwmlhdkkkw 09-05 22:05
    4
    写一个简单的解析 commit msg 的脚本,然后按照约定写 commit msg 。
  • milkleeeeee 09-05 22:05
    5
    用 https://nx.dev/
  • netnr 09-05 22:08
    6
    按 yml 变动构建(增加环境变量 改注释);每个配置里面的矩阵是并行构建
  • is 09-05 22:14
    7
    建个 ci 的项目把规则写进去,不懂的地方让 agent 帮忙就好了。项目依赖图什么的都好做,可能不太确认的就是 cache 机制。commit 消息也可以做个规范,方便规则匹配
  • Reficul 09-05 22:52
    8
    上增量编译,bazel 之类的。
  • xhawk 楼主 09-05 22:58
    9
    这个 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 09-05 23:13
    10
    建议是每次都全部构建,使用 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 09-05 23:19
    11
    问题在于,有时候我可能只修改了其中一个应用,但很难控制每次只自动构建被修改的那一个。
    ---
    难在哪里?
    我们的方案是构建过程中运行一个脚本,git diff merge-base 有哪些文件改动,构建对应的服务。

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

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

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

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

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

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

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

    好多 c++ 项目跑一次 build 还要几个小时呢,我觉得挺正常的
  • zengxs 09-06 01:43
    20
    @zengxs #19 如果不同 app 构建没有依赖关系,可以用 github action matrix 并发构建
    最后用一个 merge job 合并为最终 image 产物就行了
  • xiaket 09-06 15:28
    21
    bazel 向你招手
  • SanjinGG 09-06 20:33
    22
    每个项目一个 workflow ,只监听相关项目的目录和文件,每个项目独立的 Dockerfile ,然后构建步骤指定目录的 Dockerfile 即可。我用的是 github action ,其他的话要看下支不支持
  • hxndg 09-06 21:26
    23
    @bazel,

    monorepo 只是个外表,重点是内里。讨厌 monorepo 实际上得说讨厌内里的什么。敏捷开发还是好词呢,不一样有一群拿敏捷开发当幌子的,噶
  • xhawk 楼主 09-06 21:49
    24
    @xiaket 的确是, 我仔细去分析了一下, 这个的确才是正解的好的解决方法。

    然后, 我也分享一下我最后自己的实践。 因为我的目的是让阿里云的 acr 来构建镜像(这个里头有网络传输的因素)

    所以, 我的路径是这样子的:

    1. 本地开发之后:apps 下有 10 个 app, 写个脚本, 针对本次的修改, 分析出是哪几个 app 需要构建镜像,打 tag
    2. 然后代码 commit , 推送到 github 的时候, 就有 commit 同时也有需要的 tag
    3. 阿里云 acr 基于 tag 来自动构建。 (因为用的阿里云的个人版, 不支持正则打 tag, 这个让我测试了一下午,吐血)

    这个是大致的做法。 感谢各位参与发言的小伙伴, 感谢。
* 帖子来源V2EX
返回