[快讯] ttrpc-rust进入containerd, kata agent 有望将内存开销控制在1MB以下
在经过漫长的流程之后,蚂蚁金服开源的 ttrpc-rust 成为了 containerd 的子项目,并准备用于 Kata 2.0 的 Rust Agent,根据先前的测试,使用 ttrpc 的 rust-agent 的匿名页内存占用大约是500-800KB,也就是说,未来的 rust-agent 的内存开销可以控制在 1MB 以下。
快速信息
Repo: https://github.com/containerd/ttrpc-rust
Crates: https://crates.io/crates/ttrpc
Maintainers: The Ant Financial Kata Team
致谢: 因为同样使用 protobuf 作为序列化协议,编译器部分基于 PingCAP 的 grpc-rs
什么是 ttRPC
简单地说,ttRPC 可以被看作是一个更节约内存的 gRPC,和 gRPC 一样使用 Protocol Buffer 作为跨语言序列化协议,但是没有复杂的 HTTP/2 协议实现。ttRPC 最初由 containerd 项目使用 Go 语言实现,用于 containerd 和 shim 的通信,因为这一通信一般在本机完成,也没有高并发场景,所以,可以考虑用更简单的协议来节约资源。
为什么用 Rust
Rust 是一个高性能、内存安全的系统级语言,沿袭了 C++ 的“零开销抽象”的高效原则,不仅具有系统层操作能力,提供了丰富的语言表达力,而且通过严格的资源生命周期管理,在不使用 GC 的情况下保证了内存安全性。这些优异的特性使得 Rust 即使学习曲线陡峭,仍然让众多自命不凡的系统程序员和区块链爱好者趋之若鹜。
Kata Agent 的选择
Agent 简史:从 C 到 Go 再到 Rust
第一代 Agent 是在前 Kata 时代,蚂蚁 Kata 团队的前身用 C 写了 hyperstart (github.com/hyperhq/hyperstart ),作为轻量级虚拟化中的 init 进程,Kata 的两个前身 runV 和 Clear Containers 都使用了这个进程。hyperstart 最大的优势是轻量级,但是,C 语言表达能力有限且本身不是内存安全语言,在完全兼容 OCI 语义和保证更轻量可控之间,我们选择了后者。于是,我们在发布 Kata 的时候做了一个选择——
第二代 Agent 是我们在发布 Kata 的时候,一致选择了 Go 语言,带来的好处是
可以使用 libcontainer 完全兼容 OCI 语义,毕竟 OCI 语义最早就是用 libcontainer 定义的;
可以使用 gRPC 承载协议,不需要自己处理通信进程,提升通信可靠性。
然而,这也带来了一些问题,最显著的问题就是这个 kata-agent 是远大于 hyperstart 的,所以,我们在2019年年中,开始了下一步选择。
第三代 Agent 是我们在 Kata 1.10 的时候,合并了蚂蚁 Kata 团队贡献的 rust-agent,这个版本虽然摒弃了虚拟串口的支持,只保留了 vsock 一种链接方式,但兼容了已有的 gRPC agent protocol,和其他 Kata 组件可以完美地工作在一起,匿名页内存开销从大约 11MB下降到 1.1MB 起。
从 gRPC 到 ttRPC
虽然换用语言重新实现已经让 kata-agent 大幅度降低了,但我们仍然没有停止改进的步伐,主要有这些原因:
1.1MB 虽然不大,但毕竟在 1MB 心理线以上,我们希望降低到 sub-mega。而且,随着功能增加、Pod内容器增加、Pod Spec变复杂,都有可能让这个开销进一步扩大,因此,我们应该再进一步压缩不必要的开销。
目前最成熟的来自 PingCAP 的 gRPC-rs 的核心链接了 gRPC 的 C++ 语言库,虽然保证了高性能全功能,并且我们也为上游开发了支持 vsock 的补丁让它可用,但是一方面链接尺寸比较大,另外一方面,链接很复杂,尤其在链接轻量级的 musl 库的时候就更复杂了。
继承来 gRPC 的丰富功能和高性能,对于虚机内外的简单通信来说是能力过剩的,却带来了额外开销。
可以说,gRPC 很好,但对这个场合来说不是最恰当的,在这个情况下我们尝试实现了 Rust 版本的 ttRPC,并以 kata rust-agent 和 Go 版本的 shim-v2 作为第一个应用来验证了这个实现,支持 Go/Rust 之间的实现互通,也支持包括 vsock 在内的各种通信方式,最小内存开销可以达到 500KB 左右。
上游化
在开发完成后,Kata 社区内讨论决定,首选将 ttrpc-rust 贡献回到 ttRPC 的发祥地——containerd 社区,如果由于某种原因 containerd 社区不能接受,我们会将它放在 kata 社区。不过事实证明我们的担心是多余的:
https://github.com/containerd/project/issues/40
在经过一系列讨论、修订之后,ttrpc-rust 终于进入了 containerd 社区
https://github.com/containerd/ttrpc-rust/
因为修改 agent 的 RPC 协议是不向前兼容的,所以根据我们的计划,Kata rust-agent 会在 2.0 开发周期中开始使用 ttrpc-rust。
附:关于内存占用的说明
这里的内存占用指归属到进程的匿名页,也就是堆内存,代码二进制本身的只读内存通过DAX是可以共享的,因此没有计算在内;
这里的内存开销测量是开发中的测量结果,一般测试情况为单一 Pod 运行一个 ubuntu 容器,不同版本、不同 Pod Spec 会有不同。