面向哲思的编程与架构
笔记哲思阅读动态搜索RSS 订阅
切换到深色模式
搜索
RSS 订阅
切换到深色模式
© 2026 Vic Chen. All rights reserved.CC BY-NC-ND 4.0
← 笔记
现代计算的基础设施(一):隔离的代价

现代计算的基础设施(一):隔离的代价

2026年4月3日3,76911分钟

从进程到容器,从 QEMU 到 Firecracker,从 VM 到 V8 Isolate——多租户隔离没有银弹,只有取舍:安全边界越硬,启动开销越大;资源密度越高,逃逸风险越高。这篇文章拆解 AWS Lambda、Cloudflare Workers、Vercel Functions 各自的隔离方案,梳理它们在安全性、冷启动、资源密度之间的不同选择。


目录
  • 1. 问题的本质
  • 2. 隔离方案的设计空间
  • 2.1 进程隔离:操作系统的默认答案
  • 2.2 容器:命名空间的组合
  • 2.3 microVM:最小化的硬件虚拟化
  • 2.3.1 QEMU 的问题
  • 2.3.2 Firecracker 的内部架构
  • 2.3.3 冷启动时间的分解
  • 2.4 V8 Isolate:在语言运行时层面隔离
  • 2.4.1 V8 Snapshot:微秒级冷启动的实现
  • 2.4.2 Spectre 侧信道:共享进程的代价
  • 3. 架构决策矩阵
  • 4. 架构师的设计决策
  • 4.1 热路径优化:预热池的设计
  • 4.2 资源限制的实施层
  • 4.3 网络隔离
  • 5. 本站情况
  • 6. 专线 SaaS 的映射
目录
  • 1. 问题的本质
  • 2. 隔离方案的设计空间
  • 2.1 进程隔离:操作系统的默认答案
  • 2.2 容器:命名空间的组合
  • 2.3 microVM:最小化的硬件虚拟化
  • 2.3.1 QEMU 的问题
  • 2.3.2 Firecracker 的内部架构
  • 2.3.3 冷启动时间的分解
  • 2.4 V8 Isolate:在语言运行时层面隔离
  • 2.4.1 V8 Snapshot:微秒级冷启动的实现
  • 2.4.2 Spectre 侧信道:共享进程的代价
  • 3. 架构决策矩阵
  • 4. 架构师的设计决策
  • 4.1 热路径优化:预热池的设计
  • 4.2 资源限制的实施层
  • 4.3 网络隔离
  • 5. 本站情况
  • 6. 专线 SaaS 的映射
目录
  1. 1. 问题的本质
  2. 2. 隔离方案的设计空间
  3. 2.1 进程隔离:操作系统的默认答案
  4. 2.2 容器:命名空间的组合
  5. 2.3 microVM:最小化的硬件虚拟化
  6. 2.3.1 QEMU 的问题
  7. 2.3.2 Firecracker 的内部架构
  8. 2.3.3 冷启动时间的分解
  9. 2.4 V8 Isolate:在语言运行时层面隔离
  10. 2.4.1 V8 Snapshot:微秒级冷启动的实现
  11. 2.4.2 Spectre 侧信道:共享进程的代价
  12. 3. 架构决策矩阵
  13. 4. 架构师的设计决策
  14. 4.1 热路径优化:预热池的设计
  15. 4.2 资源限制的实施层
  16. 4.3 网络隔离
  17. 5. 本站情况
  18. 6. 专线 SaaS 的映射
架构基础设施Serverless云原生
相关文章
  • 01
    现代计算的基础设施(五):一次请求的完整旅程2026/05
  • 02
    现代计算的基础设施(四):在边缘执行代码2026/04
  • 03
    现代计算的基础设施(二):并发的重新发现2026/04
← 上一篇给网站加朗读功能(一):从 Web Speech API 到云端 TTS 的完整实现
下一篇 →现代计算的基础设施(二):并发的重新发现

评论

© 2026 Vic Chen · 面向哲思的编程与架构CC BY-NC-ND 4.0

这篇文章从一个具体的工程问题出发:如果要运行大量用户上传的任意代码,安全、快、便宜——三个目标互相对立,该如何设计?

这是 AWS Lambda(Chen 注:AWS 的 FaaS(函数即服务)平台,2014 年发布,是 Serverless 计算的事实标准。底层使用 Firecracker microVM 隔离,按实际执行时间计费,不需要管理服务器。)、Cloudflare Workers(Chen 注:Cloudflare 的边缘计算平台,代码运行在全球 300+ 个 PoP 节点上,基于 V8 Isolate 隔离,冷启动 < 1ms。不是传统 Serverless,没有「区域」概念,请求直接在离用户最近的节点执行。)、Vercel Functions(Chen 注:Vercel 的计算平台,底层基于 Firecracker microVM,支持完整 Node.js 运行时。与 Next.js 深度集成,是本站 API 路由和 RSC 渲染的执行环境。) 每天面对的问题。他们给出了不同的答案,而这些答案背后的取舍逻辑,在任何需要多租户隔离的系统里都会反复出现——包括专线 SaaS 的私有化部署场景。

1. 问题的本质

在同一批机器上运行多租户代码,有三个互相对立的约束:

  • 安全性:租户之间不能互相读写内存,不能影响宿主机
  • 启动速度:用户触发函数调用,等待时间必须在可接受范围内
  • 资源密度:同一台机器要跑尽可能多的租户,控制成本

这三者构成一个三角,每种隔离方案都是在不同顶点上做权衡。

图1. 隔离方案的三角约束

没有方案能同时最优化三个维度。理解这个前提,才能理解为什么 AWS 和 Cloudflare 做出了截然不同的选择。


2. 隔离方案的设计空间

2.1 进程隔离:操作系统的默认答案

最直觉的隔离单元是操作系统进程。Linux 进程天然拥有独立的虚拟地址空间,内核通过内存管理单元(MMU)保证进程间内存不可见。fork() + exec() 启动一个新进程,操作系统负责隔离。

但进程隔离的问题在于共享内核。所有进程共用同一个 Linux 内核,内核漏洞可以被任意进程利用来逃逸。对于公有云的多租户场景,「共享内核」意味着一个租户的恶意代码可以通过内核漏洞影响其他租户甚至宿主机。

此外,启动一个能跑 Node.js 的进程需要几百毫秒,内存开销在几十到几百 MB,在 per-request 的 FaaS 模型下代价太高。

2.2 容器:命名空间的组合

容器并不是一项新技术,它是 Linux 内核若干特性的组合。

Linux 提供了六类命名空间(Namespace),每一类隔离一种资源视图:

命名空间隔离内容内核引入版本
PID进程 ID 视图3.8(2013)
Network网络接口、路由表、端口2.6.24(2008)
Mount文件系统挂载点2.4.19(2002)
UTS主机名、域名2.6.19(2006)
IPCSystem V IPC、POSIX 消息队列2.6.19(2006)
User用户 ID、组 ID 映射3.8(2013)

容器 = 进程 + 命名空间约束 + cgroups 资源限制。Docker 不过是把这些内核特性包装成了友好的 CLI。

容器比进程轻,因为镜像层共享,同一宿主机上多个容器可以复用相同的只读层。但根本问题没有变:共享内核。Dirty COW(CVE-2016-5195)、Runc 逃逸(CVE-2019-5736)表明容器逃逸漏洞的根源都在于内核被共享。

对于运行用户提交任意代码的场景,容器的隔离强度不够。

图2. 容器与共享内核
⚠

内核是共享的,也是最大的攻击面。

2.3 microVM:最小化的硬件虚拟化

要从根本上消除共享内核的风险,需要让每个租户运行独立的内核。传统虚拟机(VM)做到了这一点,但启动时间以分钟计,资源开销以 GB 计,用来处理 HTTP 请求完全不现实。

AWS 的答案是 Firecracker(Chen 注:AWS 2018 年开源的极简 VMM,用 Rust 编写,约 5 万行代码。专为 serverless 多租户设计,最小化设备模型(只有 virtio-net、virtio-block 等 5 种设备),冷启动约 125ms,每个 microVM 内存开销约 5MB。)——一个为 serverless(Chen 注:无服务器计算模式:开发者只部署函数代码,平台负责服务器的供给、扩缩容和运维。按实际调用次数和执行时间计费,没有请求时不产生计算成本。本质上是把「弹性伸缩」和「容量规划」的复杂度转移给平台。) 设计的极简 VMM(Virtual Machine Monitor)。

2.3.1 QEMU(Chen 注:Quick EMUlator,Linux 世界最通用的 VMM,约 140 万行 C 代码。支持模拟完整的 PC 硬件(PCI 总线、USB、声卡、显卡等),功能全面但攻击面极大,不适合多租户隔离场景。) 的问题

QEMU 是 Linux 世界最通用的 VMM,代码量约 140 万行 C,支持模拟 PCI 总线、USB 控制器、声卡、显卡……这些模拟设备的代码构成了巨大的攻击面。一个 guest VM 可以通过构造恶意的设备 I/O 请求触发 QEMU 中的漏洞,从而逃逸到宿主机。

Firecracker 的设计原则是最小化设备模型:只实现运行服务端工作负载所必需的设备。

设备说明
virtio-net网络
virtio-block块存储
virtio-vsockhost-guest 通信
串口(UART)控制台输出
i8042 键盘控制器仅用于发送电源事件(关机信号)

没有 PCI 总线模拟,没有 USB,没有 BIOS/UEFI。Firecracker 实现了一套自定义的 microVM 引导协议,直接加载 Linux 内核,跳过 BIOS 初始化阶段。整个代码库约 5 万行 Rust,相比 QEMU 的约 140 万行,代码规模缩小了约 28 倍。(Chen 注:数据来源:Brooker et al., Firecracker: Lightweight Virtualization for Serverless Applications, NSDI 2020, USENIX。)

2.3.2 Firecracker 的内部架构

图3. Firecracker 内部架构

jailer 在启动 VMM 之前完成三件事:

  1. chroot:将进程根目录切换到空目录,限制文件系统访问
  2. seccomp-bpf(Chen 注:Secure Computing Mode,Linux 内核的系统调用过滤机制。seccomp-bpf 变体允许用 BPF 字节码编写过滤规则,精确定义进程允许调用的系统调用白名单。违规调用会被内核直接以 SIGKILL 终止进程,无法被用户态代码捕获或绕过。) 过滤器:用 BPF 字节码定义允许的系统调用白名单,其余调用直接被内核以 SIGKILL 终止
  3. capability 剥离:撤销所有不必要的 Linux capability

即使 Firecracker VMM 本身存在漏洞,攻击者在 VMM 进程内能调用的系统调用也被 seccomp 严格限制,极大地缩减了提权路径。

2.3.3 冷启动时间的分解

Firecracker microVM 的 ~125ms 冷启动构成如下:

图4. Firecracker microVM 启动时序(约 125ms)

AWS Lambda 在这个基础上做了预热池(warm slot)优化:预先维护一批已初始化的 microVM 实例,请求到来时直接注入函数代码执行,用户感知到的「冷启动」只剩代码加载和运行时初始化。

图5. Lambda 预热池工作流程

2.4 V8 Isolate:在语言运行时层面隔离

Cloudflare Workers 走了一条完全不同的路。

V8 是 Chrome 和 Node.js 使用的 JavaScript 引擎。V8 的 C++ API 提供了 v8::Isolate 类,代表一个完全独立的 JS 引擎实例——这里的 Isolate(Chen 注:V8 引擎中的隔离执行单元。每个 Isolate 拥有独立的堆内存、垃圾回收器和 JIT 编译器,与其他 Isolate 之间内存完全不可达。单个 OS 进程可以同时运行数千个 Isolate,每个约 3MB 开销。这是 CF Workers 能在一台服务器上运行数万个 Worker 的基础。) 是隔离的核心单元:

  • 独立堆内存:每个 Isolate 有自己的新生代(New Space)、老生代(Old Space)、大对象空间(Large Object Space)
  • 独立 GC:各 Isolate 的垃圾回收器独立运行
  • 独立 JIT 编译器:Ignition 字节码解释器 + Turbofan/Maglev JIT 各自独立
  • 内存不可达:两个 Isolate 的堆内存在 V8 层面互不可见
图6. 单进程内的多 V8 Isolate

单个进程里可以跑数千个 Isolate,每个约 3MB 内存开销,是 microVM(5MB + 完整内核)的若干分之一。

2.4.1 V8 Snapshot:微秒级冷启动的实现

V8 Isolate 冷启动能达到微秒级的关键是 Snapshot 机制:将一个已初始化的 Isolate 堆内存序列化成二进制 blob,之后用这个 blob 恢复新 Isolate——本质上是把「初始化后的内存状态」存成快照,实例化时直接 memcpy。

图7. V8 Snapshot 的部署与实例化流程

2.4.2 Spectre 侧信道:共享进程的代价

V8 Isolate 的隔离有一个根本性的安全限制:多个 Isolate 共享同一个 OS 进程,共享 CPU 缓存。

Spectre(CVE-2017-5753)利用 CPU 的预测执行和缓存时序差异,允许攻击者通过测量内存访问时间来推断同一进程内其他区域的内容——即使 V8 的 JS 层面禁止了直接内存访问。

实施这类攻击需要:

  1. 高精度计时器:区分缓存命中(~4ns)和缓存未命中(~100ns)
  2. 跨线程共享内存:SharedArrayBuffer 可作为计时器基础

Cloudflare 的缓解措施:

措施目的
禁用 SharedArrayBuffer消除最常用的高精度计时来源
降低 performance.now() 精度加入随机抖动,使时序推断不可靠
进程级隔离(高付费层)高价值 Worker 运行在独立 OS 进程,获得硬件级隔离

这是 V8 Isolate 模型无法彻底消除的风险。


3. 架构决策矩阵

四种隔离方案的综合对比:

图8. 隔离强度 vs 启动速度
方案隔离强度冷启时间内存开销典型用途
进程中(共享内核)~200ms几十 MB传统 web 服务
容器中(共享内核)~100ms几十 MB + 镜像微服务、CI
Firecracker microVM高(独立内核)~125ms~5MB + 内核AWS Lambda
V8 Isolate中高(语言层面)< 1ms~3MBCF Workers

选择逻辑:

  • 任意代码执行 + 最高安全要求 → Firecracker(Lambda、Fly.io)
  • JS/Wasm 工作负载 + 极低延迟 → V8 Isolate(CF Workers、Deno Deploy)
  • 受信任代码 + 快速迭代 → 容器(内部服务、CI/CD)

4. 架构师的设计决策

如果零设计一个 FaaS 平台,需要在隔离方案之上回答几个关键问题。

4.1 热路径优化:预热池的设计

无论选哪种隔离方案,都需要设计预热池来消除感知冷启动。设计要素:

图9. 预热池的调度模型

预热池大小是一个成本与延迟的权衡:池子太小,峰值时冷启增多;太大,空闲实例浪费资源。通常基于历史请求分布做预测性扩缩容,而不是被动响应。

4.2 资源限制的实施层

每个隔离方案在不同层次实施资源限制:

限制维度Firecracker 方案V8 Isolate 方案
CPUcgroups 配额独立计时线程 + TerminateExecution
内存cgroups 限制V8 HeapStatistics 监控
网络带宽virtio 设备限速出站请求过滤(无原始 TCP)
进程 / 文件Guest 内核参数不适用(无进程、无文件系统)

V8 的 CPU 限制实现尤其有趣:CF 在独立线程里用 wall clock 计时,超过配额时调用 isolate->TerminateExecution(),这会在下一个 V8 安全点(safepoint)注入一个终止异常。JS 代码无法捕获这个异常,isolate 被强制终止。

4.3 网络隔离

计算隔离做好之后,还需要网络层面的隔离。每个租户的函数实例应当只能访问其被授权的出站地址,不能扫描内网、访问其他租户的服务。

  • Firecracker 方案:每个 microVM 有独立的 tap 设备,宿主机用 iptables/nftables 规则控制出站,结合 VPC/安全组实现网络隔离。

  • V8 Isolate 方案:CF 的 Worker 发起的 fetch() 请求经过 CF 自己的网络层过滤,私有地址段(RFC 1918)的请求被拒绝,出站流量可审计。


5. 本站情况

本站的动态请求(评论 API、浏览量统计、内容审核)跑在 Vercel Functions 上,底层是 Firecracker microVM(Vercel 的 Fluid Compute)。

对这个场景来说,V8 Isolate 方案在技术上也够用——API 逻辑简单,不依赖 Node.js 特有的底层 API。真正决定选择 Vercel 而不是 CF Workers 的原因,不是隔离模型,而是 Next.js 的深度集成:Server Components、Server Actions、Middleware 的完整支持,在 CF Workers 的 V8 Isolate 约束下至今有功能缺口。

隔离方案的选择,最终被框架兼容性决定,而不是被安全需求决定——这是个值得记住的教训。


6. 专线 SaaS 的映射

最后将这套隔离思路映射到专线 SaaS场景。对于一批自有机器,通过专线向多个金融机构客户提供服务,不同客户的数据和计算必须严格隔离。

图11. 专线 SaaS 的租户隔离架构

隔离方案的选择逻辑与公有云完全一致:

  • 容器方案适用于:客户代码是我自己写的(受信任),客户之间的隔离主要是逻辑隔离(数据不互通),对等保合规的解读允许共享内核。成本低,运维简单。

  • microVM 方案适用于:需要通过等保三级或更高级别的安全评审,合规要求明确要求内核级隔离;或客户是监管机构,审计时需要展示硬件级隔离证明。成本相对高,但 Firecracker 的 ~5MB 开销使得同一台机器上运行数百个隔离实例是现实可行的。

  • 关键差异:公有云的多租户问题是「互不相识的陌生人共用机器」,而专线 SaaS 的问题是「已知的 B 端客户共用机器」。后者的威胁模型不同——攻击者通常不是客户本身,而是针对你基础设施的外部入侵。因此隔离的首要目的从「防止客户间互攻」变成了「满足监管合规要求 + 防止单点被攻破后横向扩散」。

这个差异直接影响方案选择:在专线 SaaS 场景下,容器 + 严格的网络隔离 + 数据加密,往往比 microVM + 松散的网络策略更安全、更实用。


下一篇:现代计算的基础设施(二):并发的重新发现