字节研发设施下的 Git 工作流到底是怎么做的?
共 5831字,需浏览 12分钟
·
2021-05-12 22:16
Git 提供了丰富的分支策略和工作流方式,我们在深入学习业界 Git 工作流时,每种工作流都设计的非常好,似乎都能运用到团队实践。但在引入 Git 工作流规范开发时要留意:Git 工作流仅仅是整个研发流程中的一环。上游项目管理/缺陷追踪系统虎视眈眈,下游 CD (Continuous Delivery) 嗷嗷待哺,还得考虑团队规模、产品形态、发版方式等等因素。因此,在团队中落地 Git 工作流规范并不是一件能轻松决定的事。
字节跳动 Git 仓库有效的 CR (Code Review) 覆盖率 70%,仍有提升空间,通过调研,团队中又以 GitHub Flow 模式居多。随着字节研发效能建设愈发完善,GitHub Flow 已无法充分利用研发设施进行提效并保障工程质量,很多团队均意识到这点并着手建设流程规范。
本文通过介绍业界 Git 工作流和公司研发设施现状,力求从仓库形态、部署流程等多角度进行分析,给出一些制定工作流规范的建议。
业界 Git 工作流介绍
Git Flow
初级 Git 开发者,面对这满图的分支和 merge 指向,简直想手撕作者。高级 Git 开发者要将这个流程运用实践也大感头疼。
Git Flow 有不少优点:
• 分支各司其职,覆盖大部分开发场景。
• 预期 master 分支中任何 commit 都是可部署的。
• 严格按照流程执行,出现重大事故的情形会大大降低。
缺点也不少:
• 过于繁琐,无法要求所有团队成员按照这个流程严格执行。
• 违反 git 提倡的 short-lived 分支原则。
• master 分支历史记录并不干净,只能通过打 Tag 标记哪些是 master 真正要部署的。
• 对持续部署和 monorepo 仓库不友好。
GitHub Flow
GitHub Flow 是一个基于分支的轻量级工作流。它突出了 CR 的重要性,有助于我们掌握 CR 的开发模式,但它没有解答部署、环境、发布、集成等问题。
GitLab Flow
GitLab Flow 并不像 Git Flow, GitHub Flow 一样具有明显的规范,它更多是在 GitHub Flow 基础上,综合考虑环境部署、项目管理等问题而得出的一种实践。
基于环境:
基于发布计划:
Trunk-based Flow
和“基于发布计划”的 GitLab Flow 类似,有一个主干分支接受所有开发者的 commit,并为后续 CI/CD 提供关键助力。
按照官方文档描述:「你可以选择直接向主干分支提交代码的方式(适用于小团队)或者采用 Pull-Request 的方式,只要保证特性分支不能长期存在,并且产品是独立存在的。(the product of a single person.)」,trunk 分支提交是比较随意的(不一定可部署),但也需要走 CR,可以采用 Fast-forward 形式的 merge 保证主干是一条线,到了合适的时间点,checkout release-* 分支,执行正式上线操作。
一旦发现 release 分支有 hotfix 需求,则先在 trunk 分支上进行 fix 开发,测试完成后,cherry-pick 到 release-_ 分支,确保修复代码即在 release-_ 中上线,又能被下一个 release 周期包含。
Aone Flow
按阿里云开发者社区描述:Aone Flow「基础玩法是将每条发布分支与具体的环境相对应,比如 release/test 分支对应部署测试环境,release/prod 分支对应线上正式环境」,这种发布方式可保证每个 feature 都被测试,但不能保证 release/test CI 通过的 feature,能在 release/prod 环境也通过(feature pick 组合不同)。
「进阶点的玩法是将一个发布分支对应多个环境,比如把灰度发布和正式发布串在一起,中间加上人工验收的步骤」。实质是将基础玩法中的“release/test”,“release/prod” 改成 “release/combine-feature”,固定了 feature pick 组合,保证 features 在各个环境测试的一致性。
Aone Flow 的 pick 模式,适合复杂仓库大团队持续上线,避免了 Trunk-based Flow 引入未完成 feature 的问题。但似乎不适合周期发版的要求。一个发版周期内会创建多个 feature ,上一个发版周期可能遗留若干 feature,随着时间推移,feature 数越来越多,最终发版人在 pick feature 过程中疯掉。
公司实践
字节跳动的 Web 服务都跑在私有云 CE (Compute Engine) 中,部署产物则由统一的代码编译发布和版本管理平台分发,每个构建产物都有一个 AR (Artifact Repository) 管理。
多环境部署现状
服务端视角
服务端微服务跑在 CE 上,代码编译由 AR 完成,CE 和 AR 是 1:N 的关系,一个应用的运行依赖多个 AR,在进行环境管理时,需要以 CE 为纬度来区分。从 CE 视角来看,公司有 5 类环境(以国内产品为例):
通过 headers -H 'x-env-tag:{env}'
将流量导向不同环境,满足“开发测试”、“QA 测试”、“预发测试”、“小流量测试”、“全量上线” 各阶段的测试需求。
CE 测试环境服务示例:
前端视角
前端和服务端有差异,一个 URL path 访问的资源通常由一个 AR 产出,URL paths 和 AR 是 N:1 关系,所以前端部署以 AR 版本来区分环境:
前端部署可为测试环境和产品预览环境生成独立的域名进行测试,也可通过设定 headers -H 'x-env-tag:{env}'
进行环境导流。
团队实践的 Git 工作流
结合前后端的环境现状,可整理三类研发流程:
功能测试流程(测试环境) QA 提测流程(测试环境) 上线发布流程(测试、预发、灰度、线上环境)
公司内目前有三种 Git 工作流与之对应:
• 小步快跑:单主干
• 周期发版:双主干
• 周期发版:三主干
对比“双主干”和“单主干”,
有联系:
处于 MR 状态的迭代分支 ≈≈ 研发主干 Dev 单主干 Master ≈≈ 发布主干 Master
也有区别:
单主干的“研发分支”不存在一个固定的测试环境(相较于双主干 dev 分支) 多个 feature 同时发测试环境时需要组合成新分支,管理不便 单主干迭代分支在 MR /非 MR 状态下的 CI 流水线有差异
单主干实践
前端微服务管理平台
字节前端微服务平台属于成熟业务,只需做少量 feature/fix 开发,在工作流上采用单主干模式。
本地测试后,不再经过功能测试环境测试。发起 Merge Request,CR 通过合码后,上测试基准环境进行测试,如发现问题,回滚,进入下一轮 CR。
虽然小修小改影响风险小,但流程缺乏进入功能测试环境的流程,还是存在隐患,有可能影响测试环境的业务方使用,不是很好的实践。
单主干只适应于业务规模小,成熟度高无大改动的项目。但无论业务规模如何,执行上线发布流程前,都必须先经过线下环境验证。
双主干实践
私有云平台
云平台的 Git 工作流是由繁入简的过程。最开始为每个部署环境设定了一个部署分支。feature 分支的 commit 点需要和三个环境的部署分支发生 merge,起不到串联测试的目的。
经过改进后,采用标准的 Trunk-based Flow,仍能满足 online/sandbox/boe 三个环境的部署要求。
云平台参与业务方有上百个(类似阿里云平台),虽然图中仅展示了三个环境,但实际上 5 大环境的使用都融入了日常开发中。
某业务中台
某业务中台的 Git 工作流重点阐述了同一个项目多人协作开发时会遇到的问题:
多个 feature 各自独立提测, 临近上线合码时有较多冲突, 可能导致线上 bug 提测前和提测中, 如果 master 更新了, 可能没有及时同步下来, 上线前合入 master 可能会导致冲突或 bug 在流程设计上,master 作为发布分支,release-* 为提测分支,结合了单主干的便捷(hotfix 直接和 master 交互)和双主干对 feature 的管理 和 Trunk-based Flow 刚好相反,主分支是发布分支,提测分支是短期的 另一个比较有特点的是,在 release 测试过程中,发现某个 feature 的 bug, 直接从 release 分支 checkout 出来进行修复,并再次合入 release
Jupiter 工作流规范
Jupiter 是字节 Web 开发引擎,覆盖 Web、组件库、BFF、SSR 等前端开发领域。Jupiter 推荐 Trunk-based Flow 类似的 Flow,并从 CI/CD 角度出发,在开发新需求、hotfix 等时机给出 Git 操作建议。
有重叠人员参与的各项目之间有一致的流程和模式,避免增加认知负担,避免同一个人在不同项目之间切换时混淆和迷惑,也能集中力量做实践和改进,共享经验和基础建设。 上线节奏要灵活。既照顾早期的项目,也照顾规模化落地的项目,考虑到项目在追求不同里程碑时,上线频率会有变化。所以每周上线一次、每天上线一次(适合人多、稳定性要求高的项目)、一天内分多次上线多个 feature,都有可能,不能限定一个固定的节奏。任何人,在任何时候都可以发起上线。 支持 monorepo。不同方向不同人参与的项目,可能会共用一个仓库,方便复用代码和基础设施。所以仓库中不同项目可能有不一样的上线节奏和上线需求。 鼓励持续集成(CI),集成不等同于部署,发 MR 集成代码的时候不用有压力,不会在不知情的情况下被上线。也鼓励持续部署(CD),部署不等于发布,不能发布的代码,在正式上线前有机会关掉。 上线过程必须是固定、重复、能统一改进,能逐步增加自动化的,不能每次上线时重新、临时规划或修改上线方法,增加负担和成本。 CI 是 CD 的前提,没经过 CI 的修改不能进入 CD 环节。 不能有任何修改不经过 staging(预发布,尽可能跟线上一致)测试,直接上线。 CI 和 CD 的历史记录要绝对可靠、可追溯,只能增加,不能减少和修改。 尽可能减少手动操作环节,避免在特定的个人机器上做上线操作。
三主干实践
亿级 App
App 发版应该是目前为止最为复杂的分支管理场景了。客户端安装包一旦下发到渠道被用户下载,如果无法通过热更新修复,将严重影响 App 用户留存。App 发版具有更规范的发版规律,Feature Toggle 必不可少,当我们觉得 CR,CI/CD 麻烦时,对比开发上线一个影响上亿用户的 App feature,是不是感觉做 Web 的 CI/CD 简单多了?
总结
本文尽可能从多角度阐述 Git 工作流的使用姿势,希望对大家有帮助。Git 工作流是为了上线有保障,上线过程中充分测试必不可少,良好的 Git 工作流能保障测试是渐进且可靠的。环境管理和 Git 工作流结合在头条内部也形成了很多规范,包括「环境部署」、「流量调度」、「连通性测试」等使用规范;「限定场景允许」、「暂时场景允许」、「限定流程允许」等环境约束规范。再结合 CI/CD,我们就可以全链路保障业务的快速迭代、安全上线。
参考资料
Trunk-based Development vs. Git Flow
( https://www.toptal.com/software/trunk-based-development-git-flow )
Please stop recommending Git Flow!
( https://georgestocker.com/2020/03/04/please-stop-recommending-git-flow/ )
Understanding the GitHub flow
( https://guides.github.com/introduction/flow/index.html )
Introduction to GitLab Flow
( https://docs.gitlab.com/ee/topics/gitlab_flow.html )
https://cn.trunkbaseddevelopment.com
在阿里,我们如何管理代码分支?
( https://developer.aliyun.com/article/573549 )
谷歌的代码管理
( http://www.ruanyifeng.com/blog/2016/07/google-monolithic-source-repository.html )