3 月 2 日事件更新
周二 UTC 时间凌晨 02:01,Deno 组织提供的几个服务发生了 98 分钟的服务中断。这影响了 deno.land 网站上的图像和视频、deno.land/x 和 deno.land/std 上 TypeScript 文件的服务、doc.deno.land 上文档报告的生成以及从 cdn.deno.land 下载注册表元数据。我们已经得出结论,这次停机是上游服务提供商 Cloudflare 的一个错误的滥用预防过滤器导致的。这篇文章详细介绍了到底发生了什么、我们如何恢复系统以及我们正在采取哪些措施来防止这种情况在将来再次发生。
所有服务现已恢复正常。api.deno.land 上的注册表 API 未受此事件影响。没有数据丢失。我们认真对待此类停机事件,并真诚地为由此造成的中断表示歉。
要理解真正发生了什么,重要的是要知道我们发布了 Deno 1.8 并发布了在事件发生前 1.5 小时发布的博客文章中的发行说明。这篇文章在事件发生前约 30 分钟出现在 Hacker News 上。在事件发生时,我们网站的流量约为正常流量的 9 倍。
事件时间线
UTC 时间凌晨 02:00,我们收到了 Cloudflare 自动化系统发来的电子邮件,通知我们 deno.land 上的所有媒体都被阻止,原因是涉嫌违反其服务条款的第 2.8 节。服务条款的这一部分详细说明了 Cloudflare 不能被用于主要服务媒体文件。收到这封电子邮件后,我们决定将 1.8 博客文章中的屏幕截图和图像作为临时缓解措施删除。这是在 UTC 时间凌晨 02:09 完成的。但这并没有解决问题。UTC 时间凌晨 02:22,我们向 Cloudflare 提交了支持工单。
UTC 时间凌晨 03:00,我们决定将我们的基础设施迁移到另一个基础设施提供商 (https://fly.io) 来缓解停机问题。非常感谢 Fly.io 的 Kurt Mackey 为此提供帮助,并立即为我们提供了基础设施。我们在 UTC 时间凌晨 03:24 切换了受影响服务的 DNS 记录。这在 UTC 时间凌晨 03:41 为全球大部分用户解决了停机问题。
Cloudflare 在 UTC 时间下午 18:40 解除了对我们网站的封锁 - 事件发生后 16.5 小时,也是我们联系他们后 16 小时。这是我们在提交工单后收到的第一个非标准化回复。
根本原因
我们对事件的初步分析得出结论,Cloudflare 阻止了 deno.land 区域的所有媒体文件 - 这可能是由于 Hacker News 导致的流量大幅增加。这本身不应该导致 deno.land/x 或 deno.land/std 停机,因为它们不提供媒体,而是源代码。这是由于 Cloudflare 似乎将所有 .ts 文件(无论内容或内容类型标头如何)都解释为 MPEG 传输流(属于媒体阻止范围)。在我们这里,这是不正确的,因为 .ts 文件既可以是 MPEG 传输流,也可以是 TypeScript 文件(就像我们一样)。我们所有的 TypeScript 文件都使用 application/typescript
提供。
影响
如您所知,Deno 使用 URL 导入远程代码。这意味着,如果您要导入的模块的宿主出现停机,您将无法再从该宿主下载该模块。所有包管理器都存在相同的问题 - 例如,当 npmjs.org 出现停机时,您将无法再执行 npm install
。
这是否意味着当模块宿主停机时,您将无法运行您的项目?不会。Deno 将所有远程导入缓存在您系统上的全局缓存目录中。这意味着,当您第一次导入一段代码时,它将被下载并缓存,然后在随后的运行中,您可以在没有网络访问的情况下离线使用该代码 - 就像使用 node_modules 一样。
我们预计这次停机对大多数在活跃项目中使用 Deno 的开发人员的影响相对较小,因为他们的依赖项可能已经缓存了。这次停机主要影响了新的 Deno 用户和 CI 管道。
同样重要的是要注意,Deno CLI 不依赖 deno.land 域在线才能正常工作。它完全独立于注册表。如果您的项目只包含来自其他注册表(如 esm.sh、skypack.dev、jspm.dev 或 nest.land)的模块,那么您将不会受到这次停机事件的影响。
下一步是什么?
Cloudflare 在周二晚上联系了我们,讨论了发生的事情。经过初步调查,他们得出结论,这是其滥用监控系统中的错误。Cloudflare 向我们保证,这个问题不会再次发生,并且他们将对其系统实施更改,以确保这种情况不会发生在其他 Cloudflare 客户身上。
Cloudflare 还向我们保证,在误报检测和修复之间存在 16 小时的差距是不可接受的,并且这将是他们立即关注的领域。
这次经历巩固了我们对在标准化的开放式 Web API(如 fetch
)上构建 Deno 运行时的信念,这的确是正确的举措。由于 Cloudflare Workers 也建立在这些标准化 Web API 之上,因此我们能够在 20 分钟内将我们主要的 Cloudflare Worker 迁移到运行在 Fly.io 上的 Deno 脚本。我们只需要填充“fetch”事件就可以运行我们的 Worker。
如果您有兴趣,这是我们用来填充“fetch”事件的代码:https://gist.github.com/lucacasonato/1a30a4fa6ef6c053a93f271675ef93fc。尝试在本地运行此示例,然后访问 http://0.0.0.0:8080。
$ deno run --allow-net https://gist.githubusercontent.com/lucacasonato/1a30a4fa6ef6c053a93f271675ef93fc/raw/efcdc8e798604e194831830fcb962b50261384b3/example-worker.js
Listening on http://0.0.0.0:8080
由于这次事件,我们建立了一个公共状态页面。此页面显示 deno.land/x、deno.land/std、cdn.deno.land 和 api.deno.land 的当前状态。您可以在 https://status.deno.land/ 查看它。