边缘节点上跑多租户代码,常见的隔离做法有三种:每个函数一个 VM(Lambda@Edge),多个租户共用一个进程、各占一个 V8 isolate(Cloudflare Workers),以及 Firecracker microVM(Fly Machines、Cloudflare Containers)。三种做法出现的时间相互重叠:Lambda@Edge 2017-07-17 GA,Workers 2017-09-29 公布,Firecracker 2018-11-26 开源时已经在承载 AWS Lambda 和 Fargate。到现在三者仍然并存,区别在冷启动、单机密度,以及运行环境完整到什么程度。

每个函数一个 VM:Lambda@Edge#

通用计算上边缘之前,边缘节点上能写的逻辑主要是标记和规则。ESI(W3C 2001-08-04 的 Note)是一种基于 XML 的标记语言,让 CDN 节点按标记拼装页面片段;Varnish 的 VCL 用来写缓存和路由规则。Akamai 2002 年宣布要把 IBM 的 WebSphere 放到自己的边缘服务器上跑 servlet 和 JSP(见 The Register 的报道),是更早的一次通用计算尝试。

Lambda@Edge 跑在 CloudFront 的 regional edge cache 上。AWS 2021-05-03 介绍 CloudFront Functions 时写明,Lambda@Edge 用的是 VM 级隔离,当时部署在 13 个 regional edge cache 上,支持 Node.js 和 Python,有网络和文件系统访问;同一篇介绍的 CloudFront Functions 改用进程级隔离,跑在 218+ 个 edge location 上,代价是只能跑兼容 ECMAScript 5.1 的 JavaScript,没有网络和文件系统访问(见 Introducing CloudFront Functions)。

VM 级隔离的成本,能查到的对照数字来自 Cloudflare 2018-11-09 的 Cloud Computing without Containers:Lambda 起一个新的容器化进程要 500 ms 到 10 s,一个什么都不做的 Node Lambda 占 35 MB 内存。这是 Cloudflare 对 AWS Lambda 的描述,没有单独测 Lambda@Edge。

同一进程里的 V8 isolate:Cloudflare Workers#

Workers 把多个租户的代码放进同一个运行时进程,每份代码各占一个 V8 isolate,隔离机制、限制和安全模型见《Cloudflare Workers 的 V8 isolate 隔离模型》。按上面那篇 2018 年文章的数字,isolate 启动约 5 ms、约 3 MB 内存,一个进程能跑几百到几千个 isolate;代价是只能跑 JavaScript 和 WebAssembly,没有真实的文件系统、子进程和入站 TCP 连接。

这种运行时和 Node.js 不兼容,2022-05-09 Cloudflare 联合 Vercel、Shopify 以及 Node.js、Deno 的个人核心贡献者发起了 WinterCG,目标是让非浏览器的 JavaScript 运行时实现同一组 Web API;2025-01-10 它移到 Ecma,成为 TC55(WinterTC)。

边缘状态:KV、Durable Objects 和 D1#

计算放到边缘之后,数据库如果还在一个区域里,每次查询都要回源。Cloudflare 2023-09-28 介绍 Hyperdrive 时的说法是,数据库单次往返可能在 30 ms 到(经常是)300 ms 之间,新建一个连接还要七次以上往返。Cloudflare 按读写模式给出了三种存储,一致性和读延迟各有取舍:

flowchart TD subgraph "Workers KV:最终一致" KV_W["写入"] --> KV_C["中央存储"] KV_C -->|"按需拉取,缓存过期前可能读到旧值"| KV_R["各地缓存"] end subgraph "Durable Objects:同一 ID 只有一个实例" DO_C1["请求(地区 A)"] --> DO_Inst["Durable Object 实例
建在首次请求附近,单线程,自带存储"] DO_C2["请求(地区 B)"] --> DO_Inst end subgraph "D1:primary 加区域只读副本" D1_W["写入"] --> D1_Primary["primary(SQLite)"] D1_Primary -->|"WAL 异步复制"| D1_Replica["每个 D1 区域一个只读副本"] end

Workers KV#

KV 的写入先落到中央存储,不会主动推到每个地点;某个地点第一次读一个 key 时回中央拉取,之后从缓存读,文档把这叫 hybrid push/pull 复制(见 How KV works)。所以 KV 是最终一致的:写入在发起写的那个地点通常立刻可见,其他地点要等各自的缓存过期,文档写的是「up to 60 seconds or more」。读延迟方面,Cloudflare 2024-09-26 给的数据是,最热的那批 key(占全球 KV 请求的 40% 以上)从内存缓存返回,不到 1 ms,KV Worker 调用的 p90 在 12 ms 以内(见 We made Workers KV up to 3x faster)。文档把它定位在写得少、读得快且频繁的场景。

Durable Objects#

Durable Objects 2020-09-28 开始 closed beta,2021-11-15 GA。每个 Durable Object 有一个全局唯一的名字,从世界任何地方都能把请求发给它,实例建在第一次请求它的地点附近(见 What are Durable Objects);同一个 ID 同一时刻在全球只有一个实例在跑(见 Known issues;唯一性在事件开始和访问存储时检查,一个执行很久又一直不碰存储的事件,可能和别处新起的同 ID 实例短暂并存)。实例是单线程的,自带一份只有它自己能访问的存储,文档的描述是「durable, transactional, and strongly consistent」。文档举的例子是聊天室和多人游戏这类要在多个客户端之间协调同一份状态的场景。

D1#

D1 建在 SQLite 上,2022-05-11 公布(当时是 beta 报名),2024-04-01 GA。只读副本 2025-04-10 进入 public beta:开启后,D1 在它支持的每个区域(ENAM、WNAM、WEUR、EEUR、APAC、OC)各放一个只读副本,写入仍然全部转发给 primary(见 Read replication)。复制是异步的,primary 那边的 SQLite 跑在 WAL 模式,写进 WAL 的记录被转发给各个副本;客户端用 Sessions API 带上 bookmark,同一个会话里的读能保证顺序一致(见 Sequential consistency without borders)。副本按区域放,不在每个边缘节点上。

需要完整 Linux 的负载:Firecracker microVM#

依赖原生二进制、真实磁盘或子进程,或者要跑 Go、Java 写的完整服务,进不了 isolate。这类负载可以放进 Firecracker microVM:每个实例有自己的客户机内核,AWS 2018 年开源时给的数字是 microVM 最快 125 ms 启动。

Fly.io 把容器镜像转换成 Firecracker microVM 来跑(见 Docker without Docker),应用代码都在 Firecracker microVM 里(见 The Fly.io Architecture)。Fly 2022-05-24 介绍 Machines 时说,能在 300 ms 左右启动一个实例(见 Fly Machines)。

Cloudflare Containers 2025-06-24 公测,2026-04-13 GA。容器实例由 Workers 代码按需拉起和控制,每个实例跑在单独的 Firecracker microVM 里,有自己的内核和网络(见 Containers architecture)。冷启动按 FAQ 的说法是「often in the 1-3 second range」,取决于镜像大小和入口程序。

小结#

三种隔离做法现在都还在用,选哪一种看工作负载:

  • 请求改写、鉴权、轻量 SSR 这类短逻辑,用 V8 isolate(Cloudflare Workers),按 Cloudflare 的数字冷启动约 5 ms,代价是只能跑 JavaScript 和 WebAssembly。
  • 需要状态的,按读写模式选存储:写少读多、能接受最长约 60 s 旧值的放 KV;同一份状态并发写多、要强一致的放 Durable Objects;关系型数据用 D1,开了只读副本之后读走区域副本,写仍然回 primary。
  • 依赖原生二进制、真实磁盘、子进程,或者整套 Go、Java 服务,放 Firecracker microVM 平台(Fly Machines、Cloudflare Containers),冷启动从几百毫秒到几秒,随平台和镜像大小而变。

参考资料#