最近云原生构建团队开始进行一个新项目,需要基于 Google 的 Android 14 系统源码来定制 ROM。团队需要下载 AOSP(Android Open Source Project)的代码进行开发。AOSP 是一个提供了支持移动设备和嵌入式设备开发的完整技术栈,可以用来开发智能手机、平板电脑、车载系统、智能电视等各种设备。AOSP 项目源码规模很大,占用磁盘 124.44GB,超过 1400 仓库。 这样的代码规模背后面临拉取耗时长、存储空间占用大、效率低、并发构建受限等问题,于是,我们关注到了这款最新发布的 CI 神器。
关注腾讯云开发者,一手技术干货提前解锁👇
“大佬,可以打一个有最新代码的 ROM 么? 我测试一下新功能” “可以,我去操作一下, 大概 1 个半小时后你再来拿” “1个半小时这么久?” “对啊, 拉代码就得 20 分钟, 还得跑测试和编译….”
团队在开发中,经常会出现类似的对话,我们发现团队在开发这种超大型仓库时,面临着多方面的问题和困难:
为了加速团队的开发速度,解决流水线每次需要全量 clone 代码的耗时问题,团队还购置了一台本地的高性能机器,专门服务于代码编译。每次有开发同学需要编译的时候,就去该机器上增量拉代码,然后编译,这样可以尽可能使用机器上的代码缓存,然而,这种方法仍存在局限性:
面对这些持续存在的挑战,我们意识到需要从根本上解决代码clone的速度问题。这不仅关系到开发效率,还直接影响着我们的 CI/CD 流程和资源利用。因此,我们开始探索各种可能的优化方案,希望能找到一个既能显著提升 clone 速度,又能满足我们复杂开发环境需求的解决方案。
我们首先研究了几种常见的代码 clone 优化技术:
这些方法虽然在代码速度或者存储空间方面有所改善,但都无法全面解决我们面临的问题。特别是在处理超大型仓库时,这些方法往往显得力不从心。
主要影响我们开发的核心 2 大矛盾点:
我开始关注腾讯云新发布的 CI 工具:CNB
Cloud Native Build 云原生构建 -- https://cnb.cool
CNB 基于 Docker 生态,对环境、缓存、插件进行抽象,通过声明式的语法,帮助开发者以更酷的方式构建软件。
在这里可以托管代码和制品、项目开源以及参与社区协作,通过云原生构建可以更快的构建软件,使用云原生开发,告别复杂的本地开发环境,一键唤起云上工作空间。
其中他有一个非常重要的特性:“读秒克隆” , 就是可以在数秒内完成代码准备,无视仓库的大小,并且是并发安全的,高并发场景下也是如此。这个特性对于我们团队非常重要,如果真的可以秒级 clone 准备好代码,仅仅在代码 clone 上就可以给我们团队代码大约 200 倍的速度提升。我们马上开始用 125GB 的 AOSP 代码仓库进行测试。
迁移到 CNB 也很简单:
build_config: &aosp_build_config
runner:
cpus: 64
docker:
build: .ide/Dockerfile
volumes:
- out:copy-on-write
stages:
- name: build
env:
BUILD_HOSTNAME: cnb-build
shell: |
source build/envsetup.sh
lunch aosp_arm-eng
make -j64
script: bash -e -c "${shell}"
# 测试并发 6 流水线测试
main:
push:
- *aosp_build_config
- *aosp_build_config
- *aosp_build_config
- *aosp_build_config
- *aosp_build_config
- *aosp_build_config
测试数据:
代码仓库地址:https://cnb.cool/aosp/monorepo
1、首次启动,无缓存拉取代码,直接构建
2、第二次以后,带有代码缓存,并且并发构建
真的 秒级 clone , 3.8s !!!124.44GB !!!
且并发构建依然可以命中缓存!
下面截图就展示了并发 6 条流水线时的真实耗时:
性能对比
代码准备 | 编译 | |
---|---|---|
未使用 CNB,无缓存加速 | 16m52s | 46m4s |
使用 CNB,缓存加速 | 3.8s | 1m30s |
使用 CNB 后,团队在代码克隆方面的需求得到了显著改善,传统的浅克隆和部分克隆等优化措施变得不再必要,因为 CNB 提供了快速且全面的代码克隆功能。这一优势使得 CNB 在与其他 CI 系统的比较中脱颖而出,有效解决了团队在处理超大型仓库时所面临的代码克隆和构建缓存问题。
为什么 CNB 可以做到秒级克隆这么快?
CNB 使用 git-clone-yyds 插件实现的秒级克隆, 出于好奇,我阅读了代码,以了解其内部实现的机制。
代码开源地址:
https://cnb.cool/cnb/cool/git-clone-yyds
1、缓存机制加速代码拉取
CNB 上代码首次被 clone 下来以后,会被持久化缓存到构建机的母机上。往后启动流水线只需要增量更新代码,这个过程比全量 clone 代码要快得多,可以在数秒内完成( 如 AOSP 125GB -> 3 秒完成)。
代码缓存能显著减少启动流水线时准备代码所需的时间。然而,如果仅仅实施代码缓存,其本质与在开发机上拉取代码并构建相似,存在明显缺陷。当团队中多人同时开发,需要并发启动多条流水线时(如同时进行端到端测试(e2e)、单元测试、构建多个包等),这些并发构建操作会同时修改工作空间(workspace)中的多个文件。这种情况下,被缓存的源代码可能被污染,从而影响流水线的幂等性,导致流水线失败,或者产生不可预测的结果,增加了维护成本,并且还有可能会出现 cache miss 的情况,导致不得不重新全量 clone 代码。
对于上面这种情况,大部分的 CI 系统会采用集中解决方案,如:
那么,CNB 是如何做到在高并发中使用代码缓存的呢?
2、Copy-on-Write 机制
从计算机角度来看,git 代码克隆缓存和构建缓存等操作本质上是典型的文件独占问题。传统上,这些文件在同一时间只能服务于一次构建,限制了并发性能。为了解决这个问题,CNB 采用了 Copy-on-Write (简称 CoW)机制。
Copy-on-Write(CoW)是一种优化策略,允许多个进程共享同一份资源,直到需要修改时才创建副本。这种机制大家并不陌生,在 Docker 上就有使用。Docker 利用 OverlayFS(一种联合文件系统)来实现 CoW。OverlayFS 能够将多个目录层叠在一起,形成统一视图,这使得多个容器可以共享同一个基础镜像,只有在需要修改时才在特定容器中创建独立的可写层。这种技术不仅优化了存储使用,也是 Docker 容器瞬间启动的原因。
下图简单描述了 OverlayFS 的运作原理,其中:
通过结合 CoW,CNB 能够有效地解决文件独占问题,允许多个并发构建共享相同的基础文件系统,同时保持各自的独立性。这不仅提高了系统的并发性能,还确保了每个构建环境的隔离性和一致性。
这就是 CNB 同时解决代码克隆速度慢和高并发下缓存复用冲突的关键所在。
3、git-clone-yyds 插件秒级 clone 原理
3.1、git-clone-yyds 工作流程图
git-clone-yyds 本质上是运行在母机上的一个 docker 容器,他通过 volume 把代码缓存挂载到工作区 (workspace)下。下图展示了 CNB 的 git-clone-yyds 在准备工作区的流程。
3.2、从文件系统挂载上观察 git-clone-yyds
从文件系统挂载上看,可以更清晰的看到 CNB 是如何使用 git-clone-yyds 实现秒级的代码 clone 的。
当母机上启动构建容器时,会将 /data/git/{group}~{repo}/cache 作为只读的 lowerdir 层,通过 OverlayFS 挂载到容器的/workspace目录下。这个 /workspace 目录具有 Copy-on-Write 特性,任何对文件的修改都只会影响容器自己的 upperdir,不会改变底层 lowerdir 的代码缓存。
这样就实现了代码的秒级拉取,同时保证了并发场景下的代码隔离。
3.3 并发构建场景下的缓存
在同一个代码仓库并发多个流水线构建时,母机上只会存在一份代码缓存,每个流水线会通过自己的 git-clone-yyds 准备 workspace 代码,通过 OverlayFS 建立 CoW 文件夹等。同一个代码仓库不同的流水线的 workspace 是通过 pipeline id 文件夹实现隔离的,但是 workspace 底层的 lowerdir 是同一个。这样就保证了在并发启动多条流水线的时候,可以并发使用缓存。母机上只需要一份缓存代码,并且最重要的首次准备好代码缓存以后,可以在数秒内完成代码准备。高并发场景下,也是如此。
总的来说, git-clone-yyds 的性能非常高:
从 CNB 系统全局性能监控来看,git-clone-yyds 非常快, git clone 的时间稳定在 10s 以下,大部分项目都在 3~6s 时间准备好工作区,速度非常快!
当然这里只是 CNB 对克隆时间的加速,如何将 AOSP 的编译时间从46分钟显著缩短至仅1分钟?后面我们再深入探讨 CNB 如何通过使用 CoW 和 volume 实现编译缓存的具体实现。
CNB 系统通过创新的 git-clone-yyds 插件和 Copy-on-Write (CoW) 机制,不仅解决了大型代码仓库的克隆速度和并发构建问题,还为我们开启了更多可能性。
-End-
原创作者|黎志航