如何在EC2中运行Firecracker虚拟机并在1秒内启动浏览器
摘要
Browser Use使用常规EC2上的Firecracker微虚拟机重构了其云浏览器基础设施,实现了低于400毫秒的冷启动,并将每个浏览器小时的成本从0.06美元降至0.02美元,同时改善了隔离性和自动扩缩容能力。
暂无内容
查看缓存全文
缓存时间: 2026/06/17 17:42
# 我们如何让云浏览器成本降低 3 倍、速度提升 3 倍
来源:https://browser-use.com/posts/firecracker-browser-infra
Browser Use:我们如何让云浏览器成本降低 3 倍——每个浏览器都是运行在普通 EC2 上的独立 Firecracker 微型虚拟机,每浏览器小时仅需 0.02 美元,VM 冷启动时间低于 400 毫秒,且支持无头隐身。
我们的云浏览器需要同时做到三件事:快速启动、保持隔离、成本低廉。这就是为什么我们重建了 Browser Use Cloud,使得新会话在一秒内启动,每浏览器小时成本从 0.06 美元降至 0.02 美元。
这说起来容易做起来难。一个浏览器包含 Chromium、文件系统、Cookie、缓存、代理设置、下载内容,有时还有一个已登录的客户会话。如果一个浏览器能读取另一个浏览器的状态,就会产生安全问题。
通常的解决方案是虚拟机(VM)。虚拟机就是计算机中的计算机:它拥有独立的 CPU、内存、磁盘和网络设备。它与宿主机上的其他一切隔离,如果浏览器崩溃、泄露信息或遭受攻击,损害只局限在虚拟机内部。
然而,对于云浏览器来说,普通虚拟机过于笨重。我们需要不断创建它们,有时一次就是数千个,并在会话结束后立即销毁。如果每个浏览器都需要一个缓慢、昂贵的虚拟机,那么产品也会变得缓慢和昂贵。
我们面临的问题是:能否在不让用户等待或付费的情况下,为每个浏览器分配它自己的虚拟机?现在,我们通过 Firecracker(一种轻量级虚拟机系统)实现了这一点。
每个 Browser Use Cloud 会话都在自己独立的微型虚拟机中运行。这些虚拟机运行在 EC2(亚马逊的云服务器租赁服务)上。
这就是不寻常之处。Firecracker 通常运行在裸金属服务器上,你需要租赁整台物理机。为了降低客户成本,我们将其运行在普通 EC2 上,而 AWS 已经把你的服务器放在了一个虚拟机内部。
按理说这应该会很慢。嵌套虚拟化会使内存和 CPU 操作更昂贵,而且 Chromium 启动也需要时间。本文就是要讲述我们如何让这个架构变得快速高效。
但首先,我们为什么要重建基础设施?
一个三圆维恩图,分别代表“快速启动”、“隔离”和“低成本”,三圆重叠处表示同时实现三者。同时做到快速、隔离和低成本是很困难的。
## 为什么我们放弃了 Unikernel
我们过去使用 **Unikraft**(https://github.com/unikraft/unikraft)来运行云浏览器,它会构建一种称为 **unikernel** 的、类似虚拟机的小型容器。Unikernel 不启动完整的 Linux 系统,而是加载一个为你目的构建的小型镜像。Unikernel 启动速度快,并且在空闲时成本低廉,因为你可以在不使用时将其关闭。
Unikraft 在浏览器闲置时关闭方面表现良好,但在流量激增时快速增加浏览器数量方面表现不佳。如果突然有更多用户同时请求浏览器,就需要快速扩展浏览器容量。Unikraft 没有良好的内置自动缩放功能,因此需要工程师手动修改变量,手动添加更多实例。
当流量突增时,系统不会自动响应,而是需要人工调整。这导致了问题:一次负载测试导致生产环境宕机 45 分钟。因此,我们基于 **Firecracker**(https://github.com/firecracker-microvm/firecracker)重建了我们的架构。
两张折线图分别比较容量与需求。使用 Unikraft 时,容量在流量高峰期间保持平缓,滞后于需求,导致宕机;重建后,容量自动跟踪需求。Unikraft 需要工程师手动添加容量,因此在流量高峰时滞后,导致崩溃;重建后,容量自动跟踪需求。
Firecracker 提供了一个层,用于创建、监控和运行虚拟机。它为每个虚拟机分配 CPU、内存、磁盘和网络设备,并使其与宿主机及其他虚拟机隔离。
## 教会浏览器自行扩展
Firecracker 为每个浏览器提供了独立的虚拟机。但它并没有从根本上解决导致旧系统崩溃的问题:决定运行多少个虚拟机、将它们放在哪里,以及何时增加更多。
因此,我们构建了自己的**控制平面**。控制平面监控我们的浏览器集群,并决定是否应该扩展或缩减。
当用户请求一个浏览器时,控制平面会选择一个有空闲空间的机器。当流量增加时,它会启动更多机器。当流量减少时,它会停止向我们希望移除的机器发送新的浏览器。
它实时检查集群的状态。这比等待 AWS 的监控服务 CloudWatch(通常在一分钟的时间窗口内做出反应)要快得多。它还能知道通用指标无法获知的信息:仍在启动中的浏览器、我们正尝试移除的机器,以及不应接收新会话的机器。
用户代码通过边缘路由器连接到控制平面,控制平面将会话发送到有空闲空间的 EC2 主机,同时将正在排空的主机排除在外。请求从用户代码经过无状态边缘路由器;控制平面选择一个有空闲空间的 EC2 主机,并排除正在排空的主机。
## 为什么我们在虚拟机内部运行虚拟机
有了控制平面后,下一个问题就是它应该添加什么样的机器。
在 AWS 上运行 Firecracker 的常规方式是使用 `.metal` 实例。这意味着你租赁整台物理服务器,Firecracker 直接运行在上面。
我们选择了普通的 EC2。普通 EC2 机器获取速度更快,保持运行的成本也更低。我们的宿主机从预构建的镜像启动,并在启动后约 30 秒内开始提供浏览器服务。我们添加宿主机的速度越快,需要付费的空闲容量就越少,从而传递给客户的成本也就越低。
但问题是,普通 EC2 本身就是一个虚拟机。AWS 在我们的宿主机内部运行其自身的隔离层,然后我们在该宿主机内部运行浏览器虚拟机。换句话说,每个浏览器都是一个虚拟机内部的虚拟机。
这不是 Firecracker 的常规用法。当浏览器虚拟机需要宿主机协助时,请求需要经过两层虚拟机而不是一层,从而增加延迟。
我们决定这个权衡是值得的,因为普通 EC2 为我们提供了更快的扩展速度和更低的成本。为了减轻嵌套虚拟化的影响,我们专注于让 Firecracker 尽可能快速。
在 .metal 宿主机上,浏览器虚拟机运行在物理服务器上的 Firecracker 中;在普通 EC2 上,多了一层 AWS 虚拟机监控器,因此页面错误可能跨越两层虚拟机层。在普通 EC2 上,浏览器虚拟机位于额外的 AWS 虚拟机监控器层之上,因此页面错误可能跨越两层虚拟机层。
## 从请求到可用的浏览器
当用户请求一个浏览器时,控制平面会选择一个有空闲空间的机器。该机器恢复一个已保存的浏览器虚拟机,在其中启动 Chromium,等待 Chromium 准备好被控制,然后返回一个连接 URL。
该 URL 就是用户的代理要连接的地址。Browser Use 通过 WebSocket 使用 Chrome DevTools 协议(CDP)来控制 Chromium。CDP 是 Chrome 的远程控制 API:点击这个按钮,输入这段文本,读取这个页面,截取这张截图。
五步流程:选择宿主机、从快照恢复虚拟机、启动 Chromium、达到 CDP 就绪状态、返回连接 URL 供代理通过 CDP 连接。每个会话分五步:控制平面选择宿主机,虚拟机从快照恢复,Chromium 启动,一旦达到 CDP 就绪状态,代理通过 WebSocket 连接。
有三件事导致这一过程耗时较长:恢复虚拟机的内存、启动 Chromium、以及保持浏览器的隐身性以避免被反机器人安全机制检测到。
## 第一个瓶颈:内存
第一个瓶颈是内存。
生产环境中的浏览器并非从头启动。我们是从一个快照恢复它:一个已经启动并暂停在 Chromium 即将启动之前的虚拟机。恢复虚拟机比启动它快得多。
但我们的首次恢复仍然太慢。当恢复的虚拟机首次访问内存时,宿主机必须将该内存映射回来。这个事件称为页面错误。在嵌套虚拟机中,每个页面错误都很昂贵,因为它可能跨越两层虚拟机层。
在早期的冷启动过程中,页面错误占了所有虚拟机退出的 72%。从恢复到 CDP 就绪的浏览器需要 9.8 秒。
解决方案是以更大的块来映射内存。以前,虚拟机以 4KB 页面恢复内存。现在,它使用 2MB 页面。每个页面覆盖的内存是原来的 512 倍,因此浏览器在唤醒过程中触发的页面错误大大减少。页面错误越少,意味着穿越嵌套虚拟机层的次数越少。
柱状图:使用 4KB 页面时,从恢复到浏览器就绪需要 9.8 秒;使用 2MB 页面后,缩短到 3.1 秒;缺失内存暂停次数从约 100,000 次降到约 1,100 次。使用 2MB 页面映射内存使浏览器启动更快。
我们还通过一个自定义的 `userfaultfd`(处理缺失内存页面的 Linux API)处理程序来处理页面错误。在虚拟机开始运行之前,我们的处理程序会加载 Chromium 最可能最先访问的内存。
我们的处理程序可以防止 Chromium 在启动时收到大量页面错误。宿主机已经加载了热页面,而剩余的页面在浏览器需要它们之前就已经准备好了。
这些改动将从恢复虚拟机到浏览器准备好接受命令的时间从 9.8 秒缩短到了 3.1 秒。此外,浏览器虚拟机因需要宿主机处理缺失内存而暂停的次数从每次恢复约 100,000 次下降到约 1,100 次,下降了约 91 倍。
我们还做了一些更小的优化。虚拟机曾花费 500 毫秒寻找一个不存在的旧 PS/2 键盘。我们禁用了这个检查。
此外,我们改变了宿主机等待浏览器就绪的方式。以前,宿主机不断通过 HTTP 请求轮询虚拟机。这造成了额外的虚拟机退出,即浏览器虚拟机必须暂停以便宿主机为其处理工作。
现在,浏览器驱动程序将其就绪消息写入日志,宿主机通过 `vsock`(宿主机与虚拟机之间的快速通信通道)读取该日志。宿主机在不到一毫秒内就能看到就绪消息。
## 第二个瓶颈:Chromium 启动
下一个瓶颈是 CPU。
Chromium 启动时,它非常饥饿且要求很高。它会同时创建渲染器、合成器和 V8 隔离环境。之后,浏览器自动化工作就要安静得多。代理点击、等待、读取、再次点击。
由于 Chromium 启动后变得更加安静,我们可以将许多浏览器打包到同一个实例中。一个宿主机可以容纳大量浏览器,因为浏览器大部分时间都在等待:等待页面加载、网络响应或下一个代理操作。
一个由小方块组成的网格,代表运行在一个宿主机上的浏览器虚拟机。打包在单个普通 EC2 宿主机上的浏览器虚拟机。
我们分两个阶段处理启动峰值。当浏览器恢复且 Chromium 启动时,我们将其虚拟 CPU 保持未固定状态。这意味着 Linux 可以在整个宿主机上移动浏览器的 CPU 工作,而不是将其锁定在固定核心上。这样可以将峰值分散。
一旦浏览器报告就绪,我们就将这些虚拟 CPU 固定到稳定的核心上。这意味着浏览器虚拟机现在在特定核心上运行。稳定的放置让我们可以在不知道确切负载的情况下,在同一宿主机上打包更多浏览器。我们知道哪些核心已被占用,哪些仍有空间,以及哪些浏览器可能相互干扰。
启动阶段就像让一群人通过所有敞开的门进入。一旦所有人都在里面,分配座位效果更好。
从一开始就固定核心会使情况更糟。当许多浏览器同时启动时,它们会涌向相同的热核心,导致某些启动失败。
我们还对超线程变得谨慎。一个物理 CPU 核心通常表现为两个逻辑 CPU,称为兄弟线程。这些兄弟线程仍然共享同一个物理核心。如果两个浏览器虚拟机各占一个兄弟线程,它们会争夺同一个核心。在嵌套环境下,这种争用表现为启动失败。为了防止这种情况,每个浏览器现在都获得其使用的物理核心的两个兄弟线程。
最后,我们为每个固定的 vCPU 线程赋予实时优先级。这告诉 Linux,当浏览器虚拟机需要 CPU 时立即运行,而不是将其排在次要工作之后。在此更改之前,一个 1000 浏览器的测试在创建后不久就丢失了 17% 的会话。更改后,同样的测试零丢失。
两个阶段:Chromium 启动期间 vCPU 未固定,启动峰值分散到各个核心;浏览器就绪后,vCPU 固定到稳定核心。vCPU 在 Chromium 启动峰值期间保持未固定,以便工作分散到整个宿主机,然后在浏览器就绪后固定到稳定核心。
## 无需屏幕保持隐身
最后一个瓶颈是隐身。
无头浏览器运行时没有可见窗口。有头浏览器运行时就像你笔记本电脑上的浏览器一样,有窗口、图形和渲染帧。
普通的无头 Chromium 很容易被具有反机器人措施的网站检测到。根据我们的隐身基准测试,普通无头 Chromium 避免被网站屏蔽的概率仅为 2%。同样的 Chromium,有头且带有可见窗口,仅通过渲染内容就能避免 50% 的屏蔽。
这就是为什么大多数提供商运行有头浏览器。他们为显示服务器、GPU 和合成器付费,为没有人看的屏幕绘制帧。
我们完全以无头方式运行浏览器。这之所以成为可能,是因为我们修改了浏览器本身。
第一个组件是我们的 Chromium 分支。许多隐身工具通过在浏览器启动后向每个页面注入 JavaScript 来隐藏自动化。例如,它们覆盖浏览器属性如 `navigator.webdriver`(一个告诉网站浏览器是否被自动化控制的标志),使页面看到 `false` 而不是 `true`。网站通常可以检测到这些值被覆盖。为避免这种情况,我们在最低级别修改 Chromium,这样我们的补丁从一开始就不会暴露。
第二个组件是指纹识别。浏览器指纹由网站读取的关于你的浏览器和机器的详细信息组成,包括操作系统、屏幕尺寸、字体、图形、输出、音频、时区、语言以及数百个其他细节。检测机器人的系统会检查这些细节是否像真实用户的浏览器,还是伪造的自动化环境。我们在 macOS、Windows 和 Linux 上使用数万个真实指纹。
在我们的隐身基准测试中,我们的浏览器避免屏蔽的概率为 81%,在 Halluminate BrowserBench 上为 84.8%,是所有提供商中最高的。由于没有显示,浏览器运行成本更低,也更容易扩展。
水平柱状图显示隐身基准测试中的屏蔽避免率:Browser Use 81%、Anchor 77%、Onkernel 67%、Browserless 54%、Steel 47%、Browserbase 42%、Hyperbrowser 40%、普通无头 2%。在我们的隐身基准测试中,我们的完全无头浏览器以 81% 的屏蔽避免率领先所有提供商。
## 连接到正确的浏览器
一旦浏览器就绪,用户通过 CDP 连接到它。公共 URL 是一个 WebSocket URL。
在浏览器集群前面是简单的边缘路由器。路由器获取 WebSocket
相似文章
@browser_use: https://x.com/browser_use/status/2066911791360422071
Browser Use Cloud 使用 Firecracker 重建基础设施,将浏览器会话成本从每小时 0.06 美元降至 0.02 美元,同时实现亚秒级启动时间,并保持隔离性和可扩展性。
@gregpr07: 我们构建了新的浏览器基础设施。比其他自定义Firecracker虚拟机便宜3-6倍。自定义Chromium分支。裸金属。>$0.02/小…
Greg pr07 宣布了一种新的浏览器基础设施,采用自定义Firecracker虚拟机、Chromium分支和裸金属,成本降低高达6倍,亚秒级冷启动,支持10,000个并发浏览器。
@browser_use:不到1秒启动25个浏览器,尽情享受
Browser Use 推出全新浏览器基础设施服务,具备亚秒级冷启动、更低成本(每小时0.02美元)以及无限扩展能力,现已面向开发者上线。
@Alezander907: Aitor @reformedot 是我们的100x工程师。他每周工作80小时,从零构建了新的浏览器基础设施:自定义…
Aitor 通过使用自定义的 Chromium 分支、Firecracker 虚拟机和自定义 Linux 内核,从零构建了新的浏览器基础设施,实现了成本降低3倍、无限扩展和最快的浏览器自动化性能。现在只需 $0.02/小时。
@heygurisingh: 卧槽……一个团队刚刚开源了一个 AWS 模拟器,仅用 13 MiB 内存就能在笔记本上运行整个云服务。……
Floci 是一个新开源的轻量级 AWS 模拟器,仅用 13 MiB 内存即可在笔记本上运行 45 项服务,为 LocalStack 等基于 Docker 的工具提供了更快速、更节省资源的替代方案。