Isolate Cloud 的剖析
如果您曾经尝试过 Deno Deploy,我们多租户分布式 JavaScript 云,您可能会理解这些热情洋溢的评论
(如果您还没有尝试过,立即部署一个 Fresh 站点!)
实现这种部署速度并非偶然。设计 Deno Deploy 是一个重新构想我们希望应用程序部署的开发者体验的机会,我们希望专注于速度和安全性。我们没有部署虚拟机或容器,而是选择了 V8 isolates,这使我们能够以显著更低的开销安全地运行不受信任的代码。
这篇博文解释了什么是 V8 isolates,并概述了 Deno Deploy isolate 云的主要架构组件。
Deno Deploy 项目和部署
在 Deno Deploy 中,用户和组织将其应用程序作为项目进行管理。Deploy 项目可以链接到 GitHub 仓库,从而在每次 git push
时重新部署代码。项目还允许应用程序管理环境变量、配置 TLS 证书以及关联域名。Deploy 仪表板还开箱即用地提供日志和分析视图,以提高应用程序行为的可见性。
如前所述,Deploy 项目可以链接到 GitHub 仓库,以便在每次将更改推送到后备仓库时更新正在运行的应用程序。在 Deploy 中,这些更新中的每一个都称为部署。部署是应用程序的不可变快照。每个部署都有一个唯一的 URL,可用于测试或以其他方式通过 Internet 与应用程序交互。一个项目可以有许多部署,但在任何给定时间,只有一个部署可以标记为生产环境。
图 1 显示了 deno.land 的部署仪表板。每个部署都显示其唯一的 deno.dev URL,以及用于创建部署的 git 提交信息。“Prod”标签指示当前在 deno.land 上生产环境中运行的部署。

将请求路由到部署
但是,用户的 HTTP 请求如何到达 Deno Deploy 云中正在运行的部署?
每个部署都通过一个或多个公共 URL 对 Internet 公开。在 DNS 解析期间,所有这些 URL 都映射到 Deno Deploy 的公共 IPv4 或 IPv6 地址。这确保了所有部署流量都被路由到 Deploy 服务器。
根据用户在世界上的位置,将所有流量发送到同一目的地可能非常低效。例如,如果用户位于澳大利亚,但所有 Deploy 服务器都位于美国东海岸,则每个 HTTP 请求(包括所有必要的 TCP 握手)都需要在全球范围内旅行数千英里。
由于 Deno Deploy 旨在在边缘运行 JavaScript,因此至关重要的是,请求应智能地路由到离用户最近的边缘位置。Deploy 利用 任播 路由在世界各地 30 多个边缘位置(如图 2 所示)之间共享相同的 IPv4 和 IPv6 地址。

一旦传入的请求被路由到适当的边缘位置,它就会被交给 runner 进程。顾名思义,runner 负责运行应用程序,包括接收流量并将其路由到正确的部署。
Deploy 维护域名和部署之间的映射表以实现此目的。根据使用的协议,HTTP/1.1 Host
标头或 HTTP/2 :authority
伪标头用于确定请求的目标域。然后,TLS 服务器名称指示 (SNI) 用于确定要用于连接的 TLS 证书。最后,runner 检查映射表以确定要将请求发送到的正确部署。(顺便说一句,Deploy HTTP 流量被重定向到 HTTPS,因此可以可靠地使用 SNI。)
Runner 作为 Isolate Hypervisor
在传统的云中,hypervisor 负责创建、运行、监控和销毁虚拟机。在 Deploy 中,runner 充当 isolate hypervisor,用途相同,但使用运行 V8 isolates 而不是虚拟机的进程。
在深入了解之前,我们需要了解什么是 isolate。V8,为 Deno、Google Chrome 和其他几个流行的运行时提供支持的 JavaScript 引擎,将 isolate 记录为
Isolate 表示 V8 引擎的隔离实例。V8 isolates 具有完全独立的状态。一个 isolate 中的对象不得在其他 isolates 中使用。
这意味着 isolate 是一个独特的执行上下文,包括代码、变量以及运行 JavaScript 应用程序所需的一切。
为了帮助理解这一点,请想象一个带有多个选项卡的浏览器窗口。每个选项卡都为单个网页执行 JavaScript,但每个选项卡的 JavaScript 代码通常无法与其他选项卡的代码交互。在此示例中,每个浏览器选项卡都在其自己的 V8 isolate 中执行 JavaScript 代码。
Deno Deploy 云采用类似的方法。每个部署都在其自身进程的自身 V8 isolate 中执行其自身的 JavaScript 代码。在传入的请求已映射到部署之后,runner 负责将请求传递给部署进程。如果请求的部署尚未运行,则 runner 首先启动部署的新实例。为了节省资源,runner 还会终止已运行一段时间而未接收到流量的部署。
图 3 显示了 runner 进程和部署进程的高级架构视图。

安全注意事项
允许许多任意用户上传和执行不受信任的代码是一项极其困难的技术挑战,需要有效解决。分层安全通常被认为是最佳实践,但在这种情况下,这是必要的,因为没有单一的安全机制可以解决 Deno Deploy 用例。
本节讨论 Deploy 改进其安全态势的一些方法。
使用更严格的 Deno 运行时。 Deno 非常重视安全性。开源 Deno CLI 使用其内置的权限系统来锁定 JavaScript 应用程序可以执行的操作。Deno Deploy 通过使用 Deno 的自定义构建版本进一步提升了安全性,该版本甚至更严格。例如,部署可以读取自己的静态资源,但不能写入文件系统。
依赖 V8 沙箱。 V8 旨在在充满敌意的环境(浏览器)中运行不受信任的 JavaScript 代码。虽然仅 V8 isolates 本身并不能提供完美的沙箱,但它们在许多用例中都做得足够好。V8 也是一个备受瞩目的公共项目,具有企业支持。这导致对代码库进行持续的审计、模糊测试和测试,以确保其安全。偶尔会报告 安全漏洞,Google 对此非常重视。Deno 团队致力于与 V8 版本保持同步,并遵循 V8 团队关于 运行不受信任代码的建议。
在单独的进程中运行部署。 V8 isolates 已经… 彼此隔离…。Deno Deploy 更进一步,并寻求操作系统的帮助来提高每个部署的隔离性。
Hypervisor 监控资源利用率。 Runner 持续跟踪所有正在运行的部署的指标。这对于计费目的来说是必要的,但也允许 runner 实施资源利用率配额。消耗过多资源的部署将被终止,以防止服务降级。
限制网络访问。 云提供商和操作系统允许自定义和锁定网络访问。Deploy 还为内部控制平面流量和最终用户数据平面流量采用单独的网络。
限制允许的系统调用。 作为额外的安全层,Deploy 使用 seccomp 过滤来限制用户代码可以执行的系统调用。
使用 Rust 作为实现语言。 对于 Deploy 而言,JavaScript 是云中的语言,而 Rust 是云的语言。Rust 强制执行内存安全,从而消除了一整类错误。方便的是,Rust 还提供了与 C/C++ 相媲美的性能。
下一步是什么?
每天,随着我们不断迭代和改进 Deno Deploy,越来越多的开发者和企业选择使用我们的产品来运行生产就绪的应用程序和基础设施。我们相信,我们的架构决策使他们能够专注于为用户构建,而无需担心安全性和性能。
查看 Deno Deploy,让我们知道您的想法!
您是否觉得解决这些挑战很有趣?我们正在招聘!