本篇是对 RustConf 2023中的Infrastructure for Rust这一视频的翻译与整理, 过程中为符合中文惯用表达有适当删改, 版权归原作者所有.
我今天要和大家讨论支持Rust及Rust项目的基础设施。Rust是一门令人惊叹的语言,我非常喜欢它,看到它的普及度和社区的成长令人非常满意。但从项目背后的基础设施来看,这种增长带来了一些挑战,如果我们希望Rust在未来几十年成为基础技术,我们就需要解决这些挑战。这就是我今天要讨论的内容。
在开始正式讨论之前,我想先简单介绍一下自己。我叫Yand David,但大家通常叫我JD。大约一年前,当项目基础设施团队向Rust基金会寻求帮助并请求雇用一名全职人员时,我加入了该团队。在过去的一年里,这就是我的工作 — 全职负责项目的基础设施,重点关注我们的云组件。
我想以两个简短的问题开始这次讨论,以确保我们都在同一页上。第一个问题是:什么是我们的基础设施?第二个问题是:为什么你应该关心它?
从非常高的层面来看,我会将我们的基础设施描述为一组我们运营的工具和服务,用于帮助人们开发和使用这门语言。我不想过多地深入细节,只想通过看一下基础设施团队管理的一些内容来给你一个粗略的概念。我可以保证,这里的每个人可能至少使用过其中的一项。
一般来说,我们可以将我们的基础设施分为两组不同的工具:一组主要支持Rust的开发,另一组主要支持Rust的使用。我不会过多介绍,但我想强调每组中的一些重要例子。
在贡献者方面,我们有支持语言开发的工具。最重要的是我们的持续集成系统,我们稍后会详细讨论。GitHub或Rust的每次提交都会进行广泛测试,我们会为每次进入master分支的提交构建并上传发布工件。这是一个相当复杂的过程,但它能够实现一些有趣的下游活动,比如对每次提交进行性能和回归测试,以及所有Rust版本。
我们还有crater,这是一个工具,允许贡献者和维护者在crates生态系统上运行实验。这对于测试新语言特性对更广泛生态系统的影响非常有用。
我们还开发和维护了一系列不同的机器人和小型GitHub应用程序,这些东西帮助维护者分类问题,合并提交,并在GitHub上执行其他管理任务。值得一提的是bors和triagebot。
最后,我想强调一下云计算程序。作为基础设施团队,我们在云中管理一组服务器,允许维护者使用具有多核和大内存的相当大的机器来处理编译器和其他受益于大量计算能力的任务。这是Rust基金会云计算程序的一部分,对那些无法访问M1笔记本电脑或其他强大硬件的人来说非常有帮助,特别是在处理Rust测试套件时。据我们所知,这极大地缩短了人们运行测试的时间,从几小时减少到了15分钟。这是一个巨大的改进,也是我们作为一个团队非常自豪的支持项目。
在用户方面,情况稍微简单一些。最重要的基础设施可能是Rust发布。我们有很多工具用于准备、发布和分发Rust版本,以便用户可以下载并开始使用Rust。
基础设施团队还支持crates.io和docs.rs团队托管他们的应用程序。我们不参与开发,这是由各个团队完成的,他们应该得到所有的赞誉,但基础设施团队帮助处理底层基础设施。
我们还做很多其他工作,但我认为对于这次讨论的目的来说,这些是需要记住的重要内容。我想强调的是,我们有各种不同的组件:GitHub应用程序、应用程序、大量的云基础设施,我们运行服务器等。这是一个非常广泛的领域,团队需要管理,Rust的开发和使用的每一步都以某种方式涉及到项目运营的基础设施。
这在某种程度上回答了我的下一个问题:为什么你应该关心这个?因为无论你是维护者、贡献者还是语言用户,你都在以某种方式使用我们的基础设施。没有基础设施团队管理的一些组件,开发或使用Rust将变得更加困难,有时甚至几乎不可能。
你已经看到,我们有很多基础设施要管理。如果我们希望贡献者和用户继续拥有良好的开发体验,我们面前还有不少挑战需要克服。这些挑战来自我们所看到的Rust的快速增长。
Rust正在快速增长,而且增长非常快。我们通过每年发布的调查结果,以及我们的论坛、Zulip、Discord和社交媒体上持续增加的活跃度,可以从轶事中得知这一点。但我们也可以从我们自己的指标中看到这一点。
这种快速增长率的挑战在于,我们需要能够与之同步增长,包括我们的基础设施和管理它的团队。我们必须适应这个不断增长的社区创造的新需求。我想谈一个非常具体的例子,它很好地说明了这一点,那就是我们的流量指标,特别是Rust发布的流量。
这张幻灯片显示了过去几年Rust发布流量的增长情况,以及我们预计明年的情况。每当用户安装Rust时,他们都会从我们的后端下载一个工件。这个流量从去年每月几百TB增长到今年每月超过1PB。这延续了我们一段时间以来看到的趋势,即我们的流量大约每年翻一番。
可怕的不是实际数字,而是这种趋势。对我们来说,问题在于出站流量不是免费的。虽然目前我们可以通过云提供商的慷慨赞助来承担当前的成本,但如果这种指数增长继续下去,很快就会变得不可持续。
我们正在探索短期、中期和长期项目来解决这个问题,我想简要地谈谈它们。
在短期内,我们正在努力的解决方案是CDN(内容分发网络)。没有CDN,情况是这样的:右边的快乐用户试图安装Rust,他们通过互联网访问我们的后端。每次下载Rust版本或crate都要通过我们自己的系统,我们需要为每个用户的出站流量付费,即使他们可能都在获取相同的版本或相同的crate。
有了内容分发网络,我们可以大大改善这种情况。内容分发网络是全球分布的服务器网络,可以缓存内容并使其在本地对用户可用。当用户下载Rust版本时,他们将通过地理位置最近的节点下载。如果该版本已经在网络中缓存,就不需要从我们自己的后端获取,这对用户来说更快,对我们来说也更便宜。
Rust项目从早期就在AWS上托管了大量基础设施,这是我们存储Rust版本和crates的地方。我们也从一开始就使用AWS的内容分发网络CloudFront。去年,Fastly开始赞助我们的项目,我们开始使用它的CDN,首先是用于crates,然后是用于发布。在过去的几个月里,我们将越来越多的流量转移到Fastly,现在我们在这两个CDN之间运行50/50的分割。
这意味着我们能够将流量成本分散到多个提供商中,这对我们来说在两个方面都是好消息。首先,它降低了任何单一公司需要为支持我们的基础设施做出的贡献。其次,它为我们展示了一种前进的方式,即如何通过在多个提供商之间分配成本和基础设施来管理这种增长。
实际上,我们已经这样做了一段时间。我们一直在使用许多不同的合作伙伴来托管我们的基础设施。这些是支持Rust项目和Rust基金会的一些合作伙伴。没有他们的支持,我们就无法提供我们目前拥有的所有不同服务和工具。
但我想回到这张幻灯片。分散流量很好,它有所帮助,但它并没有真正改变我们看到的潜在趋势。如果我们的流量每年翻一番,将一半流量转移到不同的提供商只意味着我们为自己赢得了12个月的时间,然后我们就会回到起点。
所以我们需要寻找结构性的解决方案来解决我们的问题。为此,我们需要更多地了解我们的流量及其来源。这个问题有一个非常有趣的答案,至少对我们来说在目前有点出乎意料。
我们可以看到,50%的流量来自持续集成系统。不是用户安装Rust,而是自动化测试设置他们的环境。这在某种程度上是有道理的,看看我自己的偏好,当然是有一个干净的构建状态,所以它是一个全新的镜像,我在其中安装Rust,我相信我们大多数人都是这样做的。
这听起来可能很糟糕,但实际上为我们提供了优化的机会。因为我们只需要处理少数几个流量来源,我们不需要为全球数百万用户进行优化。这给了我们更多的灵活性和更多的工具来解决这个问题。
我们正在朝着两个不同的方向努力,一个是中期的,一个是长期的。我在这里以GitHub Actions为例,因为我认为它现在是最流行的持续集成平台,特别是对于开源社区来说,但原则上适用于所有主要的云平台。
目前的状态是,当用户在他们的GitHub Actions工作流中安装或更新Rust时,运行该作业的虚拟机将从我们的CDN获取发布。这每小时发生数千次,针对每个版本和在CI中安装的每个crate。
但因为我们只处理少数几个来源,我们可以针对它优化我们的基础设施。我们正在评估将缓存带到Actions实际运行位置的选项,而不是通过互联网为GitHub Actions提供请求。如果我们能够将最常用的版本和crates放在靠近GitHub基础设施的地方,我们就可以从本地缓存中服务这些请求,避免通过互联网获取数据。因为我们只为通过互联网的流量付费,这为我们提供了大幅降低总体成本的巨大机会。
但这种方法有一些缺点。首先,我们实际上无法可靠地检测哪些流量来自GitHub Action,哪些流量来自用户。我们可以根据IP范围等因素做出假设,例如流量的来源网络,但这不是准确定位到GitHub Actions工作流的保证方法。
其次,只有当缓存与运行作业的虚拟机在同一网络中时,我们才能避免成本。我们为通过互联网的流量付费,所以如果我们想缓存内容,我们需要将其带入同一个数据中心,这样它就永远不会真正到达互联网。这意味着这种策略只有在我们能够确定CI系统运行的位置并将我们的缓存带到那里时才真正有效。
即使我们设法解决了这个问题并将缓存放在那里,我们仍然需要将流量路由到缓存,而不是让它遵循通过我们的CDN的默认路径。同样,我们可以使用IP范围和DNS来实现这一点,但这不是一门科学,而是一种相当粗糙的工具。
所以你可能会问,既然有这么多缺点和困难,我们为什么还要这样做?这个问题没有更好的解决方案吗?我想用我最喜欢的回答来回答这个问题:是的,也不是。可能有一种更好的方法,但它需要更多的工作,我们还没有达到那个程度,而且它还不对我们开放。
长期来看,我们想为发布和crates部署适当的镜像。镜像不仅仅是一个缓存,而是我们数据的完整副本。我们可以在全球范围内分发它们,理想情况下,像云提供商或常见的大学这样的第三方也能够托管自己的镜像。
通过这种方式,我们可以为用户提供在他们所在地区访问这些数据的机会,这在某些情况下可以提供更好的用户体验,对我们来说,可以给我们更多工具来管理我们的流量和如何提供数据。
目前阻止我们这样做的是,我们还没有对发布和crates进行足够强的加密签名,以至于我们觉得可以放心地将它们放入我们不控制的网络中。在我们将我们的工件放入数据中心或交给世界各地的其他人之前,我们需要确保crates和发布不会被篡改。这个签名工作是项目中许多人正在思考和工作的内容。作为一个基础设施人员,我真的很期待这最终能够落地,但它仍然需要更多的时间。在那之前,我们确实需要研究其他解决方案来解决我们的问题。
我想快速总结一下。从自己的指标中看到,Rust正在快速增长,我们主要从用户方面的流量中看到这一点。我们正在以三种不同的方式解决这个问题:
- 我们正在努力改进我们的CDN,使其更高效、更快速。
- 在中期,我们正在研究GitHub Actions的缓存,以移动这部分来自CI的大量流量,并尝试以稍微不同的方式处理它。
- 从长远来看,我们真的希望为发布和crates开发镜像,以提供我们数据的更广泛地理分布。
但即使有了这些措施,指数增长仍然是让我们非常担心的事情。我们必须不断地关注和监控它的走向,以便我们能够足够快地做出反应,不被这种增长打个措手不及。
流量是属于我们基础设施用户端的一个例子。作为另一个例子,我还想谈谈我们在贡献者端看到的Rust增长,我们在那里提供工具来帮助开发语言。
这里的主要例子是我们在GitHub上的持续集成设置。如果你曾经为rust-lang/rust仓库或任何相关仓库做过贡献,你可能已经在你的拉取请求中看到这个机器人弹出。Bors是我们用来启动测试套件然后在所有检查通过后合并拉取请求的机器人。
从外部看起来相当无害的东西,如果你实际上开始查看后台发生的构建,很快就会变得复杂。因为我们的测试套件现在并行运行大约55个作业,为所有一级和二级目标平台测试和构建Rust。这个配置是一个700行的怪物,因为它嵌套得太深,没人能理解。
对我们来说,有趣的是我们看到Rust如何增长的领域是我们并行运行的作业数量不断增加,每次Rust增加对新平台或新目标的支持时都会如此。随着Rust在更广泛的硬件或目标上得到支持,我们的测试服务器也在不断增长。
这里复杂性的另一个领域是,随着更多功能被添加到Rust中,我们也看到需要运行的测试数量增加,完成这个过程所需的时间也在增加。
现在,我们大约需要两个小时来运行这个完整的测试套件,并为所有目标平台构建和测试这些工件。这还可以,但我真的想谈谈另一个数字,那就是我们为每个合并的拉取请求产生的计费时间。现在,我们大约需要两天。每次我们运行Bors时,GitHub实际上会向我们收取两天的GitHub Actions费用。
几周前,当我们开始深入研究这个问题时,我们发现我们实际上为每个PR运行了近400小时的CPU时间。这是两周的时间。
你可能会想,为什么这对我们来说是个问题?原因是CPU时间或计算能力,再加上带宽,这是我们成本的两个主要驱动因素。没有什么比CPU时间和流量对我们的总体成本有更直接的影响。
虽然GitHub慷慨地赞助了我们使用GitHub Actions,但我们需要确保这种赞助在长期内是可持续的。每次我们想要添加东西时简单地增加我们的测试套件的大小在长期内是不可持续的。
这里有一个有趣的矛盾,就是我们的测试套件的效率和我们产生的成本之间的关系。在我自己的项目中,如果我想优化我的CI成本,我会让测试更快,因为我们按分钟计费。所以如果我们可以减少测试运行的总时间,对我们来说就更便宜。但这对Rust项目来说并不适用。我们顺序合并拉取请求,我们的拉取请求队列总是有请求在里面,所以我们在GitHub Actions上持续构建。实际上,让我们的CI更快会产生相反的效果,我们增加了利用率,从而增加了成本。
所以我们需要寻找不同的方法来管理和改进我们的CI。这意味着我们需要寻找其他优化方法,而不仅仅是通过投入更多硬件来加快我们的CI。
第一步是分析我们实际上在哪里花费了最多的时间。特别是Yakob在分析我们收集的指标以找出改进领域方面做了出色的工作。这种分析真的帮助我们理解我们可能能够合并哪些作业,共享哪些工作,在更小的实例上运行它们,或者我们如何随时间重构我们的配置,以创造一个出色的开发者体验,同时仍然管理我们的成本。
如果你对这项工作感兴趣,可以来和我聊天或者加入我们的Zulip。这是一个引人入胜的话题,我喜欢讨论它。
还有另一个原因我提到我们的持续集成,那就是它是项目基础设施的基础部分。没有它,我们开发语言会困难得多。它的配置可能很复杂,但底层设计和架构实际上非常简单。我们的流水线只有两个步骤:我们并行运行55个构建,它们在Docker容器中相互隔离,所以测试之间基本上没有副作用。
虽然这在某些工作中重复了一些工作,例如为这55个作业中的每一个构建Rust编译器,但它使我们很容易理解CI的设计和相互依赖关系。这是团队面临的简单性和效率之间张力的一个很好的例子,效率通常伴随着增加的复杂性。
因此,当我们开始考虑如何重构我们的CI时,我们需要对现在非常简单的设计进行更改,并转向更复杂的东西。
在我个人看来,这几乎是团队目前面临的最大挑战。我们被这两个极端夹在中间。我们是一个管理大量基础设施的小团队,到目前为止,我们真的优化了很多东西以追求简单性。我们使用托管服务,我们有非常简单的架构和设计模式来部署,这有时是以可扩展性或效率为代价的。
但看看Rust作为一种语言、一个项目和一个社区面临的挑战,这给我们带来了一个挑战,这种策略不再适用于我们基础设施的所有部分。对于真正推动我们成本的基础设施,即流量和计算,我们需要接受更高的复杂性以实现可持续性。
这对我们团队来说是一个真正的挑战,因为我们作为一个团队也需要可持续。在我深入探讨这一点之前,我想快速介绍一下团队本身。
这是七个人,他们一起为Rust项目的基础设施工作。我们是项目中的许多团队之一,我们的目的是管理项目本身的基础设施,包括CI、发布、机器人和指标。我们是七个人,每个人都在相当专业的环境中工作,每个人通常都有不同的重点。
如果我们回到这个基础设施概览,你会看到里面有很多不同的东西,从服务器到云组件,再到GitHub Actions和CI,这些都需要不同的背景、不同的技能集和不同的知识。随着每个领域内的复杂性增加,我们需要确保我们作为一个团队与之一起成长,并能够处理复杂性带来的额外工作。
这对我们来说是一个非常困难的挑战,我认为这与其他团队的情况不同。我们面临的第一个挑战是,处理基础设施,特别是云基础设施,可能会产生相当大的影响。我不确定,我不期望很多人看过这个事后分析,但这基本上是我作为基础设施团队成员的第一次公开亮相,当时我不小心中断了crates.io大约15到30分钟,因为我部署了两个事后看来不应该一起部署的东西。
看到传入请求的图表降到零,而你刚刚对生产环境做了更改,然后看到人们在Zulip上出现询问是否发生了什么,这是非常可怕的。尽管我希望说这是我唯一一次搞坏crates.io,但事实并非如此。我还不小心搞坏了旧版Windows用户的crates.io,因为它与Fastly使用的SSL密码不兼容。我对旧版macOS用户也做了同样的事,问题类似,与OpenSSL有关。
正如你所看到的,很容易产生意料之外的重大影响。有人可能会说,在进行更改和测试我们的服务和配置时,我们本可以或应该捕捉到这些问题。我同意我们作为一个团队在这方面可以做得更多,我们肯定已经吸取了关于如何将更改部署到生产环境的教训,特别是那些不相关的可能有意想不到后果的更改。
但事实仍然是,很难测试我们的基础设施和用户设备的每一种排列组合,我们简单地无法测试一切。我们试图使对基础设施的更改更安全,每次更改所需的时间就越长,因为我们增加了测试更改的过程,实际进行更改、测试和合并的时间就越长。
随着这个时间的增加,志愿者为我们的基础设施做贡献变得越来越困难。因为我在团队中全职工作,我可以花时间进行更改,然后适当地测试它,花一天时间尝试启动带有旧Windows版本的旧虚拟机来测试一切是否仍然有效。但对于团队中的大多数人来说,有长时间不间断的时间是一种奢侈,他们simply没有这种奢侈。
对我们来说,这里存在一种紧张关系,我们需要找到一种方法,既能继续鼓励和使社区能够做出贡献,又不至于使过程变得如此复杂,以至于对他们来说实际上是不可行的。
我们面临的另一个挑战是,给人们访问权限是相当困难的。我们在基础设施团队中在信任和权限方面挣扎很多,因为在我们的云基础设施上的工作影响很大,而且可能造成很大的损害,正如我所展示的那样。我们需要非常保守地给予对这个基础设施的访问权限,我们称之为"王国的钥匙"。
但这使得人们更难做出贡献,因为对于大多数贡献者来说,他们甚至无法测试自己的更改,因为他们无法访问基础设施。这意味着有访问权限的人现在有更多的工作要做,因为他们不仅要做自己想要贡献的事情,还需要测试和报告其他人正在做的更改。这使得迭代和人们为我们的基础设施做贡献变得非常困难。
我们正在进行一些项目来使这变得更容易,例如给docs.rs和crates.io这样的团队访问他们的暂存环境的权限,这样他们就可以在隔离的安全方式下完全处理他们的基础设施,然后我们只在想要将其带到生产环境时才参与进来。但这是一个非常劳动密集的项目,老实说,我们现在就是没有足够的时间来完成它。
在这种挑战之间 - 复杂性的上升以跟上Rust的增长,团队中人员的入职困难,以及我们在授予访问权限时必须如此严格 - 我们要以可持续的方式发展团队真的很困难。
除了我们在这里面临的技术挑战,这真的是一个组织挑战,我们必须学会如何克服。我们必须学习如何解决这个问题,如果我们希望团队能够可持续发展并避免人员倦怠的话。
在所有这些关于我们面临的挑战和团队面临的挑战的略显沮丧的讨论之后,我确实想以一个积极的总结而结束。看到Rust的增长和社区内的采用真是太棒了,我为不仅仅是基础设施团队,还有如此多的贡献者和整个社区为我们的成功所做的工作感到非常自豪。
随着Rust的增长,我们需要解决一些挑战:
-
我们需要找到管理流量增长的方法,因为它是我们成本的主要驱动因素,而且它呈指数增长。我们需要认真对待这个问题,并制定解决方案,为未来提供可持续的增长。幸运的是,我们确实有一些好主意,我真的很兴奋能够开始实施它们,但这是一个会随着语言的增长和Rust获得更多采用而不断出现的话题。
-
我们需要在良好的开发者体验和快速CI之间取得很好的平衡,以实现这一点,同时聪明地使用我们从GitHub等公司获得的资源。
-
我们需要随着基础设施及其复杂性的增长而发展团队,以防止人员倦怠,并确保基础设施和团队都能长期支持语言和项目。
在过去的一年里,我一直全职在基础设施团队工作,我一次又一次地被人们愿意为我们的基础设施投入时间和精力所震惊。每当出现问题时,人们都会挺身而出,帮助我们解决问题。我想对Zulip上infra频道中的每个人表示衷心的感谢,感谢他们的帮助和奉献,不仅仅是过去12个月我亲眼目睹的,而是一直以来。与每个人一起工作真是太棒了。
正因为如此,我对我们将克服所有这些挑战并且它在未来不会成为我们的大问题感到非常乐观。
如果你对这些内容感兴趣,或者有问题或想法,或者只是想来和我聊天,请在会议上找我,在Zulip上找我。我很乐意讨论这些内容。
最后,我真诚地感谢大家的时间和关注,希望大家享受RustConf!
这就是演讲的全部内容。演讲者JD详细讨论了Rust项目面临的基础设施挑战,包括流量增长、CI复杂性、团队可持续性等问题,并提出了一些短期和长期的解决方案。他强调了基础设施对Rust生态系统的重要性,并呼吁社区继续关注和支持这方面的工作。