出处:https://careersatdoordash.com/blog/distributed-build-service-for-monorepos/
在版本控制系统的世界中,单代码库(monorepo)将多个软件开发项目的代码集中存储,以促进代码共享、工具化和版本管理。相较于另一种策略——为系统每个部分建立独立仓库的微仓库(microrepo)模式,单代码库具有显著优势,这正是DoorDash从微仓库向单代码库架构转型的原因。
尽管单代码库能简化依赖管理、支持跨项目原子变更、简化重构流程,同时促进构建过程中的代码复用与一致性,但它也面临诸多挑战。可扩展性问题、更长的构建时间以及复杂的访问控制都是需要攻克的难题。本文将深入解析我们自研的构建服务如何应对这些挑战,在作为持续集成(CI)管道核心的同时,确保单代码库变更的快速可靠。
挑战
在构建当前系统时,DoorDash后端主要使用Kotlin语言,管理着200多个微仓库。其中部分大型仓库采用Gradle多项目构建并包含集成测试,导致CI时间效率低下。此外,大型仓库在集成开发环境中的加载也极为耗时。这些痛点最终推动了DoorDash将Kotlin服务转向单代码库范式。随着单代码库中项目数量的增长,构建时间同步攀升,构建可扩展且高效的构建服务成为迫切需求。更多背景信息可参考我们在2023年开发者生产力工程峰会的演讲。
DoorDash采用gRPC协议和protobuf协议作为微服务通信标准,其中protobuf单代码库已运行五年,最初支撑了单体架构向微服务的转型。该架构对流水线的可测试性、包级别和目标语言级别的并行处理能力提出了严格要求。
构建工具选型
当前DoorDash的Kotlin单代码库包含330多个Gradle项目,protobuf单代码库涵盖500多个领域。复杂的构建图和变更引发的构建量增长,要求我们必须缩短整体构建时间并保持准确性。关键需求包括:构建规避、最小化重建、并行化、可扩展性、容错能力和清晰的故障报告。
尽管多个构建工具都能处理大型仓库,我们聚焦以下三种:
-
Bazel:高性能、多语言支持,但迁移成本高且Kotlin开发体验欠佳
-
Buck:与Bazel类似,但面临相同迁移难题
-
Gradle Enterprise:提供深度构建分析,但需定制分布式构建
考虑到迁移Bazel/Buck的学习曲线陡峭、Gradle插件生态难以替代,我们最终选择基于开源Gradle扩展开发自研构建服务。该服务实现全量构建25分钟内完成,P95构建时间低于10分钟。
分布式构建服务架构
系统采用三层架构:
-
Web层:处理构建请求入口与状态查询
-
控制器:协调构建任务分发,管理构建队列
-
工作节点:执行实际构建与测试任务
(图1:分布式构建服务架构)
通过依赖关系图分析变更影响范围(如图2示例),仅构建受影响项目。采用Redis缓存构建状态,通过心跳机制实现故障容错。资源令牌机制智能分配内存资源,结合二维弹性伸缩策略(时段调度+队列驱动)优化资源利用率。
Kotlin单代码库构建
采用组合构建架构,各Gradle项目相互独立又通过settings.gradle声明依赖。通过隔离Gradle守护进程与用户目录,支持x86/ARM多架构并行构建。针对需要Docker-in-Docker(DinD)的测试场景,强制使用随机端口避免冲突。测试分片机制将单元测试拆分为并行执行的子任务,显著缩短测试时间。
Protobuf单代码库构建
针对多语言编译需求,将每个领域按目标语言拆分为独立构建任务(如图6)。
(图 6:Protobuf 并行构建)
支持CI/CD双模式发布:
-
CI模式发布快照版本至测试仓库
-
CD模式跳过验证步骤发布正式版本
自动代码生成与格式化通过Git补丁文件整合提交,避免并行任务冲突(如图7流程)。构建服务可处理40+并发请求,峰值时段完成400+独立构建。
(图7:Protobuf PR更新)
关键成效
-
Kotlin构建速度提升65%,支持项目数增长290%
-
Protobuf平均构建时间从16分钟降至4分钟
-
支持40+并发构建请求,峰值时段处理400+构建任务
展望
自研构建服务的分布式架构为DoorDash提供了高度灵活的构建解决方案,有效支撑了CI/CD管道的效率提升。随着业务发展,我们将持续迭代优化,以应对未来更复杂的构建需求。该系统的成功实践,为大规模单代码库管理提供了有价值的参考范式。
发表评论 取消回复