[Trans-by-Gemini-2.5-pro] Node.js官网的迭代历史

原文链接 nodejs.org/en/blog/ann...

很有意思的细节你知道node.js官网的迭代历史吗,跟着这篇文章去了解。翻译是通过 Gemini 2.5 pro experiment 版本来做的,简单且直接。

去年,我们分享了 Node.js 全新网站背后的细节。今天我们再次回来,讨论为 Node.js 发行版资源提供服务的新基础设施。

这篇博文将深入探讨 Node.js 的 Web 基础设施是什么样的,它的历史,以及它今天的现状。我们还将介绍在这次基础设施彻底改造中,我们具体考虑了什么以及优先处理了哪些方面。

一些简要历史

在 2009 年项目启动之初,Node.js 发行版资源(二进制文件和文档)存储在一个可公开访问的 S3 存储桶中。这后来在大约 2010 年 5 月更改为一台 VPS,该 VPS 也托管了 Node.js 网站。

当 2014 年 io.js 分支出现时,他们也使用了一台 VPS 来托管网站和发行版资源。在 2015 年 Node.js 和 io.js 合并后,io.js 的 VPS(此后称为源服务器)被重新用于托管 io.js 和 Node.js 的发行版,以及 Node.js 网站。

还创建了一台备份服务器,其作用几乎与源服务器完全相同。它有两个主要目的:

  1. 处理源服务器无法处理的任何流量。
  2. 在源服务器出现问题时,作为二进制文件和文档的备份。

整个架构看起来是这样的:

Node.js 发行版是如何发布的

当一个主线版本准备好发布时,发布者会触发一个发布 CI 作业。这些作业为所有支持的平台构建 Node.js 二进制文件并生成文档文件。

当一个发布 CI 作业成功完成后,其资源会被上传到源服务器上的一个暂存目录中。当所有的发布 CI 作业都完成后,发布者随后手动将这些资源"提升"到生产目录,使该版本可公开访问。

对于夜间构建或测试版本,提升过程是自动发生的。

参考资料: 发行版概述Node.js 发布流程

成长的烦恼

如今,nodejs.org 域名每月处理超过 24 亿次请求和 3+ PB 的流量,其中大部分流向发行版资源。

这平均相当于每秒约 925 次请求,平均带宽为每秒 1.2 GB。

源服务器没有足够的资源来应对这种情况,并且难以满足需求。作为重新设计的一部分,nodejs.org 网站已从源服务器移走,这确实有所帮助,但仍然不够。

缓存的使用效率也相当低下。Node.js 在其发行版资源中大量使用符号链接。其中一些符号链接包括像 https://nodejs.org/dist/latest/ 这样的目录,它会随着每个版本的发布而改变。当一个版本被提升时,需要清除缓存以更新这些符号链接,以便用户实际获得最新版本。然而,没有很好的方法只清除我们需要从缓存中清除的内容。这导致我们需要在每次发布时清除整个缓存。Node.js 有夜间构建版本,这意味着这种情况至少每 24 小时发生一次。因此,每天大约在午夜 UTC 时间,源服务器实际上会因接收到大量未缓存的流量而遭受类似 DDoS 的攻击。

源服务器还存在一些与其维护相关的其他问题:

  • 服务器上运行内容的文档记录零散;有些内容有详细文档,而另一些则完全没有。
  • nodejs/build 仓库中执行的更改需要由具有访问权限的构建工作组成员(Build WG member)手动部署。也无法保证构建仓库中的内容就是服务器上实际运行的内容。
  • 除了备份实例外,没有暂存环境。
  • 回滚只能通过 VPS 提供商的 Web 门户使用磁盘镜像来完成。

尝试过的努力

所有这些问题结合在一起,造成了除非必要,否则不会触碰源服务器的情况,包括一段其正常运行时间超过 3 年的时期。这些因素也导致了诸如 2023 年 3 月 15 日至 3 月 17 日发生的事件,当时由于源服务器过载和不当的缓存规则,Node.js 发行版资源在 2 天内无法访问。在这类事件和每天发生的宕机之间,用户受到了影响,并痛苦地意识到基础设施的不可靠性。

这需要被修复。

然而,在此基础设施中为修复这些问题所做的尝试收效甚微。更改了 NGINX 配置以修改其资源消耗并添加缺失的文档。集成了 Cloudflare WAF 以阻止来自 Artifactory 等仓库的垃圾请求。进行了负载均衡更改以试图帮助减轻负载。但是,这些最终没有达到应有的效果。

重写

2023 年 8 月,Claudio Wunder 和我 (flakey5) 开始着手为一个提供 Node.js 发行版资源的新方式进行概念验证(proof-of-concept)。

想法很"简单":创建一个新服务,解决我们之前基础设施遇到的所有问题,并且对用户来说,与旧基础设施相比没有任何明显的差异。为了满足这些要求,我们在这个新服务中优先考虑了三个主要目标:

  1. 可靠性(Reliability):服务需要尽可能接近 100% 的正常运行时间。
  2. 可维护性(Maintainability):维护者不应担心因为更改了某些东西而导致系统崩溃。服务需要有良好的文档记录,并且尽可能简洁明了。
  3. 效率(Efficiency):无论使用哪个平台,服务都需要充分利用它来提供尽可能最佳的性能和体验。

为了满足这些要求,我们最终选择使用 Cloudflare Workers 和 R2,原因有以下几点:

  • Workers 和 R2 现在以及历史上一直都是可靠且快速的。
  • Workers 为我们处理了基础设施,所以我们只需要维护服务本身。这大大降低了维护成本,特别是对于一个志愿者团队来说。
  • Node.js 之前已经使用过 Cloudflare 服务;研究扩展其使用是有意义的。
  • 定价。Cloudflare 非常慷慨地通过 Project Alexandria 为 Node.js 提供了免费的 Workers 和 R2 访问权限。

2023 年 9 月,概念验证准备就绪可供审查,并在 nodejs/build 仓库中创建了一个 issue (#3461),寻求构建工作组(Build WG)的批准。

在 Build WG 讨论了这项变更后,它获得了批准,我们开始着手将该服务(现在称为 Release Worker)部署到生产环境。

通往生产之路

开发 Release Worker 的过程中充满了反复试验和学习,经历了服务的多次迭代。它需要具备与其前身 NGINX 相同的所有功能和类似(如果不是完全相同)的行为。

它需要做什么

首先,它需要在 Node.js 最新版本发布后立即提供它们。

其次,它需要正确处理路由。大多数资源的 URL 与其在文件系统中的位置并非一一对应。URL 映射到的位置甚至可能根据所请求的 Node.js 版本而改变。例如:

URL 文件路径
/dist/index.json nodejs/release/index.json
/dist/latest-iron/... nodejs/release/v20.x.x/...
/docs/v0.1.20/... nodejs/docs/v0.1.20/...
/docs/v22.0.0/... nodejs/release/v22.0.0/docs/api/...
/dist/v0.4.9/node-v0.4.9.tar.gz nodejs/release/node-v0.4.9.tar.gz
/dist/v0.4.9/SHASUMS256.txt nodejs/release/v0.4.9/SHASUMS256.txt

这种行为是在整个发布周期中通过多次不同的更改以及发行版资源的分发方式形成的,并通过符号链接实现。然而,R2 不支持符号链接,这意味着我们需要自己设计一个解决方案。

最后,我们需要达到可靠性目标。为此,我们实施了四项措施:

  1. 任何对 R2 的失败请求都会重试 3 次(此外还有 Workers 已经执行的重试)。
  2. 一个"回退"系统。任何对 R2 的请求在所有重试都失败后,会被重写到旧的基础设施。
  3. 当确实发生错误时,它会被记录在 Sentry 中,并且我们会收到通知,以便我们采取适当的行动。
  4. 为 Sentry 和 Release Worker 中的任何关键故障点(例如部署失败)设置了 Slack 警报。

迭代过程

我们最初从一个极其简单的 worker 开始。给定 URL 中的路径,它检查请求的文件是否存在于 R2 存储桶中。如果存在,就返回它。否则,它将请求重写回源服务器。对于导致目录列表的请求,worker 只是将它们转发给源服务器。这个迭代显然几乎没有涵盖任何需求,所以我们回到了绘图板。

第二个迭代基于流行的 R2 目录列表库 render2,由 Kotx 开发。该库对于我们需要覆盖的更通用的用例效果很好,但是,在我们更独特的用例中它表现不足。所以我们复刻(fork)了它,添加了我们需要的功能使其为我们工作。然而,它变得相当混乱,因此未能达到我们的可维护性目标。

这导致了我们的第三次迭代,这是一次完全重写,同时仍然保留了 render2 的某些方面。它在大部分情况下都能工作,但这也很混乱,并且没有达到我们的可维护性目标。它的设计方式也与我们需要服务表现的方式完全一致。如果我们想以任何方式改变这种行为,我们将需要重构代码库的重要部分。我们知道我们可以做得更好。

这引导我们进入了 Release Worker 的第四次也是当前的迭代,这又是一次重写。然而,这一次,它的设计更加模块化,并采用了以中间件为中心的设计。这使得代码更容易跟踪和维护,并且,截至 2024 年 11 月(注:原文日期,此处保留),这就是当前部署到生产环境的版本。

发布流程变更

将 R2 集成到发布流程中的目标是尽可能少地进行更改。为了实现这一点,创建了一个与现有流程类似的工作流。

发布 CI 作业现在将其资源上传到 dist-staging 存储桶,同时也上传到源服务器上的暂存目录。然后,当一个版本被提升时,dist-staging 中的资源会被复制到 dist-prod 存储桶,Release Worker 从该存储桶读取数据。

可维护性

正如我们之前关于网站重新设计的博文中所说,一个开源项目的好坏取决于其文档。为了使 Release Worker 具有可维护性,它需要有良好的文档记录。

这不仅通过代码库中详尽的注释实现,还通过诸如以下的文档来实现:

下一步是什么?

工作尚未完成。我们仍然希望:

  • 研究任何可以进行的性能改进。
  • 拥有更好的测试和更好的开发环境 (PR!)。
  • 获取指标,让我们更清楚地了解 Release Worker 的行为方式以及是否有任何可以改进的地方。

致谢 (Thanks)

许多个人和组织以多种不同方式为这项工作做出了贡献。我们要感谢:

  • 所有使这个项目成为可能的贡献者和协作者
  • Cloudflare 提供了为 Node.js 网站和 Release Worker 服务的基础设施。还要特别感谢 R2 团队给予我们的技术支持。
  • Sentry 为其错误报告、监控和诊断工具提供了开源许可证。
  • OpenJS 基金会 提供的支持和指导。
相关推荐
海晨忆29 分钟前
【Vue】v-if和v-show的区别
前端·javascript·vue.js·v-show·v-if
1024小神1 小时前
在GitHub action中使用添加项目中配置文件的值为环境变量
前端·javascript
齐尹秦1 小时前
CSS 列表样式学习笔记
前端
Mnxj1 小时前
渐变边框设计
前端
用户7678797737321 小时前
由Umi升级到Next方案
前端·next.js
快乐的小前端1 小时前
TypeScript基础一
前端
北凉温华1 小时前
UniApp项目中的多服务环境配置与跨域代理实现
前端
源柒1 小时前
Vue3与Vite构建高性能记账应用 - LedgerX架构解析
前端
Danny_FD1 小时前
常用 Git 命令详解
前端·github
stanny1 小时前
MCP(上)——function call 是什么
前端·mcp