kuiniu 使用指南

nettools · GPU 互联网络质量探测工具(夔牛)

Usage Guide — 面向 AI 训练集群 GPU 网卡互联监控,单进程同时承担 client + server 角色。

← 返回首页

目录

1. 概述

Client Server Both kuiniu(夔牛)是面向 AI 训练集群 GPU 互联网络的探测工具。它专门绑定 GPU NIC 进行 RoCEv2/UDP 探测,回程严格走 GPU 网卡,确保探测路径与真实训练流量对称,能够发现 GPU 互联链路上的丢包、延迟抖动以及网卡 bitflip。

与 baize、bitflip 不同,kuiniu 把"GPU 对"作为一等公民:通过 local_gpu_addrsremote_gpu_addrs 平行数组定义本端 GPU 与远端 GPU 的对应关系,在同一进程中并发探测多对 GPU 的网络质量。在 role=both 模式下,单台机器既发包也回包,两台机器互相对称部署即可完成全双向监控。

核心特性:

面向 GPU 网络

  • 探测路径与训练流量一致
  • RoCEv2/UDP over GPU NIC
  • 多对 GPU 并行监控
  • 覆盖 ECMP 全路径

role=both 单进程双角色

  • client + server 共进程
  • 部署对称、配置最简
  • localGPUSet 自回声防护
  • 共享 pprof / 日志 / 信号

运维友好

  • JSON 配置文件驱动
  • util.RotateWriter 按天轮转
  • 终端 + 文件双输出
  • panic 恢复 + 优雅退出

2. 使用场景

kuiniu 主要面向 AI 训练集群中 GPU 网卡互联的健康度监控:

GPU 互联质量监控

  • 训练任务运行期间持续监测 GPU 间网络
  • 第一时间发现 RoCEv2 链路丢包/延迟抖动
  • 多对 GPU 同时探测,覆盖整机带宽

AI 训练集群网络保障

  • 训练前预检:探测异常即拒绝调度该机
  • 训练中:与训练流量对称的旁路监控
  • 训练后复盘:回看链路丢包对收敛的影响

GPU 网卡 bitflip 检测

  • 4 种 Salt 模式覆盖互补翻转盲区
  • 在 GPU NIC 路径上发现校验和漏检的改包
  • 对训练数值稳定性问题提供网络层证据

RoCE 网络改造验证

  • RoCE 网络变更前后对比
  • 验证 PFC / ECN 配置是否引入丢包
  • 定位 ECMP 路径上的故障端口

故障复现与排查

  • 训练 hang/慢节点时同步运行 kuiniu
  • 指定一对 GPU 做小规模高频探测
  • 结合 pprof 排查软件层瓶颈

GPU 网卡基线采集

  • 新机上线常态化探测建立质量基线
  • 横向对比同机型不同机器的 GPU 网络
  • 为大规模调度提供 GPU 网络可信度评分

3. 与 baize / bitflip 的区别

三个工具底层共享 sonar 探测引擎,但面向的网络层不同:

特性bitflipbaizekuiniu
面向网络通用 IP 网络通用 IP 网络GPU NIC 互联(RoCE)
配置方式命令行参数JSON 配置文件JSON 配置文件
地址模型client_addr + server_addrsclient_addr + server_addrslocal_gpu_addrs / remote_gpu_addrs(平行数组)
多目标多 server IP多 server IP多对 GPU(按下标对应)
进程角色单一角色单进程 client + serverrole=both 一等公民 + 自回声防护
典型部署临时排查baize.json 长期监控两台机器对称 kuiniu-machine-X.json
日志标准输出util.RotateWriter(文件)util.RotateWriter(终端 + 文件)
退出策略到达 count/duration 即退出到达限制后停止发送但进程不退同 baize,长跑友好
怎么选:普通业务网络的临时排查用 bitflip,机房间长期监控用 baizeAI 训练集群 GPU 互联监控用 kuiniu

4. 安装

从源码编译

# 克隆仓库
git clone https://github.com/baidu/nettools.git
cd nettools

# 编译全部工具
make compile

# 或单独编译 kuiniu
go build -o kuiniu ./cmd/kuiniu/

使用 GoReleaser 构建发行版

# 本地测试构建 (snapshot)
make snapshot

# 正式发布 (需要 git tag + GITHUB_TOKEN)
git tag v1.0.0
make deploy
Tip:kuiniu 仅在 Linux 上有意义(依赖 raw socket + GPU NIC 绑定)。macOS 仅作为开发/编译环境。

支持的平台

操作系统架构支持
LinuxAMD64✓ 生产可用
LinuxARM64✓ 生产可用
macOSAMD64 / ARM64仅开发/编译

5. 快速开始

1. 准备一对机器的配置文件

kuiniu 在两台机器上对称部署,把彼此的 GPU IP 互相填入对方的 remote_gpu_addrs。仓库 cmd/kuiniu/ 下提供了示例:

# 机器 A:把自己的 GPU IP 填到 local_gpu_addrs,机器 B 的 GPU IP 填到 remote_gpu_addrs
cp cmd/kuiniu/kuiniu-machine-a.json /etc/kuiniu/kuiniu.json

# 机器 B:与机器 A 镜像相反
cp cmd/kuiniu/kuiniu-machine-b.json /etc/kuiniu/kuiniu.json

2. 启动 kuiniu

# 使用配置文件启动(推荐)
sudo ./kuiniu -c /etc/kuiniu/kuiniu.json

# 也可以全部用命令行参数启动(不依赖配置文件)
sudo ./kuiniu --role both \
  --local-gpu 33.0.1.25,33.0.1.26 \
  --remote-gpu 33.0.2.27,33.0.2.28 \
  --log-dir /var/log/kuiniu
Note:kuiniu 需要 sudo 权限来创建 raw IP socket 和设置 IP TOS/DSCP。建议通过 systemd 以 root 身份长期运行。

3. 最小配置示例(role=both)

{
  "role": "both",
  "local_gpu_addrs": ["33.0.1.25"],
  "remote_gpu_addrs": ["33.0.2.27"]
}

命令行参数会覆盖配置文件中的同名字段,便于在自动化系统中按需调整:

# 用配置文件做基础,按需覆盖部分字段
sudo ./kuiniu -c /etc/kuiniu/kuiniu.json \
  --rate 10000 \
  --verbose

6. 配置文件详解

kuiniu 使用 JSON 格式的配置文件,通过 -c / --config 参数指定路径。所有字段都可通过同名命令行参数覆盖。

顶层字段

字段类型默认值说明
rolestring""角色:clientserverboth
local_gpu_addrs[]string[]本端 GPU IP 数组(IPv4),与 remote_gpu_addrs 等长
remote_gpu_addrs[]string[]远端 GPU IP 数组(IPv4),按下标与本端一一对应
tosint64IP TOS/DSCP 值
client_port_rangestring"43600,43699"客户端源端口范围 min,max
server_port_rangestring"43600,43609"服务端目的端口范围 min,max
rate_in_spanint645000每个 span 内所有 GPU 对总发包速率(pps)
spanstring"1s"统计时间窗口(Go duration 格式)
delaystring"3s"统计处理延迟,等待在途报文
msg_lenint1024消息体大小(含 44 字节头部)
countint0每对 GPU 最大发包数(0 = 不限制)
send_durationstring"0s"最大发送时长(0 = 不限制)
verboseboolfalse丢包时打印每个端口的详细信息
pprof_addrstring""pprof HTTP 监听地址(如 :6060
log_dirstring""日志目录,为空则只打到 stderr
log_max_age_daysint7日志保留天数(≤0 默认 7 天)

完整配置示例

{
  "pprof_addr": ":6060",
  "log_dir": "/var/log/kuiniu",
  "log_max_age_days": 7,
  "role": "both",
  "local_gpu_addrs": [
    "33.0.1.25", "33.0.1.26",
    "33.0.1.153", "33.0.1.154"
  ],
  "remote_gpu_addrs": [
    "33.0.2.27", "33.0.2.28",
    "33.0.2.155", "33.0.2.156"
  ],
  "tos": 64,
  "client_port_range": "43600,43699",
  "server_port_range": "44600,44609",
  "rate_in_span": 5000,
  "span": "1s",
  "delay": "3s",
  "msg_len": 1024,
  "verbose": false
}
Note:rate_in_span所有 GPU 对总速率,会在所有对之间均摊。例如 4 对 GPU、rate_in_span=5000,则每对约 1250 pps。

7. GPU 对配置

kuiniu 用两个等长数组 local_gpu_addrsremote_gpu_addrs 描述 GPU 探测对。下标 i 处的本端 IP 与远端 IP 组成一对探测目标:

"local_gpu_addrs":  ["33.0.1.25", "33.0.1.26", "33.0.1.153", "33.0.1.154"],
"remote_gpu_addrs": ["33.0.2.27", "33.0.2.28", "33.0.2.155", "33.0.2.156"]

# 等价于以下 4 对探测:
#   33.0.1.25  <-> 33.0.2.27
#   33.0.1.26  <-> 33.0.2.28
#   33.0.1.153 <-> 33.0.2.155
#   33.0.1.154 <-> 33.0.2.156

对称部署:机器 A vs 机器 B

两台机器互为对端时,local_gpu_addrsremote_gpu_addrs 互换即可:

# 机器 A 的 kuiniu.json
{
  "role": "both",
  "local_gpu_addrs":  ["33.0.1.25", "33.0.1.26"],
  "remote_gpu_addrs": ["33.0.2.27", "33.0.2.28"]
}

# 机器 B 的 kuiniu.json — 完全镜像
{
  "role": "both",
  "local_gpu_addrs":  ["33.0.2.27", "33.0.2.28"],
  "remote_gpu_addrs": ["33.0.1.25", "33.0.1.26"]
}
Tip:仓库 cmd/kuiniu/kuiniu-machine-a.jsonkuiniu-machine-b.json 是两台机器对称部署的真实配置示例,可直接修改 IP 后使用。

同一对 GPU 重复出现

把同一个 GPU IP 在数组里重复填多次,可以让该对 GPU 占据更多速率配额,用于强化重点链路的探测:

"local_gpu_addrs":  ["33.0.1.25", "33.0.1.25", "33.0.1.26"],
"remote_gpu_addrs": ["33.0.2.27", "33.0.2.27", "33.0.2.28"]
# 33.0.1.25 -> 33.0.2.27 占 2/3 速率,33.0.1.26 -> 33.0.2.28 占 1/3

8. role=both 互探模式

role=both 是 kuiniu 的一等公民:在同一进程内并发跑 client 和 server 两个 goroutine,部署一份配置即可获得双向监控。

启动行为

自回声防护:localGPUSet

在 role=both 模式下,本机 server 会同时收到本机 client 自己发出的探测包;如果不加防护,会把本机自身的发包当成对端的真实探测包做回包,导致自回环误判。kuiniu 通过 localGPUSet 解决:

// 启动时把所有 local_gpu_addrs 装入集合
localGPUSet := make(map[string]struct{})
for _, addr := range cfg.LocalGPUAddrs {
    localGPUSet[addr] = struct{}{}
}

// server 收包时丢弃源 IP 命中本机集合的报文
if _, ok := localGPUSet[srcIP.String()]; ok {
    // 本机 client 自己发的包,直接丢弃
    continue
}

典型部署:两台 8 卡 GPU 机器互探

# 机器 A(8 张 GPU 卡,IP 33.0.1.25-32)
{
  "role": "both",
  "log_dir": "/var/log/kuiniu",
  "local_gpu_addrs":  [
    "33.0.1.25", "33.0.1.26", "33.0.1.27", "33.0.1.28",
    "33.0.1.29", "33.0.1.30", "33.0.1.31", "33.0.1.32"
  ],
  "remote_gpu_addrs": [
    "33.0.2.25", "33.0.2.26", "33.0.2.27", "33.0.2.28",
    "33.0.2.29", "33.0.2.30", "33.0.2.31", "33.0.2.32"
  ],
  "tos": 64,
  "rate_in_span": 8000,
  "span": "1s",
  "delay": "3s"
}
Tip:role=both 启动时日志中会同时出现 kuiniu client startedkuiniu server started。如果只看到一个,说明另一侧初始化失败,需要查 [ERRO] 行的具体原因。

9. 使用示例

仅 client 模式

用于一端只发不收的场景,例如对端是已经独立运行的 kuiniu server:

{
  "role": "client",
  "local_gpu_addrs":  ["33.0.1.25", "33.0.1.26"],
  "remote_gpu_addrs": ["33.0.2.27", "33.0.2.28"],
  "rate_in_span": 5000,
  "span": "1s",
  "delay": "3s"
}

仅 server 模式

{
  "role": "server",
  "local_gpu_addrs":  ["33.0.2.27", "33.0.2.28"],
  "remote_gpu_addrs": ["33.0.1.25", "33.0.1.26"],
  "span": "1s",
  "delay": "3s"
}

互探模式(推荐)

{
  "pprof_addr": ":6060",
  "log_dir": "/var/log/kuiniu",
  "role": "both",
  "local_gpu_addrs":  ["33.0.1.25", "33.0.1.26"],
  "remote_gpu_addrs": ["33.0.2.27", "33.0.2.28"],
  "tos": 64,
  "rate_in_span": 5000
}

限时探测:跑 5 分钟即停发

{
  "role": "both",
  "local_gpu_addrs":  ["33.0.1.25"],
  "remote_gpu_addrs": ["33.0.2.27"],
  "send_duration": "5m"
}
Note:到达 send_durationcount 后 client 停止发包,但进程不退出,server 继续工作。需要彻底退出请发送 SIGINT/SIGTERM。

调试模式:开启 pprof + verbose

{
  "pprof_addr": ":6060",
  "log_dir": "/var/log/kuiniu",
  "role": "both",
  "local_gpu_addrs":  ["33.0.1.25"],
  "remote_gpu_addrs": ["33.0.2.27"],
  "verbose": true
}
# 查看 goroutine
go tool pprof http://localhost:6060/debug/pprof/goroutine

# 查看 CPU profile
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30

# 查看堆内存
go tool pprof http://localhost:6060/debug/pprof/heap

10. 日志管理

当配置了 log_dir 时,kuiniu 通过通用工具 util.RotateWriter 写日志,自动按天轮转、维护 symlink、清理过期文件。kuiniu 同时把日志输出到 stderr 和文件(io.MultiWriter(os.Stderr, logWriter)),方便 systemd journal 与磁盘文件双重留痕。

日志文件命名

/var/log/kuiniu/
├── kuiniu.log          # symlink → 当天日志
├── kuiniu.log.20260601 # 按天轮转
├── kuiniu.log.20260602
├── kuiniu.log.20260603
└── kuiniu.log.20260604 # 当天

轮转机制

配置示例

{
  "log_dir": "/var/log/kuiniu",
  "log_max_age_days": 7
}
Tip:该轮转能力由 util.RotateWriter 提供,是 nettools 仓库中的通用工具,baize 也共用同一份实现。
Note:未配置 log_dir 时,日志只输出到 stderr;适合容器化部署中由日志收集器统一采集。

11. FAQ

Q: kuiniu 和 baize / bitflip 该选哪个?

看探测目标:通用业务网络选 baize(长期)或 bitflip(临时);AI 训练集群 GPU 互联kuiniu。kuiniu 把"GPU 对"作为一等公民并提供 role=both 自回声防护,更贴合 GPU NIC 的部署形态。

Q: 为什么不用 baize 也来探 GPU?

可以,但 baize 不理解"GPU 对"语义,需要自行展开为 N 份 client/server 配置;并且 baize 没有针对 role=both 的自回声防护,多对 GPU 同机部署容易把本机发包当对端探测包处理。kuiniu 在配置层与运行层都对 GPU 网络做了针对性优化。

Q: role=both 模式下 client 自己发的包会被本机 server 误处理吗?

不会。kuiniu 在启动时构造 localGPUSet,把所有 local_gpu_addrs 装入集合;server 收到源 IP 命中集合的报文会直接丢弃。详见第 8 节 role=both 互探模式

Q: local_gpu_addrs 与 remote_gpu_addrs 必须等长吗?

是的。两者按下标一一对应组成 GPU 探测对。如果长度不一致,cfg.Validate() 会直接返回错误并阻止启动。

Q: 一对机器对称部署时,两边的 client_port_range 和 server_port_range 应该一致吗?

建议一致。两台机器的 client 端口范围一致,server 端口范围一致,可以让 ECMP 哈希行为对称、丢包定位时不容易看花眼。

Q: rate_in_span 是单对 GPU 的速率还是总速率?

所有 GPU 对总速率。例如 4 对 GPU、rate_in_span=5000,则每对约 1250 pps。要想加大单对速率,可以减少 GPU 对数量或重复填同一对 IP。

Q: 配置文件修改后需要重启吗?

需要。kuiniu 不支持热加载。建议用 systemd 管理:sudo systemctl restart kuiniu

Q: 如何优雅停止 kuiniu?

发送 SIGINT (Ctrl+C) 或 SIGTERM。程序会取消所有 goroutine 并睡眠 1 秒等在途报文回来后退出。

Q: 为什么 kuiniu 需要 sudo?

kuiniu 使用 raw IP socket 发送 RoCEv2/UDP 探测包,并需要设置 IP TOS/DSCP。Linux 上需要 CAP_NET_RAW;最简单的做法就是用 sudo 或 systemd 以 root 启动。

Q: client 或 server 启动失败会让进程退出吗?

不会。kuiniu 在 client/server Run() 返回错误时只打印 [ERRO] 日志,不退出主进程。这避免了一侧瞬时故障导致整机失去监控能力。

Q: 日志文件会无限增长吗?

不会。日志按天轮转,超过 log_max_age_days(默认 7 天)的文件在轮转时自动删除。