Cloudflare Workers 把许多租户的代码放进同一个操作系统进程,每份代码各占一个 V8 isolate,不给每个租户单独起容器或 VM。按 Cloudflare 文档 How Workers works 的说法,isolate 的启动比容器或 VM 里的 Node 进程快约两个数量级,启动时的内存少一个数量级;代价是运行环境受限,隔离也要靠 V8 之外的几层补足。

同一时期的 Lambda@Edge#

和 Workers 同一时期的 AWS Lambda@Edge 在 2017-07-17 GA。按 AWS 2021-05-03 发布 CloudFront Functions 时的说法,Lambda@Edge 跑在 13 个 regional edge cache 上,用的是 VM 级隔离;同一篇介绍的 CloudFront Functions 用的是进程级隔离,跑在 218+ 个 edge location 上。Lambda@Edge 有文件系统和网络访问,但运行时只有 Node.js 和 Python,也不支持容器镜像和 arm64(见 Lambda@Edge 的限制)。

Cloudflare Workers 晚两个多月公布(2017-09-29),2018-03-13 对所有用户开放。

Cloudflare Workers 的 V8 isolate#

V8 里的 isolate 是一个带独立堆的虚拟机实例,context 是 isolate 里的执行环境,一个 isolate 里可以有多个 context(见 V8 的 embedder 文档)。Workers 用的是 isolate 这一层隔离,不同租户的代码在不同 isolate 里,堆和 GC 各自分开。

flowchart TD subgraph "Lambda@Edge:VM 级隔离" VM1["VM"] --> N1["Node.js 进程"] --> C1["租户 A 代码"] VM2["VM"] --> N2["Node.js 进程"] --> C2["租户 B 代码"] end subgraph "Cloudflare Workers:多个租户共用一个进程" Host["一台机器"] --> P1["运行时进程(cordon:低信任)"] Host --> P2["运行时进程(cordon:高信任)"] P1 --> IA["isolate(租户 A)"] P1 --> IB["isolate(租户 B)"] P2 --> IC["isolate(租户 C)"] end

启动一个 microVM 要先引导客户机内核,Firecracker 2018-11-26 开源时给的数字是 microVM 最快 125 ms 启动(见 AWS 的发布文);Cloudflare 2018-11-09 的 Cloud Computing without Containers 把 Lambda 的冷启动写成 500 ms 到 10 s,isolate 共用已经在跑的运行时进程,同一篇给的启动时间约 5 ms。内存上,同一篇的对照是一个什么都不做的 Node Lambda 占 35 MB,isolate 共享运行时之后约 3 MB,一个进程可以跑几百到几千个 isolate。

isolate 也不是每个请求新建一个。一个 Worker 实例可以在单线程事件循环里处理多个请求,包括并发请求;触及限制或机器资源紧张时,运行时会驱逐 isolate(见 How Workers works)。Fastly Compute 的做法相反,默认给每个请求起一个新的 WebAssembly sandbox(见 Sandbox lifecycle)。

V8 isolate 的限制#

换来冷启动和密度的同时,Workers 有四处限制:语言、系统 API、CPU 和内存上限、安全边界。

只能运行 JavaScript 和 WebAssembly#

代码要跑在 V8 里,就只能是 JavaScript 或 WebAssembly,编译成机器码的 Go 程序不能直接跑。Python Workers 是把 CPython 编译成 WebAssembly(Pyodide),再在 isolate 里解释执行 Python 代码(见 How Python Workers work);文档列出的可用包是纯 Python 包、PyPI 上的 PyEmscripten 包和 Pyodide 自带的包(见 Python packages),依赖原生库、又不在后两类里的包用不了。

系统 API 和 Node.js 兼容#

isolate 里没有真实的磁盘,不能起子进程,也不能接受入站 TCP 连接。打开 nodejs_compat 之后,Workers 补上了一部分 Node.js 核心模块:node:fs 是一套内存文件系统,/bundle 只读,/tmp 每个请求一份、不跨请求保留,兼容日期 2025-09-01 起默认可用(见 node:fs);node:child_process 只是一个不能工作的 stub,兼容日期 2026-03-17 起才默认可以 import(见 Node.js compatibility)。网络这边,Cloudflare 2025-09-25 的 A year of improving Node.js compatibility in Cloudflare Workers 说 node:net 建在 Workers 已有的 socket API 上,还不能用 net.createServer() 起 TCP 服务端,node:http 的客户端和服务端都已实现,Express、Koa 这类框架能跑。依赖真实磁盘、子进程或入站 TCP 连接的 npm 包仍然跑不起来。

网络和加密方面,Workers 实现的是 Web 标准 API,比如 fetch() 和 Web Crypto。2022-05-09,Cloudflare 联合 Vercel、Shopify 以及 Node.js、Deno 的核心贡献者发起了 WinterCG,目标是让服务端 JavaScript 运行时实现同一组 Web API;2025-01-10 宣布移到 Ecma,成为 TC55(WinterTC),公告里点名的运行时是 Node.js、Deno 和 Cloudflare Workers。

CPU 时间和内存上限#

容器的 CPU 和内存上限由 Linux cgroups 按进程组施加。Workers 的上限按请求和 isolate 计:CPU 时间在 Free 计划是每个请求 10 ms,Paid 计划默认 30 s、最多可以调到 5 min,等待 fetch()、KV 读取这类 I/O 的时间不算;超出后返回 Error 1102。每个 isolate 的内存上限是 128 MB,JavaScript 堆和 WebAssembly 分配都算在内(见 Workers limits)。

安全边界#

同一个进程里的多个 isolate 共享地址空间,Cloudflare 的 security model 文档引了 V8 团队的说法,V8 自己挡不住 Spectre。Workers 因此在 V8 之外又加了几层。一台机器上跑多个运行时实例(cordon),按信任等级把 Worker 分开放;进程启动后、加载 isolate 之前先套上 Linux namespaces 和 seccomp,文件系统是空的;执行期间 Date.now() 不前进,也不允许多线程和共享内存。Cloudflare 2020-07-29 的安全模型文章还提到,行为可疑的 Worker 会被重新调度到单独的进程,V8 补丁从发布到上线一般不到 24 小时。同一篇也给了不这么做的代价,改成严格的每租户一个进程,CPU 开销很容易到共享进程的 10 倍。

小结#

Workers 用同进程的 V8 isolate 代替每租户一个容器或 VM,按 Cloudflare 2018 年给的数字,冷启动约 5 ms、每个 isolate 约 3 MB 内存,代价是代码只能是 JavaScript 或 WebAssembly、文件系统只在内存里、CPU 时间按请求计量,隔离要靠 cordon 和进程级沙箱补足。选型时先看应用依赖什么:离不开原生二进制、真实磁盘或子进程的,放进 Fly Machines 这类 microVM 平台(应用代码跑在 Firecracker microVM 里),或者 Cloudflare 的 Containers(2025-06-24 公测,2026-04-13 GA,每个容器实例跑在单独的 Firecracker microVM 里);只做请求改写、鉴权这类短逻辑的,用 isolate 就够了。

参考资料#