Snap 2019 — Google 把网络栈搬进用户空间的微内核实践
是什么
Snap 是 Google 在 SOSP 2019 发表的用户态网络系统,核心思路借鉴微内核:把原本在 Linux 内核里跑的网络栈,整体搬到用户空间,用独立进程管理,通过模块插拔来扩展功能。
日常类比:想象一座城市的交通指挥中心——传统做法是把指挥室焊进道路基础设施(内核),想改规则必须拆马路;Snap 的做法是把指挥中心建在地面上的独立大楼,道路(硬件)不动,随时可以换新的调度算法,甚至在不停红绿灯的情况下把指挥软件整个升级。
Snap 支持四类网络功能:边缘包交换(edge packet switching)、云平台虚拟化(gVNIC 后端)、流量整形策略执行、以及高性能类 RDMA 可靠消息传递(Snap Packet Protocol,SPP)。它在 2019 年已部署到 Google 超过一半的服务器机队,支撑了数十个大型关键系统。
为什么重要
不理解 Snap,下面这些问题都解释不清楚:
- 为什么 Google 的云虚拟化(gVNIC)能在不改 Guest OS 的情况下实现接近 RDMA 的性能——RDMA(Remote Direct Memory Access)是一种让一台机器直接把数据写进另一台机器内存、绕开 CPU 的技术;Snap 在软件层模拟了这种效果,答案就在 Snap 的用户态 vNIC 层
- 为什么”用户态网络 = 绕过 OS”的直觉是错的——Snap 保留了 OS 调度语义,只是不走内核协议栈
- 为什么在大规模生产环境做网络功能”热升级”(zero-downtime upgrade)是一个系统设计难题,而 Snap 有专门的解法
- 为什么内核旁路(DPDK / XDP / eBPF)和微内核网络这两条路,在工程上截然不同——前者优化热路径,后者重构整个软件架构边界
核心要点
Snap 的设计围绕三个关键机制:
-
模块化引擎(Pony Express + 模块图):所有网络功能实现为可组合模块(module graph),数据包在模块间流动。模块在同一进程内零拷贝传递,模块边界是函数调用而非 IPC——微内核的”隔离”体现在地址空间级别(Snap 进程 vs 应用进程),而非模块之间。类比:每个网络功能是一个乐高积木,积木之间咬合不需要走快递,只需要握手传递。
-
内核/用户态 CPU 调度器协同(co-design):Snap 设计了一个新颖的双层调度架构。内核感知 Snap 是”特殊的网络引擎”,通过调度提示(scheduling hints)让 Snap 的 Pony Express 线程在低负载时主动让出 CPU(sleep),高负载时按需唤醒(busy-poll)。这解决了两个极端的矛盾:传统用户态网络(DPDK style)需要 CPU 忙轮询,浪费核;传统内核网络靠中断,高吞吐时 interrupt storm。
-
透明热升级(Transparent Upgrade):Snap 支持在不迁移应用的情况下升级网络功能进程。关键技术:旧版 Snap 进程在升级前把所有 in-flight 连接状态序列化,新版进程接管后恢复,整个过程应用层 TCP 连接不中断,观察到的停顿 < 1ms。论文实测:升级对 tail latency(P99)几乎无影响。
实践案例
案例 1:Google 云平台 gVNIC 虚拟化
虚拟机(VM)需要一块”虚拟网卡”(vNIC),传统方案是在内核的 virtio 驱动层做模拟,延迟高且功能受限。Google 用 Snap 实现了 gVNIC:Snap 进程在宿主机上运行,VM 的网络包通过 shared memory ring buffer 传到 Snap,由 Snap 做加密、封装、流量整形,再发到物理网卡。
VM (Guest OS) | virtio-net / gVNIC 驱动 ↓shared memory ring buffer ↓Snap 用户态进程(宿主机) ├── 模块:加密(AES-GCM) ├── 模块:封装(GRE/VXLAN) ├── 模块:流量整形(令牌桶) └── 物理 NIC(DPDK/RDMA)效果:VM 里跑标准 TCP/IP,宿主机 Snap 透明实现 RDMA-like 吞吐(5M IOPS/core),且功能可热升级——改流量整形策略不需要重启 VM。
案例 2:大规模 RPC 吞吐优化
Google 内部 gRPC 调用量巨大,内核 TCP 栈在高 PPS(每秒包数)场景下成为瓶颈——主要因为:内核协议栈上下文切换开销、syscall 次数、内存拷贝。Snap 用 SPP(Snap Packet Protocol)替换底层 transport:
- 应用依然调用标准 socket API(Snap 提供 shim 库,拦截 syscall)
- 数据包通过 shared memory 传给 Snap 引擎处理,避免内核介入
- Snap 的 Pony Express 线程负责调度和发包
应用进程 Snap 进程 | | | [socket shim] | |---shared mem----->| | SPP 封包 |---> NIC论文结果:RPC 工作负载 Gbps/core 提升 3 倍以上。等价于同样的核,能处理 3 倍的 RPC 流量——对于 Google 这种规模,节省的机器成本是真实可见的。
案例 3:零停机网络功能升级
运维场景:需要给 Snap 打安全补丁(比如修复 TLS 漏洞),但 Snap 负责维护所有应用的 TCP 连接,传统方案只能停服迁移。
Snap 的热升级流程:
- 启动新版 Snap 进程(与旧版同时运行,监听升级信号)
- 旧版 Snap 停止接受新连接,把存量连接状态(序列号、窗口大小、拥塞状态)序列化到共享内存
- 新版 Snap 读入状态,接管所有连接
- 旧版退出
整个过程类似接力赛传棒:旧选手减速,把棒交出去,新选手不停跑。论文测量:应用侧 TCP 连接不中断,P99 延迟增量 < 1ms。
踩过的坑
-
调试工具全面失效:Snap 的包不走内核协议栈,tcpdump、Wireshark、strace 在应用侧全部看不到包。团队需要自建 trace 框架(ring buffer + custom tool),调试周期比传统内核网络长 2-3 倍。
-
CPU 调度器协同比想象复杂:初版 Snap 用纯 busy-poll(类 DPDK),把分配的 CPU 核跑满,导致同机器的 latency-sensitive 服务(如 memcached)受到干扰。与 Borg 调度器协同后,Snap 在低负载时主动让 CPU,但这又引入了唤醒延迟(wakeup latency)——最终需要在 P99 尾延迟和 CPU 利用率之间精心调参。
-
透明升级的状态一致性难题:连接状态序列化看似简单,实际上每个模块(加密模块、拥塞控制模块)都有独立状态,版本升级时字段可能变化。如果序列化格式不向前兼容,升级就会导致状态丢失,进而使应用 TCP 连接中断。这要求严格的序列化版本管理(类似数据库 schema migration)。
-
与现有监控体系对接成本高:Google 内部所有监控工具默认从内核拉网络指标(/proc/net/tcp 等),Snap 绕过内核后这些指标全部消失,需要 Snap 自己暴露等价指标,并修改监控 pipeline 来适配,改动波及面极广。
适用 vs 不适用场景
适用:
- 需要快速迭代网络功能且不能改动 Guest OS 或应用的场景(云 vNIC 虚拟化)
- 对网络功能需要零停机升级/回滚的生产系统
- 高吞吐 RPC 场景,内核 TCP 栈成为瓶颈(PPS > 1M/core)
- 需要统一管理流量整形、加密、路由等多种策略的平台
不适用:
- 小规模部署(< 数百台),Snap 带来的工程复杂度无法摊薄
- 调试工具链不可改动的环境(监管/合规要求只能用内核网络)
- 延迟极度敏感(< 10µs)且不能接受任何调度抖动的 HFT / 实时控制场景——内核旁路(DPDK)可能是更直接的选项
- 需要完全标准 POSIX socket 语义且不愿意引入 shim 库的应用
历史小故事(可跳过)
- 2014 年:Google 发表 Andromeda,把 SDN 控制面放到用户态,物理网络由软件定义。但数据面依然走内核协议栈,快速迭代受限。
- 2016-2017 年:Google 内部开始 Snap 项目,目标是把数据面也搬到用户态,同时解决 Andromeda 遗留的”升级必须停服”问题。
- 2019 年:Snap 在 SOSP(操作系统领域顶会)公开发表,同年已经部署到 Google 超过一半的机器。论文作者列表有十余人,体现这是一个大型工程项目而非学术原型。
- 2020 年后:Snap 成为 Google gVNIC 的底层,gVNIC 随 Google Cloud VM 系列向外提供,是 Google 云网络性能的核心竞争力之一。Snap 的 CPU 协同调度思路后来也被学术界引用,影响了 Caladan(MIT,2020)等后续工作。
学到什么
- “微内核”不等于”慢”:传统观点认为微内核因 IPC 开销慢,Snap 证明:只要隔离边界设计合理(进程级隔离 + 共享内存通信),用户态网络可以比内核网络快 3 倍
- 升级性是一类一等公民的系统需求:Snap 把”零停机升级”从一开始就列为设计目标而非事后加入,这让其架构决策(状态可序列化、模块可热替换)与普通网络栈截然不同
- 内核/用户态协同调度是关键:纯 bypass 内核(DPDK)和纯走内核是两个极端,Snap 找到了中间路——利用 OS 调度器语义,同时获得用户态的灵活性和 OS 的资源管理能力
- 规模创造工程机会:Snap 的很多设计决策(专属调试工具、与 Borg 协同)只在 Google 规模下才有投入产出比,这提醒我们:顶级工程论文描述的是特定规模下的最优解,不是通用银弹
延伸阅读
- 论文原文:Snap: a Microkernel Approach to Host Networking, SOSP 2019
- 前身:andromeda-2018 —— Google SDN 平台,Snap 是其数据面的演进
- 对照参考:arrakis-2014 —— 类似的用户态网络思路,来自学术界
- 对照参考:ix-2014 —— 数据平面 OS,同类工作的另一条路线
- eBPF 路线对比:ebpf —— 在内核内部做可编程网络,与 Snap 的”搬到用户态”是两种哲学
- 微内核基础:l4-1995 —— 现代微内核性能可以很高的奠基工作
关联
-
andromeda-2018 —— Snap 的前身,Google SDN 数据面演进的起点
-
arrakis-2014 —— 同为用户态网络系统,学术界同期代表作
-
ix-2014 —— 数据平面 OS,与 Snap 解决同类问题的不同路径
-
exokernel-1995 —— 微内核/Library OS 思想的学术源头,Snap 有相似哲学
-
barrelfish-2009 —— 多核时代微内核研究,与 Snap 的 CPU 协同调度有共鸣
-
mach-1986 —— 早期微内核代表,奠定了”把功能移到用户态”的架构思路
-
ebpf —— 内核内可编程网络,与 Snap 用户态方案形成对比和互补
-
sel4-2009 —— 形式化验证的微内核,与 Snap 的安全隔离目标有呼应
-
borg —— Google 集群调度器,Snap 的 CPU 协同调度需要与 Borg 配合
反向链接
- andromeda-2018 —— Andromeda — Google Cloud 网络虚拟化的高速通道
- arrakis-2014 —— Arrakis 2014 — 让操作系统只管规则、硬件直接服务应用
- barrelfish-2009 —— Barrelfish / Multikernel — 把多核机器当成一个小型网络来设计 OS
- borg —— Borg — Google 把一万台机器假装成一台
- ebpf —— eBPF — 用户写小程序,内核证明安全后再跑
- ix-2014 —— IX 数据面操作系统 — 用虚拟化把高吞吐和低延迟同时塞进内核
- mach-1986 —— Mach — 把内核拆成消息互通的小服务
- sel4-2009 —— seL4 — 第一个被数学证明”代码和规范完全一致”的操作系统内核