微软如何与 DevOps 开发合作

Microsoft 努力使用 一个工程系统,通过以 Git 分支和发布流为中心的坚实 DevOps 流程来生成和部署所有 Microsoft 产品。 本文重点介绍了实际实现、系统如何从小型服务扩展到大规模平台开发需求,以及跨各种Microsoft团队使用系统所吸取的教训。

采用标准化开发过程是一项雄心勃勃的事业。 不同Microsoft组织的要求大相径庭,组织内不同团队的需求规模和复杂性都很大。 为了满足这些不同需求,Microsoft使用 基于中继的分支策略 来帮助快速开发产品,定期部署产品,并安全地将更改交付到生产环境。

Microsoft还使用 平台工程原则 作为 其一个工程系统的一部分。

Microsoft发布流

每个组织都应满足标准代码发布过程,以确保团队之间的一致性。 Microsoft发布流包含从开发到发布的 DevOps 流程。 发布流的基本步骤包括分支、推送、拉取请求和合并。

Branch

若要修复 bug 或实现功能,开发人员会在主集成分支中创建一个新分支。 Git 轻型分支模型为每个代码贡献创建这些短暂的主题分支。 开发人员尽早提交代码,并使用 功能标志 以避免长时间运行的功能分支。

推送

当开发人员准备好将更改集成并交付给团队的其余部分时,他们将本地分支推送到服务器上的分支,并打开拉取请求。 在多个分支中工作的数百名开发人员的存储库使用服务器分支的命名约定,以缓解混淆和 分支激增。 开发人员通常创建名为 users/<username>/feature其帐户名称的 <username> 分支。

拉取请求

拉取请求控制主题分支合并到主分支,并确保满足分支策略。 拉取请求过程生成建议的更改并运行快速测试通过。 第一级和第二级测试套件在不到五分钟内运行约 60,000 个测试。 这不是完整的Microsoft测试矩阵,但足以快速增强对合并请求的信心。

接下来,团队的其他成员查看代码并批准更改。 代码评审在自动测试结束后继续进行,对于发现架构问题特别有用。 手动代码评审可确保团队中的其他工程师能够了解更改,并且代码质量仍然很高。

Merge

拉取请求满足所有构建策略并且审阅者已批准后,议题分支将合并到主集成分支,拉取请求即完成。

合并后,其他验收测试会运行,这些测试需要更多时间才能完成。 这些传统的签入后测试会进行更彻底的验证。 此测试过程在拉取请求审核期间提供快速测试,并在发布前确保完整的测试覆盖率,提供了良好的平衡。

与 GitHub Flow 的差异

GitHub Flow 是一种常用的基于主干的开发发布流程,组织可以实现 Git 的可扩展方法。 但是,一些组织发现,随着需求的增长,它们必须背离 GitHub Flow 的某些部分。

例如,通常被忽视的 GitHub Flow 部分是,拉取请求必须部署到生产环境进行测试,然后才能合并到主分支。 此过程意味着所有的拉取请求都会在部署队列中等待合并。

一些团队有数百名开发人员在单个存储库中不停地工作,每天可以完成200多个拉取请求到主分支。 如果每个拉取请求都需要部署到全球多个 Azure 数据中心进行测试,开发人员会花时间等待分支合并,而不是编写软件。

相反,Microsoft团队继续在主分支中进行开发,并将部署批处理成定时发布,通常与为期三周 的冲刺 节奏保持一致。

实现细节

下面是Microsoft发布流的一些关键实现详细信息:

Git 存储库策略

不同的团队有不同的策略来管理其 Git 存储库。 某些团队将大部分代码保存在一个 Git 存储库中。 代码分为组件,每个组件都位于其自己的根级别文件夹中。 大型组件(尤其是较旧的组件)可能有多个子组件,这些子组件在父组件中具有单独的子文件夹。

显示 Git 存储库结构的屏幕截图。

辅助存储库

某些团队还管理临时存储库。 例如,在 GitHub 上开发的内容包括生成和发布的代理任务VS Code 扩展以及开源项目。 配置更改签入到单独的存储库。 团队依赖的其他包来自其他位置,并通过 NuGet 使用。

Mono 存储库或多存储库

虽然一些团队选择使用单一存储库,即 单一存储库,但其他 Microsoft 产品则采用 多存储库 方法。 例如,Skype 拥有数百个小存储库,这些存储库采用各种组合拼凑起来,以创建许多不同的客户端、服务和工具。 尤其是对于采用微服务的团队,多存储库可能是正确的方法。 通常,最初作为整体式架构的较旧产品发现,单一代码仓库方式是最容易过渡到 Git 的方法,其代码组织也反映了这一特征。

发布分支

Microsoft版本发布流程确保主分支始终处于可生成状态。 开发人员在短生命周期的主题分支中工作,并将其合并到 main。 当团队准备好发布软件时,无论是在冲刺结束时还是计划进行重大更新,他们都会从主分支创建新的发布分支。 发布分支永远不会合并回主分支,因此可能需要 精心挑选 重要更改。

下图显示了蓝色的短生存期分支和黑色的释放分支。 一个具有需要挑选提交的分支以红色显示。

显示 Git 发布分支结构的关系图。

分支策略和权限

Git 分支策略有助于强制实施发布分支结构,并使主分支保持干净。 例如,分支策略可以阻止直接推送到主分支。

为了保持分支层次结构整洁,团队使用权限阻止在层次结构的根级别创建分支。 在以下示例中,每个人都可以在用户/功能和团队/等文件夹中创建分支。 只有发布管理员有权在 releases/下创建分支,并且某些自动化工具具有 集成/ 文件夹的权限。

显示分支的屏幕截图。

Git 存储库工作流

在存储库和分支结构中,开发人员执行日常工作。 工作环境因团队和个人而异。 一些开发人员更喜欢命令行,其他开发人员(如 Visual Studio)和其他开发人员在不同的平台上工作。 Microsoft存储库中的结构和策略可确保建立坚实且一致的基础。

典型的工作流涉及以下常见任务:

生成新功能

构建新功能是软件开发人员工作的核心。 该过程的非 Git 部分包括查看遥测数据、提出设计和规范以及编写实际代码。 然后,开发人员通过同步到最新提交 main来开始使用存储库。 主分支总是处于可构建状态,因此肯定是一个不错的起点。 开发人员检出新功能分支,进行代码更改、提交、推送到服务器,并创建新的拉取请求。

使用分支策略和检查

创建拉取请求后,自动化系统会检查新代码是否能成功构建、不会造成任何功能破坏,并且不违反任何安全或合规性策略。 此过程不会阻止其他工作并行发生。

分支策略和检查可能要求成功构建,包括通过测试、由接触的代码所有者签署以及进行外部检查以验证公司策略,然后才能完成拉取请求。

这是一个显示拉取请求检查的屏幕截图。

与 Microsoft Teams 集成

许多团队配置 与 Microsoft Teams 的集成,这将向开发人员的团队成员宣布新的拉取请求。 任何被更改代码的所有者将自动被添加为审阅者。 Microsoft团队通常对许多人参与的代码(如 REST 客户端生成和共享控件)使用可选审阅者来审查这些更改。

显示 Teams 集成的屏幕截图。

Teams 通知中关于拉取请求的屏幕截图。

使用功能标志进行部署

一旦审阅者、代码所有者和自动化等都感到满意,开发人员即可完成拉取请求。 如果发生合并冲突,开发人员将获取有关如何同步到冲突、修复冲突和重新推送更改的说明。 自动化在固定代码上再次运行,但用户无需再次注销。

分支被合并到main,新代码将在下一个冲刺或主要版本中部署。 这并不意味着新功能将立即显示。 Microsoft使用 功能标志分离新功能的部署和公开。

即使该功能在展示之前还需要更多工作,但只要产品能够成功生成和部署,也可以放心地继续访问 main。 进入 main后,代码将成为正式版本的一部分,该版本将再次进行测试、确认满足策略并进行数字签名。

向左移动以提前检测问题

此 Git 工作流提供了多种优势。 首先,在单个主分支中制定,几乎消除了 合并债务。 其次,请求合并流提供了一个统一平台,用于在项目初期执行测试、代码评审和错误检测。 这种 转移左移 策略有助于缩短开发人员的反馈周期,因为它可以在几分钟(而不是几小时或几天)内检测错误。 此策略还有助于对重构充满信心,因为所有更改都会持续进行测试。

目前,具有 200 多个拉取请求的产品每天可能会生成 300 多个持续集成生成,相当于每 24 小时运行 500 多个测试。 如果没有基于中继的分支和发布工作流,就不可能进行这种级别的测试。

在冲刺里程碑时发布

在每个冲刺结束时,团队会从主分支创建发布分支。 例如,在冲刺 129 结束时,团队会创建一个新的发布分支 releases/M129。 然后,该团队将迭代 129 分支投入生产。

发布分支的分支后,主分支将保持打开状态,供开发人员合并更改。 这些更改将在三周后的下一次冲刺部署中实施。

sprint 129 上的发布分支插图。

发布修补程序

有时,更改需要快速转到生产环境。 Microsoft通常不会在冲刺期间添加新功能,但有时希望快速引入错误修复来解除用户阻碍。 问题可能很小,例如拼写错误,或可能足够严重,导致可用性问题或在线故障事件

从正常工作流开始纠正这些问题。 开发人员从 main 创建分支,进行代码评审,并完成合并请求以将其合并。 此过程始终从更改main开始。 这允许快速创建修补程序并在本地验证它,而无需切换到发布分支。

遵循此过程还保证更改会在 main 生效,这一点至关重要。 修复发布分支中的 bug 但不将更改反馈到 main,这意味着在下一个部署期间,当冲刺 130 发布分支从 main 开始时,该 bug 将再次出现。 在故障期间,由于可能产生的混乱和压力,很容易忘记更新 main 。 首先对 main 进行更改,确保始终在主分支和发布分支中有这些更改。

Git 功能支持此工作流。 为了立即将更改引入生产环境,一旦开发人员将拉取请求合并到main之后,他们就可以使用拉取请求页面将更改挑选到发布分支中。 此过程将创建一个新的拉取请求,该请求面向发布分支,并回送刚刚合并到 main的内容。

将修补程序提交到分支 129 中的插图。

使用 cherry-pick 功能可以快速创建拉取请求,从而提供分支策略的可追溯性和可靠性。 挑拣(Cherry-picking)可以在服务器上进行,无需将发布分支下载到本地计算机。 由于两个分支之间的差异,进行更改、修复合并冲突或进行轻微更改,都可能发生在服务器上。 团队可以直接从浏览器编辑器中编辑更改,或经由 Pull Request Merge Conflict Extension 以便获得更高级的体验。

拉取请求面向发布分支后,团队代码会再次查看它、评估分支策略、测试拉取请求并将其合并。 合并后,修补程序在几分钟内部署到服务器的第一 。 从那里,团队会使用部署通道逐步将修补程序部署到更多帐户。 随着更改部署到更多用户,团队会监视成功并验证更改是否修复了 bug,同时不引入任何缺陷或速度放缓。 修复程序最终会部署到所有 Azure 数据中心。

移动到下一个迭代

在接下来的三周内,团队完成向冲刺 130 添加功能,并准备好部署这些更改。 创建新的发布分支releases/M130,然后从 main 部署该分支。

此时,生产中实际上有两个分支。 借助基于环的部署,可以将更改安全地引入生产环境,快速环将应用冲刺 130 的更改,而慢速环服务器保留在冲刺 129 上,同时在生产环境中验证新更改。

在部署过程中对更改进行热修可能会需要对两个不同的发布版本进行修补:第129次迭代发布和第130次迭代发布。 团队将热修复移植并部署到两个发布分支。 130 分支使用修补程序重新部署到已升级的通道。 129 分支使用紧急修复程序重新部署到尚未升级到下一个迭代版本的外层环境。

在部署所有环之后,旧的冲刺 129 分支将被废弃,因为所有作为修补程序引入冲刺 129 分支的更改已在main中完成。 因此,这些更改也将位于releases/M130分支中。

sprint 130 中发布分支的插图。

概要

发布流模型是Microsoft如何使用 DevOps 进行开发以提供联机服务的核心。 此模型使用简单的基于主干的分支策略。 但是,微软的发布流程允许开发人员继续工作,而不是让他们停滞在部署队列中,等待合并更改。

此发布模型使我们能够定期在 Azure 数据中心推出新功能,尽管 Microsoft 的代码库庞大且开发人员众多。 该模型还允许将修补程序快速高效地引入生产环境。