@jhleath: https://x.com/jhleath/status/2065408690992148698
摘要
作者解释了如何构建一个能够在恒定时间内每秒启动数百万个沙箱的计算平台,重点介绍了使用Cassandra和S3进行解耦调度和能力聚合。
查看缓存全文
缓存时间: 2026/06/12 21:02
每秒恒定时间启动百万级沙盒
据说在未来,每个人都会有属于自己的沙盒公司,为期15分钟。我们来简化一下这件事——下面是我如何构建计算平台,实现每秒恒定时间启动数百万沙盒的精确方法。希望你能将其接入 Fable/Mythos,快速推出产品,这样你也能拥有自己的沙盒公司。
如何为性能而构建
我们需要实现两个关键属性,但它们相互矛盾。
首先是吞吐量。我们希望每秒能启动数百万个沙盒。实现这一属性的两个关键洞察是:第一,你需要巨大的容量来吸收这些沙盒启动;因此,你无法构建一个假定自己随时掌握世界完整状态的系统。第二,你显然不能让系统的任何部分串行化——两个不同的沙盒启动必须完全独立地执行,无锁。
其次是延迟。通常,沙盒平台倾向于使用运行沙盒的机器本地的各种缓存来实现快速启动。但对我们来说,这是个坏主意,因为这会开始将沙盒的放置位置与该沙盒的性能紧密耦合(例如,如果你有一个客户运行了一个沙盒,你需要让他们的下一个沙盒运行在同一台机器上才能获得良好的性能)。这样做的主要问题是,它导致调度器需要更多信息才能运作,从而降低了整体性能。
我们来逐一解决这些问题。
如何每秒启动百万级沙盒
首先研究如何设计一个能处理每秒百万级沙盒的调度器。我们将范围限定在纯粹让沙盒在任意主机上运行,然后在下一节处理延迟问题(理解沙盒局部性是我们明确不希望拥有的属性,因为它会造成瓶颈)。
要实现无瓶颈调度,我们需要两个重要的状态管理原语。
第一,数据库。你可能知道,我偏爱各种形式的 Cassandra(DynamoDB、BigTable),因为我喜欢它带来的性质:获取 O(~1) 次查找,且 IOPs 可轻松随运行 Cassandra 的集群规模线性扩展。缺点是我们需要非常小心地预先规划查询,因为之后无法更改。
第二,存储。如果你不是我,你可以用 @archildata 来做这个;但由于我们必须避免服务中的循环依赖,我直接使用 S3。这让我们免费获得扩展能力,但缺点是任何对 S3 的请求都不能处于热路径中,因为 100ms 的读取对于我们想要启动的沙盒来说太慢了。
调度器的目的是收集每台主机在任何时间运行了多少个沙盒的信息,这样在调度时我们可以快速选择放置位置。这意味着我们希望在调度之前就聚合好每台主机的容量,所以引入一个专门的服务。
假设我们运行一个由 100 万台运行时服务器组成的集群来容纳所有这些沙盒,它们每 15 秒报告一次容量,那么我们将需要处理接近 7 万次请求/秒。虽然高,但肯定可以实现。
我们希望将每台主机的容量存储在 Cassandra 中,以便后续在需要将某物放置到主机上时方便查找。我们希望使用一个在搜索空间上分布均匀的主键——所以假设我们需要将每台主机的 IP 地址映射到某种随机唯一标识符,如 UUID。
我们应该让容量聚合服务负责将这个映射上传到像 S3 这样的地方。假设我们有 64 位服务器标识符和 32 位 IP 地址,这个映射大约为 12 MB。同样,虽然大,但肯定可以做到——如果需要,我们还可以将其拆分为不同的页面。
我们的容量聚合服务还将负责新主机的注册:只需将主机添加到集群中,它就开始报告容量,我们的服务将其添加到 Cassandra。因此,我们需要一个“容量服务“主机(需要进行某种领导者选举)负责定期扫描 Cassandra 并将 UUID 到 IP 地址的映射放入 S3;但由于主机更换速度较慢,这可以在分钟级别完成。类似地,只要选中死亡主机的概率较低,我们不必立即检测到它们;所以假设该服务器中有一个慢速进程扫描我们的 Cassandra 表,查找长时间未发送容量的服务器并将其移除。
将所有这些集中到一个服务中的好处是,它还可以负责我们可能需要的许多其他功能,比如让一台存活的主机静默,以便我们可以在不干扰客户工作负载的情况下进行部署。
现在来看实际的主机选择路径:
人们常给微服务差评,但它是构建可无限扩展的东西的超级简单方式,而且这段代码可能不到 1 万行(虽然我们不再关心这个了)。
我们的 API 服务将在后台定期(分钟级别)从存储在 S3 的映射文件中刷新主机列表。这将让我们对哪些主机存活有一个稍微过时的视图(你会注意到“稍微过时“是一个常见主题)。
当客户请求一个沙盒时,我们可以从这个列表中均匀随机地选择。我们会随机挑选 2 个(或者你喜欢的“N 中选优“数量),然后查询 Cassandra 获取这两台主机的稍微过时的容量信息(假设约 10ms)。我们会选择容量最大的主机,并尝试将沙盒放置到那台主机上。如果失败(由于容量或健康原因),我们将向容量服务报告失败,以便采取相应措施(更新或移除主机)。如果失败,我们会尝试我们选择的另一台主机,或者重新选择。
这给我们带来了 10ms 的 p50 调度延迟,以及根据我们选择坏主机的频率而略高的 p99。根据你作为平台的目标,你可以通过以下方式调整选择坏主机的概率:选择更多或更少的主机进行查询,更新容量信息更频繁或更少,以及通过总是报告低于实际主机容量的值来故意保留一些容量。
这条路径中没有任何形式的锁,因此我们可以在大约 10ms 内每秒启动一百万个沙盒。接下来是什么?
如何在恒定时间内启动沙盒
下一个常见挑战是如何做到无论沙盒放置在哪个主机上,启动延迟都不糟糕。我们刚刚构建了一个不考虑所选主机任何约束的放置服务,所以我们需要确保无论沙盒落在哪个主机上,都能在相同时间内启动。
实际上启动沙盒通常不是问题,因为你可以通过总是启动它们来轻松获得恒定时间。另一种极端是,你可以像 @archildata 那样,让平台可能运行的所有沙盒都预先启动,并将客户锁定在少数几种形状的选择中(我们可以通过报告更复杂的容量信息向调度器发出信号)。读者可以自行决定采用哪种方式。
实际上,沙盒平台通常的问题在于用户想要在主机上使用的镜像。用户不太擅长根据系统约束来构建工作负载,所以尽管我们都希望所有容器镜像只有 10 MB,但事实证明生产中的大多数容器镜像大约为 500 MB 到 2 GB。
这就带来了问题。容器平台的常见构建方式是用户将容器镜像“准备“或上传到某种注册表中(通常是基于 S3 的存储)。当他们在主机上启动容器时,主机会将镜像拉取到本地缓存。
这使得热启动(在同一主机上调度同一容器)非常快(镜像已经在本地),但冷启动非常糟糕。如果直接从 S3 下载(80 MB/s),要下载一个 2 GB 的镜像到运行时主机可能需要近 25 秒。当热启动可以 0 秒完成时,这对一致性来说非常不利。
因此,许多系统最终试图进行镜像感知的放置,以最大化用户的容器落在已完成下载的主机上的概率。这当然会给放置过程带来瓶颈,最终限制系统启动容器的速度。
更糟糕的是,大多数容器启动实际上并不需要读取镜像 100% 的字节才能启动,因此懒加载方法可以显著改善这些冷启动时间。
幸运的是,在 2026 年,我们可以使用高速文件存储(如 @archildata)来直接托管镜像,因为它们提供了从所有主机在线访问数据的能力,而无需下载步骤。在这种模式下,我们将客户想要的所有容器镜像(通过“准备步骤“)放到他们已经拥有的文件系统上。
与容量管理(这是运营沙盒服务的问题)不同,我们确实可以使用 Archil 进行镜像存储,因为我们将这些镜像视为属于每个用户,这意味着他们可以使用自己已有的 Archil 磁盘来保存镜像。
由于这是解耦的存储,服务中所有 100 万台主机都能够以相同速度访问数据,并且只读取它们需要的字节。即使沙盒已经启动,我们也可以轻松地告诉 Linux 在“启动“过程中从解耦存储系统切换到容器命名空间。
这意味着我们现在可以在相同时间内,在任何主机上启动任何容器,无论容器想要使用多大的镜像。
让我们看看整个架构图。
你可能已经意识到,构建这个东西实际上并不复杂。我认为“调度“被诟病困难的核心原因是许多随机因素:
- 我认为人们习惯于用 Kubernetes 来思考问题,而 Kubernetes 并非专门为容器放置和扩展而设计
- 我怀疑许多系统设计依赖关系型数据库,而关系型数据库开箱即用扩展性更具挑战性,容易遇到问题
- 我相信今天正在构建的许多调度器(尤其是考虑到构建沙盒的竞赛)可能试图实现完全一致的容量管理,而这在高度扩展时是不可能的
- 人们不会立即想到使用像 @archildata 这样的在线共享存储,而是倾向于尝试从 S3 等地方上传+下载东西(速度慢)
如果你想要任何代码——将 Docker 镜像准备成高效格式存储在 Archil(或其他文件存储)上,或者“启动“该 Docker 镜像(无需安装 Docker)的代码——请私信我,我会发给你。
总之,希望你按照这个蓝图去建立一家沙盒公司。显然,除了让容器使用 Docker 镜像在主机上运行之外,还有很多事情要做,但我认为其他一切对你和 Fable 来说应该相对简单。
如果你把这个喂给你的 Claude,然后有什么东西冒出来,请告诉我!
相似文章
@motatoeshq: 我们如何将 opencomputer.dev 扩展到 100 万个沙盒
OpenComputer 为 AI 智能体提供长期运行、持久化的云虚拟机,支持有状态、始终在线的计算,并允许动态调整资源,作为临时沙盒的替代方案。
@steren: 今天我们正式发布 Cloud Run 沙箱。这里,我在5秒内启动、执行并停止1000个沙箱,平均延迟约…
Google 正式发布 Cloud Run 沙箱,展示了在5秒内启动、执行并停止1000个沙箱的能力,平均延迟为500毫秒。
@latentspacepod: Daytona 的 Agent-Native 计算:60毫秒沙箱,75秒内启动5万个沙箱,每日85万次运行,RL/评估,CLI优于MCP,以及终结…
Daytona 首席执行官 Ivan Burazin 讨论了他们的 Agent-Native 计算平台,该平台提供60毫秒沙箱、有状态快照,并支持 RL/评估,标志着从本地开发到基于云的代理基础设施的转变。
@larsencc: https://x.com/larsencc/status/2053862900289470765
本文详解了开源 browser-use 库的生产架构,阐述了如何利用 AWS Lambda、SQS 和 S3 扩展浏览器代理,实现状态管理与重试机制。
@zongheng_yang: 沙盒正风靡一时(Modal、E2B、AWS等)。多数AI团队支付超过4倍的成本,在别人的机器上运行沙盒…
SkyPilot Sandboxes 允许AI团队在自己的集群上运行沙盒,相比Modal可节省4-10倍成本,支持亚秒级启动和温池。