AWS Lambda 如何利用 eBPF 和 Rust 记录每台主机上数千个微虚拟机的所有流量

AWS Lambda 如何利用 eBPF 和 Rust 记录每台主机上数千个微虚拟机的所有流量

💡 原文英文,约3800词,阅读约需14分钟。
📝

内容提要

AWS Lambda 为高密度微虚拟机环境重建网络流量日志系统。旧方案依赖 iptables 规则,随虚拟机增多性能骤降且不支持 IPv6。新系统用 eBPF 在内核捕获数据包,Rust 编写用户态聚合程序,每租户独立环形缓冲区,通过文件描述符传递实现权限隔离,输出格式与旧系统完全兼容,实现零下游迁移。

🔎

延伸解读

旧架构的瓶颈:规则爆炸与IPv6缺失

旧系统依赖iptables规则匹配流量,每新增一个微虚拟机就增加规则,导致规则数量线性增长。一台主机运行两千个微虚拟机时,规则超过十万条,每个数据包都要遍历规则链,性能随主机负载上升而下降。更关键的是,旧内核模块不支持IPv6,无法记录双栈地址空间的流量,在Lambda计划支持IPv6时,旧方案已无改进空间。

新架构的三层分工:eBPF捕获、Rust聚合、编排器管控

新系统分为三层:内核态eBPF程序挂载在tc钩子,每个数据包生成约24字节事件写入环形缓冲区;用户态Rust编写的tagger进程每个网络一个,无特权,负责将事件聚合为流记录并输出Ion格式;每主机一个特权编排器,负责加载eBPF、配置tc、生成和管理tagger。这种解耦让捕获在内核、聚合在用户态,编排器集中权限,降低安全风险。

环形缓冲区大小与唤醒机制的设计依据

环形缓冲区大小根据微虚拟机最大包速率和排空间隔计算。例如,每方向10万包/秒,每100毫秒排空一次,事件约24字节,双向需约300KB,取2的幂为512KiB。实际部署中配置为几MB以留余量。内核仅在缓冲区超过1%满时唤醒用户态进程,用户态最多每100毫秒读取一次,避免频繁唤醒导致CPU抖动,同时确保突发流量不丢事件。

权限隔离与兼容性:文件描述符传递和字节级兼容

无特权的tagger无法直接打开环形缓冲区,编排器通过Unix域套接字的SCM_RIGHTS机制传递文件描述符,使tagger获得已打开的句柄。这样,数千个处理客户流量的进程几乎无权限,特权集中在每主机一个编排器。输出记录与旧系统字节级兼容,下游消费者无需修改,且可逐记录对比验证完整性,实现零迁移。

Q&A

AWS Lambda 为什么需要重新设计网络流量日志系统?

旧系统基于 iptables 规则,在微虚拟机密度增加时性能急剧下降,且不支持 IPv6。随着 Lambda 转向多租户 Firecracker 微虚拟机架构,旧方案无法满足正确归属、低开销和 IPv6 支持的需求。

新系统如何保证每个数据包都能正确归属到对应的租户和微虚拟机?

每个微虚拟机拥有独立的网络命名空间和虚拟设备,eBPF 程序在各自网络的 tc 钩子上捕获数据包并写入专属的环形缓冲区。每个租户的流量从捕获点就完全隔离,标签器只读取本网络的缓冲区,因此归属天然正确。

eBPF 程序在捕获数据包时具体做了哪些工作?

eBPF 程序附加在 tc 的 clsact qdisc 上,解析以太网、IP 和传输层头部,为每个数据包生成一个约 24 字节的固定大小事件,写入环形缓冲区。程序只观察,不复制、不阻塞、不丢弃、不修改数据包,也不在核内做聚合。

为什么选择 Rust 编写用户态标签器?

Rust 提供可预测的内存使用和无垃圾回收,避免 GC 暂停导致记录出现缺口。每个标签器仅占用几百 KB 内存,使得单主机可运行数千个实例,同时编译器能防止多种导致错误归属的 bug。

环形缓冲区的大小是如何确定的?

根据每个微虚拟机的峰值包速率和排空间隔计算下限。例如,峰值 100,000 pps,排空间隔 100ms,事件大小约 24 字节,双向流量,则所需约 300 KB。环形缓冲区 API 要求 2 的幂,因此默认 512 KiB,确保在排空间隔内不会溢出。

新系统如何实现权限隔离,让非特权进程安全读取环形缓冲区?

每个主机上只有一个特权编排器进程负责加载 eBPF、配置 tc 和打开环形缓冲区。编排器通过 Unix 域套接字的 SCM_RIGHTS 机制将环形缓冲区的文件描述符传递给非特权的标签器进程。标签器无需自行打开缓冲区,从而将特权集中在一处。

新系统如何确保与旧系统的下游消费者兼容?

新系统输出的 Amazon Ion 记录与旧守护进程生成的记录字节级完全一致。下游的计费、流日志和计量系统无需任何修改即可继续工作,实现了零迁移。

在生产环境中,标签器进程的回收策略遇到了什么实际问题?

原设计为严格保证跨租户隔离,每次回收网络时都销毁并重新创建标签器进程。但在生产负载下,频繁 fork/exec 导致 CPU 尖峰。因此系统增加了一个可配置选项:允许复用标签器以减少 CPU 开销,但会牺牲部分结构性保证。

🏷️

标签

➡️

继续阅读