使用 kTLS 和 splice(2) 实现沙箱的零拷贝 TLS 入口
Tensorlake 重建了沙箱的网络入口架构,将数据路径从 L7 反向代理迁移到 L4 字节转发,利用内核 TLS(kTLS)和 splice(2) 实现零拷贝,大幅提升吞吐量并降低 CPU 开销。架构变化包括分离数据路径和控制平面、使用 kTLS 在内核中完成加解密、通过字节流监测实现自适应超时。性能测试显示,单连接吞吐量从 1.12 GB/s 提升至 2.50 GB/s,每 GB 的 CPU 消耗从 0.90 CPU 秒降至 0.49 CPU 秒。
Tensorlake 的沙箱产品为 AI 代理提供了高性能运行环境,代理需要频繁与沙箱通信以执行上传、下载和进程管理等操作。然而,原有的入口架构依赖于两层 L7 反向代理,导致复杂的超时配置、高 CPU 开销以及连接中断问题。为此,团队重新设计了入口网络,将数据路径从 L7 代理迁移到 L4 字节转发,并引入内核 TLS(kTLS)和 splice(2) 系统调用,实现了近乎零开销的数据传输。
在旧架构中,用户流量首先通过边缘网关进行 TLS 终止和认证,然后以 mTLS 方式转发到第二层 L7 代理,后者再将请求转发给沙箱内的应用。这一过程涉及两次用户态拷贝和完整的 HTTP 解析,性能受限且维护复杂。新架构将数据路径抽象为一个独立的 L4 转发守护进程,与沙箱编排器分离,从而避免编排器升级时中断实时连接。
新的数据路径完全在 L4 层工作:边缘网关终止 TLS 后,通过 mTLS 连接到主机上的 L4 转发器,并发送一个短帧,包含沙箱 ID 和目标地址。转发器据此建立到沙箱应用的连接,然后使用 splice(2) 将两个连接在内核中直接拼接。关键在于,转发器在用户态完成 TLS 1.3 握手后,通过 setsockopt(TLS_TX) 和 setsockopt(TLS_RX) 将密钥安装到 socket 中,让内核接管记录层。这样,从网关套接字读取的数据已经是解密后的明文,可以零拷贝地 spliced 到沙箱套接字;反之亦然。这一过程完全避免了用户态缓冲。
由于移除了 L7 层,自适应超时机制改为基于字节流监测。splice() 返回的字节数被用于计量流量,转发器定期向编排器报告,从而重置沙箱的空闲计时器。连接关闭也更加透明,半关闭和重置信号都能直接传递。
性能测试显示,新路径的单连接吞吐量从 1.12 GB/s 提升到 2.50 GB/s,每 GB 的 CPU 消耗从 0.90 CPU 秒降低到 0.49 CPU 秒。有趣的是,大部分性能提升来自移除 L7 解析和缓冲,而 kTLS 本身贡献了约 20% 的吞吐量提升。需要注意的是,这些测试是在单台主机上进行的,多连接场景下聚合吞吐量可达 8.67 GB/s(69 Gbps)。团队计划未来进一步优化 splice 循环的序列化问题,并探索绕过 kTLS 直接在内核拼接的路径。