背景
当前 Kata 的 IO 实现路径过长,效率过低,复制 10GB 文件竟然需要长达 10 分钟的时间才能完成,针对此情况 Kata 社区成员 @lifupan[1] (Kata AC Member) 和 @frezcirno[2] 提交了一系列代码通过 passthrough fd 技术大幅改善和提升了 IO 性能。
传统的 IO
在使用 passthrough fd 技术(以下有时会简称为 pass-fd)实现 IO 以前,Kata 的 IO 流是通过 ttrpc + virtio-vsock 实现的。由 Host 上的 kata-shim(containerd-shim-kata-v2)进程来打开 containerd 通过 shimv2 接口传递过来的 FIFO 路径名,这时我们得到三个 fd(file descriptor,以下简称 fd),分别对应 stdin, stdout 和 stderr,kata-shim 中会启动三个线程分别处理三个 IO 流,这样等于在 kata-shim 中维护了三个 buffer,kata-shim 作为中间人要把 stdin, stdout 和 stderr 读到自己的 buffer 中之后才能通过 ttrpc over vsock 发送到最终目的地,在这个过程中 kata-shim 的三个线程作为 ttrpc over vsock 的 client 是必不可少的。下图展示了 stdin 在 kata 中原本的处理流程,为了简洁省略了 stdout 和 stderr。

什么是 passthrough-fd 技术
pass-fd 技术是 dragonball 在其 hybrid-vsock 实现的基础上增加对 recv-fd 的支持,使得 dragonball 的 hybrid-vsock 模块可以直接接收文件描述符从而能够直接读取或写入 host 上的 fd,这样可以省略掉转发的中间层(因为原来 hybrid-vsock 不支持接收 fd,那么就只能增加一个中间层来读取 fd 的内容再转发给 hybrid-vsock 了),如下图我们省略了中间层相当于将 IO 流直接通到了 kata-agent 上。
passthrough-fd IO 示意图
passthrough-fd 技术细节
怎么打开和配置
首先 pass-fd IO 特性通过 kata 配置文件中的两个配置项 use_passfd_io
和 passfd_listener_port
来控制,第一个配置项 use_passfd_io
是特性的开关,第二个配置项则是用来控制 agent 端的 listen 端口的,默认值为 1027。
业务流程
1.kata-agent 端会启动一个 server listen 到配置项 passfd_listener_port
所指定的端口上。2.kata-shim 会在 create container 阶段将 stdin, stdout 和 stderr 的 fd 通过 sendfd 的方式发送给 dragonball 的 hybrid-vsock 模块,从而连接上第一步中 kata-agent 端启动的 server。3.agent 端的 server 会通过 accept 得到该连接的 fd 和 host-port,并以 host-port 作为标识将建立的连接保存下来,现在 agent 端得到了 stdin,stdout 和 stderr 三条连接并分别以 stdin-port, stdout-port 和 stderr-port 作为标识。4.随后 kata-shim 在通过 rpc 调用 agent 的 create_container 的时候会将 stdin-port,stdout-port 和 stderr-port 作为 rpc 请求发送给 agent。5.agent 在接到 create_container 的 rpc 请求后通过 stdin-port,stdout-port 和 stderr-port 将第三步中保存的三条连接分别取出然后分别跟容器的 stdin,stdout 和 stderr 建立转发关系【注释1】。
借用 github pull request 的图片可以跟直观的了解以上的业务流程:

性能对比

在 IO 速度上,pass-fd IO 比原版 IO 提高了 185%。

在 CPU 上 pass-fd IO 比原版 IO 节省了 20%。
写在最后
passfd-io 在性能和资源消耗上都比 Kata 原本的 IO 实现有着较大的优势,如果您正在使用 Kata 并且 vmm 选用了 dragonball 的话大可以尽快切换到 passfd-io 上去了。
本文中提到的相关 pull requests
dragonball: vsock add fifo/pipe stream support for passed fd hybridStream[3]
vsock: passthrough fd support for hybrid mode[4]
runtime-rs: improving io performance using dragonball's vsock fd passthrough[5]
注释
为什么不一步到位,将三个 vsock socket 直接作为 container 的 stdin, stdout 和 stderr 呢?我们其实是尝试过这么做的,但是经过测试发现 Linux 并不允许将一个 socket 直接作为 stdio,socket 只能通过转发来作为 stdio。
References
[1]
@lifupan: https://github.com/lifupan[2]
@frezcirno: https://github.com/frezcirno[3]
dragonball: vsock add fifo/pipe stream support for passed fd hybridStream: https://github.com/kata-containers/kata-containers/pull/7585[4]
vsock: passthrough fd support for hybrid mode: https://github.com/openanolis/dragonball-sandbox/pull/278[5]
runtime-rs: improving io performance using dragonball's vsock fd passthrough: https://github.com/kata-containers/kata-containers/pull/7483