跳转到内容

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 的设计围绕三个关键机制:

  1. 模块化引擎(Pony Express + 模块图):所有网络功能实现为可组合模块(module graph),数据包在模块间流动。模块在同一进程内零拷贝传递,模块边界是函数调用而非 IPC——微内核的”隔离”体现在地址空间级别(Snap 进程 vs 应用进程),而非模块之间。类比:每个网络功能是一个乐高积木,积木之间咬合不需要走快递,只需要握手传递。

  2. 内核/用户态 CPU 调度器协同(co-design):Snap 设计了一个新颖的双层调度架构。内核感知 Snap 是”特殊的网络引擎”,通过调度提示(scheduling hints)让 Snap 的 Pony Express 线程在低负载时主动让出 CPU(sleep),高负载时按需唤醒(busy-poll)。这解决了两个极端的矛盾:传统用户态网络(DPDK style)需要 CPU 忙轮询,浪费核;传统内核网络靠中断,高吞吐时 interrupt storm。

  3. 透明热升级(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 的热升级流程:

  1. 启动新版 Snap 进程(与旧版同时运行,监听升级信号)
  2. 旧版 Snap 停止接受新连接,把存量连接状态(序列号、窗口大小、拥塞状态)序列化到共享内存
  3. 新版 Snap 读入状态,接管所有连接
  4. 旧版退出

整个过程类似接力赛传棒:旧选手减速,把棒交出去,新选手不停跑。论文测量:应用侧 TCP 连接不中断,P99 延迟增量 < 1ms。

踩过的坑

  1. 调试工具全面失效:Snap 的包不走内核协议栈,tcpdump、Wireshark、strace 在应用侧全部看不到包。团队需要自建 trace 框架(ring buffer + custom tool),调试周期比传统内核网络长 2-3 倍。

  2. CPU 调度器协同比想象复杂:初版 Snap 用纯 busy-poll(类 DPDK),把分配的 CPU 核跑满,导致同机器的 latency-sensitive 服务(如 memcached)受到干扰。与 Borg 调度器协同后,Snap 在低负载时主动让 CPU,但这又引入了唤醒延迟(wakeup latency)——最终需要在 P99 尾延迟和 CPU 利用率之间精心调参。

  3. 透明升级的状态一致性难题:连接状态序列化看似简单,实际上每个模块(加密模块、拥塞控制模块)都有独立状态,版本升级时字段可能变化。如果序列化格式不向前兼容,升级就会导致状态丢失,进而使应用 TCP 连接中断。这要求严格的序列化版本管理(类似数据库 schema migration)。

  4. 与现有监控体系对接成本高: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)等后续工作。

学到什么

  1. “微内核”不等于”慢”:传统观点认为微内核因 IPC 开销慢,Snap 证明:只要隔离边界设计合理(进程级隔离 + 共享内存通信),用户态网络可以比内核网络快 3 倍
  2. 升级性是一类一等公民的系统需求:Snap 把”零停机升级”从一开始就列为设计目标而非事后加入,这让其架构决策(状态可序列化、模块可热替换)与普通网络栈截然不同
  3. 内核/用户态协同调度是关键:纯 bypass 内核(DPDK)和纯走内核是两个极端,Snap 找到了中间路——利用 OS 调度器语义,同时获得用户态的灵活性和 OS 的资源管理能力
  4. 规模创造工程机会: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 — 第一个被数学证明”代码和规范完全一致”的操作系统内核