- 云原生
- 容器运行时
【免费下载链接】kata-containers
Kata Containers is an open source project and community working to build a standard implementation of lightweight Virtual Machines (VMs) that feel and perform like containers, but provide the workload isolation and security advantages of VMs. https://katacontainers.io/
Passthrough-FD(pass-fd)是 Kata Containers 为优化容器进程 IO 性能引入的一项关键技术:它让 Dragonball VMM 的 hybrid-vsock 直接接收并传递宿主机文件描述符,从而绕过 kata-shim 的中转缓冲层,显著降低 IO 延迟与 CPU 开销。本文以 docs/how-to/how-to-use-passthroughfd-io-within-runtime-rs.md 为骨架,结合 runtime-rs 与 kata-agent 的源码实现,完整讲解其工作原理、端到端数据流以及配置启用方法,读者读完后可掌握该特性的适用边界、底层调用链与实际配置方式。
重要限制:仅 Dragonball VMM 支持
在深入技术细节之前,必须先明确该特性的适用范围:
- 专属支持 Dragonball VMM:Passthrough-FD 目前只在 Kata Containers 内置 VMM——Dragonball 上实现并生效;
- 不支持的 VMM:QEMU、Cloud Hypervisor(CLH)、Firecracker 等其他 VMM 当前均不支持该特性。
从源码结构看,这一点与 runtime-rs 的 hypervisor 抽象设计一致:虽然 hypervisor 接口 中定义了get_passfd_listener_addr()方法(QEMU、CLH、Firecracker、OpenVMM、Remote 等实现均有对应桩实现),但真正完整落实 FD 直通链路的只有 Dragonball 路径。runtime-rs 在创建 hypervisor 实例时,也仅在匹配到 Dragonball 且开启了use_passfd_io的情况下才会调用set_passfd_listener_port()注入监听端口,参见 lib.rs。
背景:传统 IO 路径的瓶颈
在引入 Passthrough-FD 之前,Kata 的容器进程 IO 流(stdin/stdout/stderr)是通过ttrpc + virtio-vsock实现的,数据流如下:
这一路径的核心问题在于 kata-shim 扮演了"中间人"角色:
- kata-shim(containerd-shim-kata-v2)通过 shimv2 接口打开 containerd 提供的 FIFO 管道,得到 stdin、stdout、stderr 三个 FD;
- kata-shim 为这三条流分别管理三个独立线程;
- 每个线程都必须先把数据从 FD 读入 shim 内部缓冲区,再通过 ttrpc 经 vsock 转发到 guest 内的 kata-agent,最终才到达容器进程。
多线程代理加上三层缓冲(FD → shim buffer → ttrpc/vsock → agent),导致数据路径过长、效率低下。原文档给出的一个直观例子是:复制 10GB 文件可能耗时长达 10 分钟。这一痛点正是 Kata AC 成员 @lifupan 与 @frezcirno 引入 passthrough-fd 技术优化的动因。
什么是 Passthrough-FD?
Passthrough-FD 的核心思想是:让 VMM 直接处理文件描述符。它增强了 Dragonball VMM 的 hybrid-vsock 实现,使其支持 recv-fd(接收带 FD 的报文),从而把宿主机上的 FD 直接"穿透"到 kata-agent,而不是让 kata-shim 先把数据读进自己的缓冲区再转发。
启用后的数据流变为:
对比传统路径可以发现:kata-shim 的缓冲代理层被整体移除。hybrid-vsock 模块可以直接从 Host 接收文件描述符,系统将 Host 的 FD "直通"给 kata-agent,IO 流在 guest 环境中被直接接通,消除了 kata-shim 中的代理逻辑。
端到端工作原理
整个过程的完整时序如下:
整个过程分为六个关键步骤:
- Agent 初始化:kata-agent 启动一个服务器,监听
passfd_listener_port指定的端口。对应实现见 main.rs:当配置的passfd_listener_port != 0时,调用passfd_io::start_listen(port)。 - FD 传输:在容器创建阶段,kata-shim 通过 sendfd 机制将 stdin、stdout、stderr 三个 FD 发送给 Dragonball hybrid-vsock 模块。
- 连接建立:借助 hybrid-vsock,这些 FD 连接到第 1 步中 agent 启动的服务器。
- 标识与保存:agent 的服务器调用
accept()获得连接 FD 及其对应的 host-port,并以 host-port 作为唯一标识保存连接。此时 agent 持有三条已建立连接(分别由 stdin-port、stdout-port、stderr-port 标识)。 - RPC 映射:kata-shim 调用
create_containerRPC 时,把这三个端口标识一并放入请求。 - 最终绑定:agent 收到 RPC 后,根据传入的端口从保存的连接中取出对应连接,直接绑定到容器进程的标准 IO 流上。
源码印证:kata-agent 侧的实现细节
kata-agent 侧的完整逻辑集中在 passfd_io.rs:
start_listen(port):通过VsockListener::bind(libc::VMADDR_CID_ANY, port)绑定到 guest 内任意 CID 的指定端口,循环accept()并把(peer port → VsockStream)立即插入全局映射HVSOCK_STREAMS(注释明确说明"尽快插入映射以最小化竞态风险");take_io_streams(stdin_port, stdout_port, stderr_port):按端口从映射中取出流,端口为0时返回None(表示该流未启用直通,回退到常规路径);若流已被 accept 但尚未插入映射,会最多重试 3 次、每次间隔 100ms;- 最终组装成
rustjail::process::ProcessIo供容器进程使用。
调用点位于 rpc.rs:do_create_container在创建容器伊始即调用take_io_streams,且注释强调"先创建 proc_io,若后续出错可确保 IO 流被正确关闭"。
源码印证:runtime-rs 侧与配置的传递链路
runtime-rs 侧的配置传递链路完整闭环,从配置到内核参数再到 RPC 请求:
- 配置解析后,runtime-rs 在创建 hypervisor 时(仅 Dragonball 且
use_passfd_io = true)调用set_passfd_listener_port(),见 lib.rs; - Dragonball 启动时将该端口以内核参数
agent.passfd_listener_port=<port>的形式注入 guest,见 inner.rs; - kata-shim(runtime-rs 容器创建路径)通过
get_passfd_listener_addr()拿到 hybrid-vsock 的 UDS 路径与监听端口,见 inner_hypervisor.rs; - 容器创建时调用
init_process.passfd_io_init(hvsock_uds_path, port)建立三条直通连接,并把stdin_port、stdout_port、stderr_port填入CreateContainerRequest发给 agent,见 container.rs。
如何启用 Passthrough-FD IO
Passthrough-FD 由 Kata 配置文件中的两个参数控制:
| 参数 | 含义 | 默认值 |
|---|---|---|
use_passfd_io | 布尔开关,启用/禁用 Passthrough-FD IO 特性 | false |
passfd_listener_port | kata-agent 监听 FD 连接的端口 | 1027 |
在 runtime-rs 的 Dragonball 配置模板 configuration-dragonball.toml.in 中,该特性默认即为开启状态,配置写法如下:
... # If enabled, the runtime will attempt to use fd passthrough feature for process io. # Note: this feature is only supported by the Dragonball hypervisor. use_passfd_io = true # If fd passthrough io is enabled, the runtime will attempt to use the specified port instead of the default port. passfd_listener_port = 1027启用步骤与注意事项:
- 确保使用 Dragonball VMM(runtime-rs 的
configuration-dragonball.toml.in对应配置); - 将
use_passfd_io设为true; - 按需调整
passfd_listener_port(默认1027),该端口会同时配置给 runtime-rs 与 kata-agent; - 若
passfd_listener_port配置为0,kata-agent 将不会启动 passfd 监听(见 main.rs),此时即便use_passfd_io = true也无法生效,需保持非 0 值。
配置项的源码映射
- runtime-rs 侧:
use_passfd_io与passfd_listener_port定义在 runtime.rs,其中passfd_listener_port带默认值函数default_passfd_listener_port()(即1027); - agent 侧:对应配置项为
agent.passfd_listener_port,定义在 agent.rs,其解析逻辑位于 config.rs,并通过agent.passfd_listener_port内核参数注入(常量定义见 mod.rs)。
这种"一份配置、两端生效"的设计,确保了 runtime-rs 与 kata-agent 对监听端口理解一致,是直通链路能够正确建立的前提。
迁移视角:Go runtime 与 Runtime-rs 的配置对应
若读者正在从传统 Go runtime 迁移到 runtime-rs,可在 migrating-config-go-runtime-to-runtime-rs.md 中找到两个配置项的对应关系:passfd_listener_port在两套 runtime 中均表示 fd-passthrough IO 特性所用端口(agent 侧),迁移时注意核对端口取值与use_passfd_io开关是否一致。
总结
Passthrough-FD 通过"FD 直通"重构了 Kata 容器进程 IO 的数据通路:kata-shim 不再充当数据搬运工,hybrid-vsock 直接接收宿主机 FD,kata-agent 以端口为索引完成流与容器标准 IO 的绑定。这一设计从根源上消除了多线程代理与缓冲拷贝带来的开销,是 Kata Containers 针对 IO 性能优化的重要实践。其完整链路在仓库中均可验证:从 configuration-dragonball.toml.in 的配置入口,到 runtime-rs 侧 container.rs 的端口注入,再到 kata-agent 侧 passfd_io.rs 的监听与流管理,形成了清晰可追踪的实现闭环。使用前请务必确认 VMM 为 Dragonball,并保持use_passfd_io = true与passfd_listener_port非 0 的配置前提。
- 云原生
- 容器运行时
【免费下载链接】kata-containers
Kata Containers is an open source project and community working to build a standard implementation of lightweight Virtual Machines (VMs) that feel and perform like containers, but provide the workload isolation and security advantages of VMs. https://katacontainers.io/
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考