跳至主要内容
Deno 2 终于来了 🎉️
了解更多
A Denosaur looking at the deno cloud.

隔离云的剖析

  • Colin J. Ihrig

如果您曾经尝试过 Deno Deploy,我们的多租户分布式 JavaScript 云,您可能会了解一些这些 glowing 评论

these deno deploy times are incredible

deno deploy is the simplest

deno deploys are so fast

deno deploy was so smooth and frictionless

(如果您还没有,今天就部署一个 Fresh 网站!)

实现这种部署速度并非偶然。设计 Deno Deploy 是一个重新构想我们希望部署应用程序的开发人员体验的机会,我们希望专注于速度和安全性。我们没有部署 VM 或容器,而是选择了 V8 隔离,它允许我们安全地运行不受信任的代码,且开销明显更低。

这篇博文解释了什么是 V8 隔离,并概述了 Deno Deploy 隔离云的主要架构部分。

Deno Deploy 项目和部署

在 Deno Deploy 中,用户和组织将他们的应用程序管理为项目。部署项目可以链接到 GitHub 存储库,使代码能够在每次git push时重新部署。项目还允许应用程序管理环境变量,配置 TLS 证书以及关联域名。Deploy 仪表板还提供开箱即用的日志和分析视图,以提高对应用程序行为的可见性。

如前所述,Deploy 项目可以链接到 GitHub 存储库,以便每次将更改推送到支持存储库时更新正在运行的应用程序。在 Deploy 中,这些更新中的每一个被称为部署。部署是应用程序的不可变快照。每个部署都有一个唯一的 URL,可用于测试或以其他方式通过互联网与应用程序交互。一个项目可以有多个部署,但一次只能将其中一个部署标记为生产环境。

图 1 显示了 deno.land 的部署仪表板。每个部署都显示其唯一的 deno.dev URL 以及用于创建部署的 git 提交信息。“Prod”标签表示当前在 deno.land 上生产环境中运行的部署。

Deployments dashboard in Deno Deploy
图 1. Deno Deploy 中的部署仪表板。

将请求路由到部署

但是,用户如何将 HTTP 请求发送到 Deno Deploy 云中运行的部署呢?

每个部署通过一个或多个公共 URL 公开到互联网。在 DNS 解析期间,所有这些 URL 都映射到 Deno Deploy 的公共 IPv4 或 IPv6 地址。这确保所有部署流量都路由到 Deploy 服务器。

根据用户在世界上的位置,将所有流量发送到相同的目的地可能会非常低效。例如,如果用户位于澳大利亚,但所有 Deploy 服务器都位于美国东海岸,则每个 HTTP 请求(包括所有必要的 TCP 握手)都需要绕地球行驶数千英里。

由于 Deno Deploy 旨在在边缘运行 JavaScript,因此必须将请求智能地路由到最靠近用户的边缘位置。Deploy 利用 anycast 路由在全球 30 多个边缘位置之间共享相同的 IPv4 和 IPv6 地址,如图 2 所示。

Deno Deploy's 30+ edge locations around the world
图 2. Deno Deploy 在全球 30 多个边缘位置。

一旦传入请求被路由到相应的边缘位置,它就会被传递给运行器进程。顾名思义,运行器负责运行应用程序,包括接收流量并将其路由到正确的部署。

Deploy 保持一个域名和部署之间的映射表,以实现此目的。根据使用的协议,HTTP/1.1 Host 标头或 HTTP/2 :authority 伪标头用于确定请求的目标域名。然后,TLS 服务器名称指示 (SNI) 用于确定要用于连接的 TLS 证书。最后,运行器检查映射表以确定要将请求发送到的正确部署。(顺便说一句,Deploy HTTP 流量被重定向到 HTTPS,因此可以可靠地使用 SNI。)

运行器作为隔离虚拟机管理程序

在传统云中,虚拟机管理程序负责创建、运行、监控和销毁虚拟机。在 Deploy 中,运行器充当隔离虚拟机管理程序,执行相同的功能,但使用运行 V8 隔离的进程而不是虚拟机。

在继续之前,我们需要了解什么是隔离。 V8(为 Deno、Google Chrome 和其他几个流行运行时提供支持的 JavaScript 引擎)将隔离记录为

隔离表示 V8 引擎的独立实例。V8 隔离具有完全独立的状态。来自一个隔离的对象不得在其他隔离中使用。

这意味着隔离是一个独立的执行上下文,包括代码、变量以及运行 JavaScript 应用程序所需的一切。

为了帮助理解这一点,想象一个有多个标签的浏览器窗口。每个标签都为单个网页执行 JavaScript,但每个标签的 JavaScript 代码通常不能与其他标签的代码交互。在本例中,每个浏览器标签都在自己的 V8 隔离中执行 JavaScript 代码。

Deno Deploy 云采取了类似的方法。每个部署都在自己的进程中、自己的 V8 隔离中执行自己的 JavaScript 代码。在将传入请求映射到部署后,运行器负责将请求传递给部署进程。如果请求的部署尚未运行,则运行器首先启动部署的新实例。为了节省资源,运行器还会终止一段时间未收到流量的部署。

图 3 显示了运行器进程和部署进程的高级架构视图。

Deploy runner and isolate pool architecture.
图 3. Deploy 运行器和隔离池架构。

安全注意事项

允许许多任意用户上传和执行不受信任的代码是一个非常困难的技术挑战,难以有效地解决。分层安全通常被认为是一种良好的实践,但在这种情况下,它是一种必要性,因为没有单一的安全机制可以解决 Deno Deploy 使用案例。

本节讨论了 Deploy 如何提高其安全姿态的一些方法。

使用更严格的 Deno 运行时。Deno 非常重视安全。开源的 Deno CLI 使用其内置的权限系统来锁定 JavaScript 应用程序可以执行的操作。Deno Deploy 更进一步,使用 Deno 的自定义版本,该版本更加严格。例如,部署可以读取自己的静态资产,但不能写入文件系统。

依赖 V8 沙箱。V8 被设计为在不友好的环境(浏览器)中运行不受信任的 JavaScript 代码。虽然 V8 隔离本身不能提供完美的沙箱,但它们在许多使用案例中做得足够好。V8 也是一个非常知名的公共项目,拥有公司支持。这导致对代码库进行持续审核、模糊测试和测试,以确保其安全。偶尔会报告 安全漏洞,谷歌对此非常重视。Deno 团队致力于及时更新 V8 版本,并遵循 V8 团队的 运行不受信任代码的建议

在单独的进程中运行部署。V8 隔离本身已经...隔离...彼此。Deno Deploy 更进一步,利用操作系统的帮助来提高每个部署的隔离性。

虚拟机管理程序监控资源利用率。运行器会持续跟踪所有正在运行的部署的指标。这是计费目的所必需的,但也允许运行器强制执行资源利用率配额。消耗过多资源的部署将被终止,以防止服务降级。

限制网络访问。云提供商和操作系统允许自定义和锁定网络访问。Deploy 还为内部控制平面流量和最终用户数据平面流量使用单独的网络。

限制允许的系统调用。作为额外的安全层,Deploy 使用 seccomp 过滤来限制用户代码允许执行的系统调用。

使用 Rust 作为实现语言。 在云部署方面,JavaScript 是云端的语言,而 Rust 是云的语言。Rust 强制执行内存安全,消除了整类错误。方便的是,Rust 还提供了与 C/C++ 同级的性能。

下一步是什么?

我们每天都在迭代和改进 Deno Deploy,越来越多的开发者和企业选择使用我们来运行生产就绪的应用程序和基础设施。我们相信我们的架构决策能够让他们专注于为用户构建,而不必担心安全或性能。

查看 Deno Deploy 并告诉我们您的想法!

您是否发现这些挑战很有趣?我们正在 招聘