gVisor tracereplay:将 runsc trace 会话落盘、回放与复用的完整实践
2026/9/13 10:58:07 网站建设 项目流程

gVisor tracereplay:将 runsc trace 会话落盘、回放与复用的完整实践

【免费下载链接】gvisorApplication Kernel for Containers项目地址: https://gitcode.com/GitHub_Trending/gv/gvisor

tracereplay是 gVisor 仓库内置的一个命令行工具,用于把通过remotesink 发出的runsc trace会话消息保存到本地文件,并在之后反复回放完全相同的消息序列。它解决的核心问题是:调试和测试 trace 消息时,往往需要配置好 runsc、配置 trace 会话并运行特定工作负载,成本很高。读完本文,你将掌握tracereplay save/tracereplay replay两个子命令的完整用法、trace 文件的二进制格式,以及如何结合examples/seccheck中的示例服务端查看回放的具体消息内容。

核心定位:为 trace 消息测试摆脱环境依赖

工具自身的包注释说明得很直接:它实现的正是“save and replay messages issued from remote.Remote”(tracereplay.go)。也就是说,它的工作对象是 gVisor 的seccheck 事件子系统remote类型 sink 发出的消息流。

典型工作流分两段:

  1. 录制(save)tracereplay save启动一个监听 runsc 连接的服务器,为每个连上来的 runsc 实例创建一个 trace 文件,把该实例触发的所有 trace 消息写入其中。
  2. 回放(replay)tracereplay replay连接到一个远端端点(例如某个检查器或服务端),把 trace 文件里的消息按原始顺序逐条发出去——此时完全不需要 runsc、不需要配置 trace 会话、也不需要运行原始工作负载。

这使得依赖特定 trace 消息的测试可以脱离完整运行时环境独立运行。

save:把 trace 会话写进文件

tracereplay save启动一个监听服务器,接受 runsc 发起的新连接,并为每个 runsc 实例单独创建一个 trace 文件。下面的示例在/tmp/gvisor_events.sock上监听,并把 trace 文件写入/tmp/trace目录:

$ tracereplay save --endpoint=/tmp/gvisor_events.sock --out=/tmp/trace

完整的命令行参数(定义在 main.go)如下:

参数默认值说明
--endpoint(必填)服务器监听的 Unix 域套接字路径。runsc 的remotesink 需要配置为连接同一个路径。启动前会先删除同名残留 socket 文件
--out./replaytrace 文件的保存目录,不存在时会自动创建
--prefixclient-每个 trace 文件的文件名前缀

服务器启动后会打印Listening on "…". Press ctrl-C to stop...,之后每来一个 runsc 连接,就会打印一行New client connected, writing to: "…",并在该客户端断开时打印写入的消息总数(见 save.go 中NewClientClose的日志逻辑)。

让 runsc 连接到这个服务器

关键配置在 runsc 的 pod-init 配置里:使用trace_session,在其中声明要采集的 trace 点,并把 sink 配置为remote类型、endpoint指向tracereplay save监听的套接字路径。完整示例如下:

$ cat > /tmp/pod_init.json <<EOL { "trace_session": { "name": "Default", "points": [ { "name": "container/start" } ], "sinks": [ { "name": "remote", "config": { "endpoint": "/tmp/gvisor_events.sock" } } ] } } EOL $ runsc --rootless --network=none --pod-init-config=/tmp/pod_init.json do /bin/true

配置说明:

  • points指定要采集的 trace 点,上例只采集container/start一个点;实际使用中可按需追加(trace 点的定义见 seccheck 包);
  • sinksnameremoteconfig.endpoint必须与tracereplay save --endpoint的路径完全一致。

执行成功后,tracereplay save一侧应看到:

New client connected, writing to: "/tmp/trace/client-0001" Closing client, wrote 1 messages to "/tmp/trace/client-0001"

这表示 runsc 连上了服务器、触发了 1 条 trace 消息(即container/start点),并且连接已正常关闭。

trace 文件二进制格式

理解文件格式有助于排查问题,也能解释为什么replay能“按原样”复现消息。从 save.go 中NewClient的注释与实现看,文件结构为:

signature <size>Config JSON [<size>message]*
  • 文件开头是固定字符串签名tracereplay file(tracereplay.go),用于快速识别这是否是一个合法的 trace 文件;
  • 签名后是一段 JSON 配置,包含Config.Version字段,即录制时的 wire 协议版本(见 Config 定义);
  • 之后是消息序列,每条消息前面都有一个8 字节小端 uint64 长度前缀

两个健壮性细节值得注意:

  • readSize对读到的长度做了上限保护,超过 1 MiB 直接报错,防止损坏或恶意文件导致内存耗尽(tracereplay.go);
  • 文件名按客户端序号递增生成(<prefix>0001<prefix>0002……),序号来自原子计数器(save.go),因此多个 runsc 实例先后连接同一个 save 服务器时互不覆盖。

replay:反复回放同一份消息

拿到 trace 文件后,tracereplay replay可以在任何时间、任意多次地回放完全相同的消息序列。参数(main.go):

参数默认值说明
--endpoint(必填)回放目标端点的 Unix 域套接字路径,例如正在运行的检查服务器
--in(必填)要回放的 trace 文件路径

使用上文录制出的文件回放:

$ tracereplay replay --endpoint=/tmp/gvisor_events.sock --in=/tmp/trace/client-0001 Handshake completed Replaying message: 1 Done

从 replay.go 的Execute可以看到回放的完整流程:

  1. AF_UNIX+SOCK_SEQPACKET创建套接字并连接--endpoint(与 seccheck remote 协议保持一致的传输方式);
  2. 校验文件签名,读入带长度前缀的 JSON 配置,取出录制时的协议版本;
  3. 与对端完成Handshake 版本交换:发送pb.Handshake{Version: …}并等待对端回应。replay 侧对服务器回传的任意版本都会接受(代码注释明确说明“只验证消息可反序列化,接受任何版本,然后尝试回放看结果”);而服务器侧则会严格校验客户端版本必须等于wire.CurrentVersion(当前为 1,见 server.go 与 wire.go);
  4. 循环读取后续所有带长度前缀的消息,逐条写回套接字,直到文件末尾,打印Done

每条 trace 消息内部还带有 wire 头(HeaderSize/MessageType/DroppedCount,见 wire.Header),save只保存未解析的原始字节,因此回放时服务端能像处理 runsc 实时消息一样处理这些消息。

用示例服务端查看消息内容

如果想直观看到 trace 文件里存的是什么,可以用仓库自带的示例服务端 examples/seccheck/server.cc(构建目标examples/seccheck:server_cc)。它的监听地址正好也是/tmp/gvisor_events.sock,启动后再执行上面的 replay 命令:

$ bazel run examples/seccheck:server_cc Socket address /tmp/gvisor_events.sock Connection accepted Start => id: "runsc-865139" cwd: "/home/fvoznika" args: "/bin/true" Connection closed

输出的Start => id: … cwd: … args: …正是被录制下来的container/starttrace 点事件,字段与当时 runsc 实例的元数据一致。

测试如何验证录制/回放的正确性

仓库自带了一个往返测试 tracereplay_test.go:TestBasic先启动一个NewSave服务器,然后用Replay把预生成的样例文件 testdata/client-0001 回放进该服务器,最后断言“重新保存出的文件与原始文件逐字节相同”。也就是说,整个工具链被验证为无损的replay original | save new ⇒ original == new过程。这个测试也给出了一个可复制的用法:把预录制的tools/tracereplay/testdata/client-0001当作现成的回放输入即可。

小结与路径索引

  • 工具入口与子命令:tools/tracereplay/main/main.go、原始文档 tools/tracereplay/README.md
  • 文件格式与读写实现:tools/tracereplay/tracereplay.go、tools/tracereplay/save.go、tools/tracereplay/replay.go
  • 通用远端服务器(连接管理、handshake、消息分发):pkg/sentry/seccheck/sinks/remote/server/server.go
  • wire 协议版本与消息头:pkg/sentry/seccheck/sinks/remote/wire/wire.go
  • 示例查看服务端与配套示例配置:examples/seccheck/server.cc、examples/seccheck/pod_init.json
  • 往返正确性测试与预录制样例:tools/tracereplay/tracereplay_test.go、tools/tracereplay/testdata/client-0001

适用前提:tracereplay走的是 seccheckremotesink 的 Unix 域套接字通道,因此 trace 会话必须配置remote类型 sink 且endpoint与 save 服务器的--endpoint一致;回放端需要有一个实现了相同 wire 协议的服务端在监听。基于SOCK_SEQPACKET的特性,该协议适用于本机套接字场景,跨主机传输需要额外手段。

【免费下载链接】gvisorApplication Kernel for Containers项目地址: https://gitcode.com/GitHub_Trending/gv/gvisor

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询