首页 公告正文

引导Coinbase Monorepo

Quick 公告 2020-06-13 24 0

Coinbase如何使用Monorepo为开发人员提供世界一流的工具,安全性和可靠性

卢克·黛米(Luke Demi)

在过去的一年中,Coinbase的开发人员生产力团队一直致力于提供Bazel monorepo,以使工程师能够更轻松地在公司范围内进行协作,并使公司处于行业标准工具上。

今天,我们的Bazel monorepo拥有100多个项目,占Coinbase日常部署的22%,涵盖四种不同语言(Go, Ruby ,Node,Python)。我们的长期目标是将公司内的所有项目迁移到monorepo。我们目前的轨迹是到2021年初实现这个目标。

此写信的目的是:

  1. 概述我们选择走Monorepo道路的决策过程
  2. 描述monorepos面临的一些通用挑战
  3. 描述我们面临的一些具体挑战
  4. 推断我们的轨迹

为什么选择monorepo?

为什么Coinbase选择暂停我们现有的开发人员生产力工具,并停止有争议的Monorepo之路 ?

依赖管理

第一个主要原因是要解决我们有关依赖关系管理和代码共享的故事。像许多具有许多较小服务的其他公司一样,我们经常创建在公司中许多(或全部!)服务之间共享的库。随着我们的成长,已经创建了越来越多的此类库(例如:日志记录,服务器样板文件,authn / authz),并且依赖于我们生态系统中的近千种服务。

这些代码共享关系经常变得复杂,其中服务取决于其他服务,而库取决于其他库。在多仓库环境中,由于需要对内部服务进行版本控制和发布,这些关系变得更加复杂。

随着公司内部服务到服务到图书馆与图书馆之间关系的数量不断增加,对复杂工具的需求也在不断增长,以确保图书馆是最新的并且可以在整个公司范围内工作。对关键库进行改进可能需要花费数月的时间才能在组织内的所有服务中推出。在进行库更改的团队和后来严重改变其版本的客户团队之间存在责任追究差距。

monorepo通过完全删除概念即可解决这些版本控制和发布问题。

根据定义,在monorepo中,代码库中只有给定库的单个版本。对该库所做的任何更改都会立即推广到整个公司,并且更改后Monorepo测试套件的结果可以看出它们的影响。

此外,不再需要使用复杂的工具来管理发布,导入和版本变更库。相反,由于所有代码都一起存放在单个存储库中,因此无需将软件包发布到另一个位置。如果您想依靠其他团队编写的代码,就像将其导入服务一样简单。如果您想编写一个新的共享库,就像在/ shared中编写代码并根据服务中的代码一样简单。

由于这一新的工作流程,对广泛依赖的库所做的更改将立即触发任何依赖服务的测试套件。在运行这些测试之后,库所有者可以放心,他们对所有下游服务的更改均已安全完成,而无需通过版本提升手动测试这些服务,也不必等待服务所有者更新和发现不兼容性的潜在时间。

行业标准工具

推进monorepo的第二个主要原因是我们希望将公司定位在行业标准工具上。

我们在Coinbase的现有工具依靠内部专有工具来定义代码和配置之间的关系。开发人员使用Dockerfile定义其存储库中的代码与生成的(将要部署的)Docker映像之间的关系。然后将这些Docker映像映射到类似docker-compose的文件,该文件将配置映射到生成的Docker映像。最后,一个名为Codeflow的工具会在将“发行版”发送到我们的部署管道之前,处理并映射这些Docker映像及其关联配置之间的关系。

当前策略所面临的挑战是,在最初设计Codeflow的时候就强烈假设一个GitHub存储库等于一个Docker映像等于每个AWS账户一个单一“部署”。

随着时间的推移,我们已经扩展(附加)了代码流功能,以允许每个存储库多个Docker映像和每个AWS账户多个“配置”。但是,这些改进的最终结果是一条僵硬,专有,不透明的链,将这些Github存储库与其生成的多个Docker映像相关联,然后将它们与每个AWS账户的多个“云”配置相关联(“环境” ”)。

即使在所有这些工作结束时,工程师仍然希望能够更好地自定义从源代码到“发布”(Docker映像+云元数据)的链。每项新的Codeflow功能都会使工程师更加艰难地了解这些专有关系。

我们需要的是一种显式且灵活地定义代码与生成的可部署映像和元数据之间的关系的方法。

Bazel提供了所需的构建块,这些块为开发人员提供了无限制的自定义和对代码部署方式的可见性。从源代码到可部署的“发行版”的典型Bazel“规则”链可能不是预先定义源代码和“发行版”之间的不透明或僵化的关系,而是这样的:

现在,开发人员可以灵活地按照自己认为合适的配置来部署代码。现在,我们构建这些关系的过程是灵活且透明的。

我们正在投资monorepo工具,因为它使我们能够无缝共享代码并更快地在整个组织中推出库和安全性更改。此外,它将自己置于行业标准工具上,该工具可以更清楚地定义代码与部署到我们的云环境中的内容之间的关系。

通用Monorepo挑战

将您的所有代码迁移到monorepo中会导致一个明显而明显的问题:过去十年中发布的大多数工具都是在假设一个“ git存储库” ==“一套测试” ==“ a离散的构建工件集”。基于该假设,在多仓库环境中,每当将更改推送到git存储库时,该存储库的所有构建和测试都将在持续集成(CI)中运行。

但是,在monorepo中,组织的所有服务和库都位于同一存储库中。基于上述假设,对monorepo的每次提交都需要测试每个测试并构建每个构建。

这就是操作monorepo的核心挑战:在每次提交时完全测试并构建整个存储库不再有意义。

相反,我们必须依靠Bazel之类的工具将所有服务和库组织到一个明确的“构建依赖关系图”中,我们可以利用它来构建和测试每次提交的确切要求。

即使使用Bazel之类的工具在存储库中构建规则的依存关系图(“构建块”),也不可避免地存在大量其他仓库和服务都依赖于monorepo的规则/库/服务。因此,简单地关闭存储库并运行测试或构建单个项目的输出仍然需要一个小时来构建。

为了使构建和测试时间达到合理的水平,要解决此问题,就要求构建是封闭的,以便可以积极地缓存构建输出。在monorepo的上下文中,“封闭”表示构建可以产生确定的输出,而不管构建运行的是哪个系统或一天中的时间。

确定性,密封构建输出的目标是,可以缓存每个构建的输出,并将其用于在CI或开发人员机器中生产的公司中的所有构建。

现实情况是,多重挑战阻碍了真正可复制,密封的构建。例如,一个封闭的构建不能使用主机系统配置的任何部分,因为即使对构建主机之间的系统库或OS版本进行很小的更改,也可能导致缓存命中率的完全下降。此外,必须将每个必需的依赖项固定为SHA或将其源代码包含在monorepo中。

可以影响monorepo的纯净度的微小外部影响无穷无尽。但是,达到“足够密封”的状态到大多数构建产生相同输出的位置可以大大提高整个构建机器的缓存命中率,并显着缩短构建时间。

在围绕缓存构建了一个故事之后,我们调整了现有工具,以在构建之间获取和更新缓存,以缩短构建时间。

即使具有缓存,在“ git存储库” ==“构建/测试的一个集合”设计的工具上利用Bazel monorepo所需的工具也会令人沮丧,尤其是当我们尝试为许多项目或项目提供测试结果时在单个项目专用空间内的所有库。

Coinbase的Monorepo挑战

在Coinbase,许多旨在保护敏感项目的安全控制都是基于以下假设:单个Github存储库==单个项目,并在Github存储库级别实施。这样,将每个项目(以及用于部署自身的基础结构工具)放置在单个存储库中,将需要“一种适合所有情况”的安全性-有效地在存储库中最敏感的项目级别上监管monorepo中的每个人。

在Coinbase,我们需要共识 (以+1的形式),以便将代码合并到母版并部署到生产中。强制执行此行为的网守是Heimdall,这是我们构建的服务,用于跟踪特定Github存储库的任何给定Git提交SHA的+1。

我们解决monorepo中“一刀切”的安全性问题的解决方案是使我们的内部工具Heimdall适应Github功能,即CODEOWNERS。该功能使您可以定义存储库根目录下通过单个CODEOWNERS文件审阅代码所需的个人或团队,格式为`/ directory-name @ github / team-name`。

在我们的monorepo中,我们使用脚本生成CODEOWNERS文件,该脚本汇总存储在存储库中每个目录的根目录下的OWNER文件的内容。每个OWNERS文件仅包含一个空格分隔的团队列表,对于给定目录,需要对其进行审阅。

我们在OWNERS文件中的团队旁边允许使用特殊的语法,以表示特定团队所需的评论总数,以便我们可以对某些目录要求多个评论。在上面的屏幕截图中,您可以看到{0}表示一个团队需要零评论,而{2}则需要该团队两个评论。假设没有{}语法的小组需要进行一次审核。

Heimdall使用特殊的语法在CODEOWNERS文件中解析所有这些信息,以确保在成功部署提交之前可以满足所有必需的审阅。尽管Github只需一个团队的批准就可以将提交标记为已审核,但是我们拥有单独的Heimdall GitHub状态,以确保PR在没有拥有团队足够的提交的情况下不会意外合并。

下一步是什么?

我们的基础架构团队的目标是为Coinbase的开发人员提供世界一流的工具,安全性和可靠性。

尽管我们在为世界一流工具的未来提供基础层方面已取得了长足的进步,但在将Monorepo推广到整个公司之前,仍然需要填补空白。

当前,开发人员使用自己的笔记本电脑在本地进行构建和测试。不幸的是,这意味着我们无法利用在构建和测试团队中填充的共享缓存,从而导致漫长甚至潜在的令人沮丧的本地构建时间(以及狂热的拥护者!)。我们的解决方案是在EC2中为每个开发人员提供他们自己的远程Linux机器,以便他们可以轻松地同步其更改,以利用共享缓存来快速构建和测试。

虽然我们增加了对Coinbase所有主要后端语言的支持,但是我们的monorepo(和Bazel本身)还没有对利用React和React Native的客户团队提供强大的支持。在今年余下的时间里,我们将努力增加对这些前端语言的支持。

最后,虽然我们当前的构建和测试工具已扩展到约100个项目,但我们预计monorepo在明年将在项目和代码行中扩展一个数量级以上。我们预计这种增长将使我们在当前的缓存,版本控制和构建策略中遇到扩展限制。

总体而言,这是Coinbase monorepo的承诺,挑战和未来的高级概述-预计未来几个月会有更多博客文章讨论我们monorepo的深入组成部分。

如果您有兴趣解决诸如此类的复杂技术难题,那么Coinbase正在招聘。


引导Coinbase Monorepo最初发布在Medium上的Coinbase博客上,人们通过突出并响应这个故事来继续对话。

声明:本资讯不能作为投资依据,与数字货币交易平台导航(https://www.heqi.cn/ex/)无关。

版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

评论