ingero-io/ingero
GitHub: ingero-io/ingero
基于 eBPF 的 GPU 因果可观测性代理,零代码修改即可追踪从 Linux 内核事件到 CUDA API 调用再到 Python 源码行的完整因果链,实现低开销的 GPU 工作负载故障诊断。
Stars: 79 | Forks: 8
# Ingero - GPU 因果可观测性
[](https://goreportcard.com/report/github.com/ingero-io/ingero)
[](LICENSE)
[](https://github.com/ingero-io/ingero/releases)
[](https://github.com/ingero-io/ingero/actions/workflows/ci.yml)
[](https://glama.ai/mcp/servers/ingero-io/ingero)
**收录在:**
[awesome-ebpf](https://github.com/qmonnet/awesome-ebpf) ·
[awesome-observability](https://github.com/adriannovegil/awesome-observability) ·
[awesome-opentelemetry](https://github.com/magsther/awesome-opentelemetry) ·
[awesome-sre-tools](https://github.com/SquadcastHub/awesome-sre-tools) ·
[awesome-cloud-native](https://github.com/rootsongjc/awesome-cloud-native) ·
[awesome-profiling](https://github.com/msaroufim/awesome-profiling) ·
[Awesome-GPU](https://github.com/Jokeren/Awesome-GPU) ·
[awesome-gpu-engineering](https://github.com/goabiaryan/awesome-gpu-engineering) ·
[awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers) ·
[awesome-devops-mcp-servers](https://github.com/rohitg00/awesome-devops-mcp-servers) ·
[MCP Registry](https://registry.modelcontextprotocol.io) ·
[Glama](https://glama.ai/mcp/servers/ingero-io/ingero) ·
[mcpservers.org](https://mcpservers.org)
**版本: 0.12.7**
**唯一能与你 AI 助手交流的 GPU 可观测性工具。**
*"是什么导致了 GPU 停顿?" → "`train.py:142` 处的 `forward()` - 在 CPU 争用期间 cudaMalloc 激增至 48ms。9,829 次调用,847 次调度器抢占。"*
Ingero 是一个生产级的 eBPF 代理,它能追踪完整的链条 - 从 Linux 内核事件到 CUDA API 调用,再到你的 Python 源码行 - 实现 **<2% 的性能开销**、**零代码修改**,并且只需**一个二进制文件**。
## 架构
代理在每个 GPU 节点上运行,并通过 OTLP 将数据发送到单个 Fleet 收集器副本(或位于一致性哈希负载均衡器后面的高可用集群)。Fleet 负责处理同级别相对阈值和问题成本富化;Prometheus / Grafana / MCP 从 Fleet 读取数据,而不是直接从代理读取。UDS 落后节点事件流到本地接收器(`straggler-sink`、`ingero-alerter`)以进行修复。
## 快速开始 (单节点)
```
# 安装 (Linux amd64 — 关于 arm64/Docker 见下文)
# ingero-version:install-curl product=ingero channel=stable
VERSION=0.12.7
curl -fsSL "https://github.com/ingero-io/ingero/releases/download/v${VERSION}/ingero_${VERSION}_linux_amd64.tar.gz" | tar xz
sudo mv ingero /usr/local/bin/
# 追踪你的 GPU 工作负载
sudo ingero trace
# 诊断发生了什么
ingero explain --since 5m
```
- **"为什么":** 将 `cudaStreamSync` 的激增与 `sched_switch` 事件关联起来 - 主机内核抢占你的线程。
- **"在哪里":** 将 CUDA 调用映射回你的 PyTorch `forward()` 过程中的 **Python 源码行**。
- **"隐藏的内核":** 跟踪 CUDA Driver API,查看由 cuBLAS/cuDNN 发起的、绕过标准分析器的内核启动。
无需 ClickHouse,无需 PostgreSQL,无需 MinIO - 只需一个静态链接的 Go 二进制文件和内嵌的 SQLite。
查看[真实的 AI 调查会话](docs/ml_eng_sample_investigation_session.md) - 一个 AI 助手仅使用 Ingero 的 MCP 工具诊断 A100 和 GH200 上的 GPU 训练问题。无需 Shell 访问,无需手动编写 SQL - 只需问答。
## 快速开始 (多节点 GPU 集群)
三份多节点快速入门指南可以让你在 20 分钟内,从零开始在三个 GPU 主机上检测落后节点。选择与你的环境相匹配的部署方式:
- [Kubernetes (Helm)](https://github.com/ingero-io/ingero-fleet/blob/main/docs/quickstart-k8s.md)
- [裸机二进制文件](https://github.com/ingero-io/ingero-fleet/blob/main/docs/quickstart-binary.md)
- [Docker](https://github.com/ingero-io/ingero-fleet/blob/main/docs/quickstart-docker.md)
如果你不知道该如何选择,请查看 [`docs/quickstart.md`](https://github.com/ingero-io/ingero-fleet/blob/main/docs/quickstart.md) 中的一页式对比说明。作为这些指南后盾的集群端服务是 [Ingero Fleet Collector](#ingero-fleet-collector-cross-node-straggler-detection) - 请参阅下方该部分以了解其架构。
## 它的功能
Ingero 使用 eBPF 在三个层面上跟踪 GPU 工作负载,从 `/proc` 读取系统指标,并组装出能够解释根本原因的因果链:
1. **CUDA Runtime uprobes** - 通过对 `libcudart.so` 的 uprobes 跟踪 `cudaMalloc`、`cudaFree`、`cudaLaunchKernel`、`cudaMemcpy`、`cudaMemcpyAsync`、`cudaStreamSync` / `cudaDeviceSynchronize`
2. **CUDA Driver uprobes** - 通过对 `libcuda.so` 的 uprobes 跟踪 `cuLaunchKernel`、`cuMemcpy`、`cuMemcpyAsync`、`cuCtxSynchronize`、`cuMemAlloc`。捕获绕过 Runtime API 直接由 cuBLAS/cuDNN 调用发起的内核启动。
3. **CUDA Graph 生命周期 uprobes** - 跟踪 `cudaStreamBeginCapture`、`cudaStreamEndCapture`、`cudaGraphInstantiate`、`cudaGraphLaunch`,用于在 `torch.compile` 和 vLLM 工作负载中提供 Graph 捕获/重放的可见性。
4. **主机 tracepoints** - 跟踪 `sched_switch`、`sched_wakeup`、`mm_page_alloc`、`oom_kill`、`sched_process_exec/exit/fork`,用于监控 CPU 调度、内存压力和进程生命周期。
5. **系统上下文** - 从 `/proc` 读取 CPU 利用率、内存使用情况、负载平均值和交换分区(无需 eBPF,无需 root 权限)。
**因果引擎** 通过时间戳和 PID 跨层关联事件,以生成带有严重性分级和修复建议的自动化根因分析。
```
$ sudo ingero trace
Ingero Trace - Live CUDA Event Stream
Target: PID 4821 (python3)
Library: /usr/lib/x86_64-linux-gnu/libcudart.so.12
CUDA probes: 14 attached
Driver probes: 10 attached
Host probes: 7 attached
System: CPU [████████░░░░░░░░░░░░] 47% | Mem [██████████████░░░░░░] 72% (11.2 GB free) | Load 3.2 | Swap 0 MB
CUDA Runtime API Events: 11,028
┌──────────────────────┬────────┬──────────┬──────────┬──────────┬─────────┐
│ Operation │ Count │ p50 │ p95 │ p99 │ Flags │
├──────────────────────┼────────┼──────────┼──────────┼──────────┼─────────┤
│ cudaLaunchKernel │ 11,009 │ 5.2 µs │ 12.1 µs │ 18.4 µs │ │
│ cudaMalloc │ 12 │ 125 µs │ 2.1 ms │ 8.4 ms │ ⚠ p99 │
│ cudaDeviceSynchronize│ 7 │ 684 µs │ 1.2 ms │ 3.8 ms │ │
└──────────────────────┴────────┴──────────┴──────────┴──────────┴─────────┘
CUDA Driver API Events: 17,525
┌──────────────────────┬────────┬──────────┬──────────┬──────────┬─────────┐
│ Operation │ Count │ p50 │ p95 │ p99 │ Flags │
├──────────────────────┼────────┼──────────┼──────────┼──────────┼─────────┤
│ cuLaunchKernel │ 17,509 │ 4.8 µs │ 11.3 µs │ 16.2 µs │ │
│ cuMemAlloc │ 16 │ 98 µs │ 1.8 ms │ 7.1 ms │ │
└──────────────────────┴────────┴──────────┴──────────┴──────────┴─────────┘
Host Context Events: 258
┌─────────────────┬────────┬──────────────────────────────────────────┐
│ Event │ Count │ Detail │
├─────────────────┼────────┼──────────────────────────────────────────┤
│ mm_page_alloc │ 251 │ 1.0 MB allocated (order-0: 251) │
│ process_exit │ 7 │ 7 processes exited │
└─────────────────┴────────┴──────────────────────────────────────────┘
⚠ cudaStreamSync p99 = 142ms - correlated with 23 sched_switch events
(GPU thread preempted during sync wait, avg 2.1ms off-CPU)
```
## 你会发现什么
其他 GPU 工具无法向你展示的内容。
**"cuBLAS 正在启动 17,509 个内核,而你一个都看不到。"** 大多数分析器只跟踪 CUDA Runtime API - 但 cuBLAS 直接调用 `cuLaunchKernel`(Driver API),绕过了 Runtime。Ingero 同时跟踪这两层:11,009 个 Runtime 调用 + 17,509 个 Driver 调用 = 完全可见每一个内核启动。
**"你的训练变慢了,因为 logrotate 偷走了 4 个 CPU 核心。"** System Context 显示 CPU 占用 94%,负载达到 12.1。CUDA 表显示 cudaStreamSync 的 p99 延迟从 16ms 跃升至 142ms。Host Context 显示有 847 个 `sched_switch` 事件。`ingero explain` 组装出完整的因果链:logrotate 抢占了训练进程 → CUDA 同步停滞 → 训练吞吐量下降 30%。修复方案:`nice -n 19 logrotate`,或者将训练绑定到专用的核心。
**"你的模型将 38% 的挂钟时间花在了数据移动上,而不是计算上。"** nvidia-smi 显示 "GPU 利用率 98%",但 GPU 正忙于执行 cudaMemcpy,而不是计算。Ingero 的时间片段细分让这一点显而易见。其修复方案(锁页内存、异步传输、更大的 batch size)可节省 30-50% 的挂钟时间。
**"你的主机正在进行 swap 交换,而你的 GPU 却一无所知。"** System Context 显示 Swap 使用了 2.1 GB。cudaMalloc p99 延迟从 0.02ms 上升到 8.4ms。没有任何 GPU 工具会显示这一点 - nvidia-smi 会显示 GPU 内存一切正常,但主机端的 CUDA 预留记账信息却正在经历 swap。
**"你的 vLLM 推理激增,因为新的 batch size 触发了 CUDA Graph 重新捕获。"** Ingero 通过 eBPF uprobes 跟踪 `cudaStreamBeginCapture` / `cudaGraphLaunch` - 无需 CUPTI,无需 Nsight,也无需修改代码。当 GraphLaunch 频率下降 50% 时,Ingero 会标记 Graph 池耗尽。当捕获与 OOM 重叠时,因果链将解释原因。开箱即支持 `torch.compile(mode="reduce-overhead")` 和 vLLM。
**"Rank 3 停顿了 200ms,而 ranks 0-2 都在等待 - 一个查询就能显示所有 4 个节点。"** 借助 `ingero query --nodes`,一条命令即可扇出分发到集群中的每个节点并合并结果。`ingero merge` 可以组合离线数据库以进行气隙环境分析。`ingero export --format perfetto` 可生成能在 Perfetto UI 中打开的时间线 - 每个节点/rank 一个轨道,便于立即发现落后节点。节点间的时钟偏移会被自动检测。
**"问问你的 AI:我代码的哪一行导致了 GPU 停顿?"** 你的 AI 助手调用 Ingero 的 MCP server,一击即中回答出:"问题出在 `train.py:142` 的 `forward()` 处,通过 PyTorch 调用了 cudaMalloc。共 9,829 次调用,平均 3.1ms,但在 CPU 争用期间会激增至 48.3ms。" 已解析的 Python 源码行、原生符号、时序统计 - 无需查看日志,无需手写 SQL,也没有十六进制地址。工程师用纯英语提问,就能得到生产环境的根因回复。
## 观看演示
```
ingero demo # run all 6 scenarios (auto-detects GPU)
ingero demo incident # full causal chain in 30 seconds
ingero demo --no-gpu # synthetic mode (no root, no GPU needed)
sudo ingero demo --gpu # real GPU + eBPF tracing
```
### 场景
| 场景 | 揭示的内容 |
|----------|----------------|
| `incident` | CPU 激增 + sched_switch 风暴 → cudaStreamSync 延迟激增 8.5 倍 → 包含根因和修复方案的完整因果链 |
| `cold-start` | 首次 CUDA 调用耗时比稳定状态长 50-200 倍(CUDA 上下文初始化) |
| `memcpy-bottleneck` | cudaMemcpy 占据了 38% 的挂钟时间,而不是计算 - nvidia-smi 掩盖了这一事实 |
| `periodic-spike` | cudaMalloc 每隔大约 200 个 batch 会激增 50 倍(PyTorch 缓存分配器) |
| `cpu-contention` | 主机 CPU 抢占导致 CUDA 延迟激增 |
| `gpu-steal` | 通过 CUDA API 时序模式量化的多进程 GPU 时间切片 |
每个场景都会打印一个 GPU 自动检测标头,显示 GPU 型号和驱动版本,然后显示系统上下文的实时 ASCII 条形图。
**本 README 涵盖的是单节点 GPU 追踪与调查。** 如需了解多节点分布式训练诊断(跨节点扇出查询、离线数据库合并、Perfetto 时间线导出、时钟偏移检测),请参阅[多节点调查演练](investigations/README.md#multi-node-investigation-walkthrough)。
## 安装
### 二进制发布版本 (推荐)
从 [GitHub Releases](https://github.com/ingero-io/ingero/releases/latest) 下载预编译的二进制文件。
压缩文件名包含版本号:`ingero__linux_.tar.gz`。将下方的 `VERSION` 替换为最新版本(例如,`0.10.0`):
```
# Linux amd64
# ingero-version:install-archive-amd64 product=ingero channel=stable
VERSION=0.12.7
curl -fsSL "https://github.com/ingero-io/ingero/releases/download/v${VERSION}/ingero_${VERSION}_linux_amd64.tar.gz" | tar xz
sudo mv ingero /usr/local/bin/
# Linux arm64 (GH200, Grace Hopper, Graviton)
# ingero-version:install-archive-arm64 product=ingero channel=stable
VERSION=0.12.7
curl -fsSL "https://github.com/ingero-io/ingero/releases/download/v${VERSION}/ingero_${VERSION}_linux_arm64.tar.gz" | tar xz
sudo mv ingero /usr/local/bin/
```
### Docker 镜像
每次发布时都会向 GHCR 发布多架构镜像 (amd64 + arm64):
```
# 拉取最新镜像
docker pull ghcr.io/ingero-io/ingero:latest
# 或固定到特定版本
# ingero-version:docker-pull-example product=ingero channel=stable
docker pull ghcr.io/ingero-io/ingero:v0.12.7
# 快速测试(无需 root,无需 GPU)
docker run --rm ghcr.io/ingero-io/ingero demo --no-gpu
# 系统就绪检查
docker run --rm --privileged --pid=host ghcr.io/ingero-io/ingero check
# 实时 eBPF 追踪(需要权限 + 内核挂载)
docker run --rm --privileged --pid=host \
-v /sys/kernel/debug:/sys/kernel/debug \
-v /sys/kernel/btf:/sys/kernel/btf:ro \
-v /var/lib/ingero:/var/lib/ingero \
ghcr.io/ingero-io/ingero trace --record
```
最低权限要求(替代 `--privileged` 的方案):`--cap-add=BPF --cap-add=PERFMON --cap-add=SYS_ADMIN`。
**数据持久化:** 容器默认将 SQLite 数据库存储在 `/var/lib/ingero/ingero.db`。挂载 `-v /var/lib/ingero:/var/lib/ingero` 以在容器停止后持久化数据。如果不进行此挂载,当容器退出时,**所有追踪数据都将丢失**。
**多数据库:** 使用 `--db` 或 `INGERO_DB` 环境变量来操作不同的数据库:
```
# 追踪到指定数据库
docker run --rm --privileged --pid=host \
-v /var/lib/ingero:/var/lib/ingero \
-v /sys/kernel/debug:/sys/kernel/debug \
-v /sys/kernel/btf:/sys/kernel/btf:ro \
ghcr.io/ingero-io/ingero trace --db /var/lib/ingero/training-run-42.db
# 调查特定数据库
docker run --rm \
-v /var/lib/ingero:/var/lib/ingero \
ghcr.io/ingero-io/ingero explain --db /var/lib/ingero/training-run-42.db
# 比较不同运行的数据库
docker run --rm \
-v /var/lib/ingero:/var/lib/ingero \
ghcr.io/ingero-io/ingero query --db /var/lib/ingero/training-run-41.db --since 1h
docker run --rm \
-v /var/lib/ingero:/var/lib/ingero \
ghcr.io/ingero-io/ingero query --db /var/lib/ingero/training-run-42.db --since 1h
```
该镜像大小约为 10 MB (Alpine 3.20 + 静态链接的 Go 二进制文件)。在本地构建开发用 Dockerfile 时,请通过构建参数传递版本信息:
```
# ingero-version:docker-build-arg product=ingero channel=stable
docker build -f deploy/docker/Dockerfile \
--build-arg VERSION=0.12.7 \
--build-arg COMMIT=$(git rev-parse --short HEAD) \
--build-arg BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ) \
-t ingero:local .
```
GHCR 镜像通过 GoReleaser 自动内置了版本信息。详情请参阅 `deploy/docker/Dockerfile`。
### 从源码构建
```
# 快速设置:在 Ubuntu 22.04/24.04 上安装所有构建依赖 (Go, clang, llvm)
curl -fsSL https://raw.githubusercontent.com/ingero-io/ingero/main/scripts/install-deps.sh | bash
# 需要 clang-14,支持 BTF 的 Linux 内核
git clone https://github.com/ingero-io/ingero.git
cd ingero
make # generates eBPF bindings, builds, tests, and lints - single command
sudo make install # optional - copies binary to /usr/local/bin/ingero
# or just use ./bin/ingero directly, or: alias ingero=$PWD/bin/ingero
```
## 系统要求
- Linux 内核 5.15+ 且支持 BTF (`CONFIG_DEBUG_INFO_BTF=y`)
- NVIDIA 驱动 550+ 且支持 CUDA 11.x、12.x 或 13.x
- 权限 / `CAP_BPF` + `CAP_PERFMON` (eBPF 需要提升的权限)
- 已测试环境:GH200、H100、A100、A10、RTX 4090、RTX 3090 (x86_64 和 aarch64)
## 命令
### `ingero check`
检查您的系统是否已准备好进行基于 eBPF 的 GPU 追踪。
```
$ ingero check
Ingero - System Readiness Check
[✓] Kernel version: 5.15.0-144-generic
need 5.15+
[✓] BTF support: /sys/kernel/btf/vmlinux
available (5242880 bytes)
[✓] NVIDIA driver: 580.126.09
open kernel modules (550+)
[✓] GPU model: NVIDIA GeForce RTX 3090 Ti, 24564 MiB
[✓] CUDA runtime: /usr/lib/x86_64-linux-gnu/libcudart.so.12
loaded by 1 process(es)
[✓] CUDA driver (libcuda.so): /usr/lib/x86_64-linux-gnu/libcuda.so.1
available for driver API tracing
[✓] CUDA processes: 1 found
PID 4821 (python3)
All checks passed - ready to trace!
```
### `ingero trace`
实时事件流,包含滚动统计信息、系统上下文和异常检测。默认情况下,事件会记录到 SQLite 中(使用 `--record=false` 可禁用)。数据库容量上限为 10 GB 的滚动存储,并在达到限制时自动清除旧事件(请参阅 `--max-db`)。
```
sudo ingero trace # auto-detect all CUDA processes for current user
sudo ingero trace --pid 4821 # trace specific process
sudo ingero trace --pid 4821,5032 # trace multiple specific processes
sudo ingero trace --user bob # trace all CUDA processes owned by bob
sudo ingero trace --record=false # disable SQLite recording
sudo ingero trace --duration 60s # stop after 60 seconds
sudo ingero trace --json # JSON output (pipe to jq)
sudo ingero trace --verbose # show individual events
sudo ingero trace --stack=false # disable stack traces (saves ~0.4-0.6% overhead)
sudo ingero trace --max-db 10g # limit DB to 10 GB (default), prunes oldest events
sudo ingero trace --max-db 500m # limit DB to 500 MB (tight disk budget)
sudo ingero trace --max-db 0 # unlimited (no size-based pruning)
sudo ingero trace --deadband 5 # suppress idle snapshots (5% threshold)
sudo ingero trace --deadband 5 --heartbeat 30s # deadband + force report every 30s
sudo ingero trace --prometheus :9090 # expose Prometheus /metrics endpoint
sudo ingero trace --otlp localhost:4318 # push metrics via OTLP
sudo ingero trace --node gpu-node-07 # tag events with node identity (for multi-node)
sudo ingero trace --cuda-lib /opt/venv/lib/python3.11/site-packages/nvidia/cuda_runtime/lib/libcudart.so.12
# explicit libcudart path (skips auto-discovery)
sudo ingero trace --ringbuf-size 32m # override high-throughput ring buffer size (power of 2, min 4096)
sudo ingero trace --sampling-rate 0 # adaptive sampling (default: 1 = emit all; N>1 = 1-in-N)
sudo ingero trace --py-walker ebpf # in-kernel CPython walker (works at ptrace_scope=3)
```
**参数参考:**
- `--cuda-lib PATH` — 显式指定 `libcudart.so` 的路径。跳过自动发现。适用于存在多个 `libcudart` 副本的 venv 工作负载。
- `--ringbuf-size SIZE` — 覆盖高吞吐量探针 (cuda, driver, host) 的环形缓冲区大小。接受 `k`/`m`/`g` 后缀。必须是 2 的幂,最小值为 4096。默认:编译时设定的大小 (cuda/driver 为 8MB,host 为 1MB)。
- `--sampling-rate N` — 事件采样率。`0` = 自适应(在持续丢弃事件时自动调整)。`1` = 发送所有事件(默认行为)。`N > 1` = 每 N 个事件发送 1 个。仅适用于 cuda/driver/graph 探针;host 探针永远不会被采样。
- `--py-walker {auto,ebpf,userspace}` — Python 堆栈帧遍历器选择。`auto`(默认)使用用户空间遍历器。`ebpf` 使用内核内 CPython 遍历器(支持 3.10、3.11、3.12 — 无需 `/proc/pid/mem`,在 `ptrace_scope=3` 下可用)。`userspace` 强制使用经典遍历器。
`ingero check` 现在会报告当前的 `kernel.yama.ptrace_scope` 值,并在其阻碍 Python 源码归因时提供可操作的提示(请参阅故障排除)。
**只有 `trace` 需要 sudo** - 它会附加 eBPF 探针到内核。所有其他命令(`check`、`explain`、`query`、`mcp`、`demo`)均以非特权模式运行。当你运行 `sudo ingero trace` 时,数据库会写入到你的家目录(而不是 `/root/`),并且其所有者会被 chown 为你的用户,因此非 sudo 命令也能读取它。
**进程定位:**
- **默认**(无参数):追踪调用用户(通过 `SUDO_USER`)拥有的所有 CUDA 进程。在单用户机器上,这意味着所有的 CUDA 进程。
- **`--pid`**:定位特定进程,以逗号分隔(例如,`--pid 1234,5678`)。
- **`--user`**:定位特定用户拥有的所有 CUDA 进程(`--user bob`,`--user root`)。
- **动态子进程追踪**:fork 事件会自动将子 PID 纳入主机关联范围。
追踪显示包含五个部分:
1. **System Context (系统上下文)** - CPU、内存、负载、Swap,配有 ASCII 条形图(绿/黄/红)
2. **CUDA Runtime API** - 每个操作的 p50/p95/p99 延迟及异常标记 (cudaMalloc, cudaLaunchKernel, graphLaunch 等)
3. **CUDA Driver API** - 驱动级操作 (cuLaunchKernel, cuMemAlloc 等),即 cuBLAS/cuDNN 直接调用的接口
4. **Host Context (主机上下文)** - 调度器、内存、OOM 和进程生命周期事件
5. **CUDA Graph 事件** - 图捕获、实例化和启动事件(当追踪使用 Graph 的工作负载时)
### `ingero explain`
分析 SQLite 中记录的事件,并生成包含因果链、根因和修复建议的事件报告。从由 `ingero trace` 填充的数据库中读取 - 无需 root 权限。
```
ingero explain # analyze last 5 minutes
ingero explain --since 1h # last hour
ingero explain --since 2d # last 2 days
ingero explain --since 1h30m # human-friendly durations (also: 1w, 3d12h)
ingero explain --last 100 # last 100 events
ingero explain --pid 4821 # filter by specific process
ingero explain --pid 4821,5032 # filter by multiple processes
ingero explain --chains # show stored causal chains (no re-analysis)
ingero explain --json # JSON output for pipelines
ingero explain --from "15:40" --to "15:45" # absolute time range
ingero explain --per-process # per-process CUDA API breakdown
ingero explain --per-process --json # JSON output for pipelines
# 多节点集群查询(扇出至多个 Ingero dashboard API)
ingero explain --nodes host1:8080,host2:8080,host3:8080 # cross-node causal chains
```
#### 按进程细分
对于多进程 GPU 工作负载(RAG 管道、带有 worker 的模型服务、多租户 GPU 共享),`--per-process` 会显示按进程分组的 CUDA API 细分:
```
$ ingero explain --per-process --since 5m
PER-PROCESS GPU API BREAKDOWN
PID 4821 (vllm-worker)
cuLaunchKernel 12,847 calls p50=4.8µs p95=11.2µs p99=16.1µs
cudaMemcpyAsync 892 calls p50=38µs p95=124µs p99=891µs
cudaMallocManaged 14 calls p50=112µs p95=2.1ms p99=8.4ms
PID 5032 (embedding-svc)
cuLaunchKernel 3,201 calls p50=5.1µs p95=12.8µs p99=19.4µs
cudaMemcpy 448 calls p50=42µs p95=98µs p99=412µs
⚠ Multi-process GPU contention: 2 processes sharing GPU with CUDA/Driver ops
```
这回答了“哪个进程正在独占 GPU?”的问题 - 这对于诊断 RAG 管道争用(即 embedding、检索和生成相互竞争 GPU 时间)至关重要。
```
INCIDENT REPORT - 2 causal chains found (1 HIGH, 1 MEDIUM)
[HIGH] cudaStreamSync p99=142ms (8.5x p50) - CPU contention
Timeline:
15:41:20 [SYSTEM] CPU 94%, Load 12.1, Swap 2.1GB
15:41:20 [HOST] sched_switch: PID 8821 (logrotate) preempted PID 4821
15:41:22 [CUDA] cudaStreamSync 142ms (normally 16.7ms)
Root cause: logrotate cron job preempted training process 847 times
Fix: Add `nice -n 19` to logrotate cron, or pin training to dedicated cores
```
### `ingero query`
按时间范围、PID 和操作类型查询存储的事件。支持使用 `--nodes` 进行多节点集群查询。
```
ingero query --since 1h
ingero query --since 1h --pid 4821
ingero query --since 1h --pid 4821,5032
ingero query --since 30m --op cudaMemcpy --json
# 多节点集群查询(扇出至多个 Ingero dashboard API)
ingero query --nodes host1:8080,host2:8080 "SELECT node, source, count(*) FROM events GROUP BY node, source"
ingero query --nodes host1:8080,host2:8080,host3:8080 "SELECT node, count(*) FROM events GROUP BY node"
```
集群查询会将 SQL 扇出分发到每个节点的 `/api/v1/query` 端点,在前面添加一个 `node` 列来拼接结果,并显示统一的表格。部分失败会返回来自可达节点的结果,并对不可达节点发出警告。节点间的时钟偏移会被自动检测(可通过 `--clock-skew-threshold` 配置,默认 10ms)。
在 `ingero.yaml` 中的 `fleet.nodes` 下配置默认的集群节点,以避免在每次命令时重复输入 `--nodes`。
存储使用带有基于大小修剪的 SQLite(默认为 10 GB,通过 `--max-db` 设置)。数据本地存储在 `~/.ingero/ingero.db` - 不会有任何数据离开你的机器。
### `ingero mcp`
启动用于 AI 代理集成的 MCP (Model Context Protocol) server。
```
ingero mcp # stdio (for Claude Code / MCP clients)
ingero mcp --http :8080 # HTTPS on port 8080 (TLS 1.3, auto-generated self-signed cert)
ingero mcp --http :8080 --tls-cert cert.pem --tls-key key.pem # custom TLS certificate
```
**AI 优先分析**:MCP 响应默认使用电报式压缩 (TSC),将 token 数量减少约 60%。在每个请求中设置 `{"tsc": false}` 可获取详细输出。
**MCP 工具:**
| 工具 | 描述 |
|------|-------------|
| `get_check` | 系统诊断(内核、BTF、NVIDIA、CUDA、GPU 型号) |
| `get_trace_stats` | CUDA + 主机统计信息(针对大型数据库的 p50/p95/p99 或聚合回退) |
| `get_causal_chains` | 包含严重性分级和根因的因果链(默认去重,显示前 10 条) |
| `get_stacks` | 已解析的 CUDA/驱动程序操作调用栈(符号、源文件、时序) |
| `graph_lifecycle` | 某个 PID 的 CUDA Graph 生命周期时间线:捕获、实例化、启动序列 |
| `graph_frequency` | 每个可执行文件的 Graph 启动频率:冷热分类、池饱和度 |
| `run_demo` | 运行合成的演示场景 |
| `get_test_report` | GPU 集成测试报告 (JSON) |
| `run_sql` | 执行只读 SQL 以进行即席分析 |
| `query_fleet` | 跨多个 Ingero 节点的扇出查询(链、操作、概览、SQL),带有自动时钟偏移检测 |
**MCP 提示词:**
| 提示词 | 描述 |
|--------|-------------|
| `/investigate` | 引导式调查工作流 - 指导 AI 通过统计信息、因果链和 SQL 诊断 GPU 问题。可与任何 MCP 客户端配合使用。 |
**不仅限于 Claude,可与任何 AI 协同工作。** 通过 [ollmcp](https://github.com/jonigl/mcp-client-for-ollama) (Ollama MCP 客户端) 使用本地开源模型:
```
# 安装 ollmcp (minimax-m2.7:cloud 通过 Ollama Cloud 路由至 MiniMax API,
# 或通过 ollama pull qwen3.5:32b 使用本地模型,如 qwen3.5:32b)
pip install mcp-client-for-ollama
# 创建指向 Ingero MCP server 的配置
cat > /tmp/ingero-mcp.json << 'EOF'
{"mcpServers":{"ingero":{"command":"ingero","args":["mcp","--db","trace.db"]}}}
EOF
# 开始调查 - /investigate 触发引导式工作流
ollmcp -m minimax-m2.7:cloud -j /tmp/ingero-mcp.json
```
已测试通过 MiniMax M2.7 和 Qwen 3.5 经由 Ollama 处理已保存的调查数据库。也适用于 Claude Desktop、Cursor 以及任何兼容 MCP 的客户端。
**curl 示例** (配合 `--http :8080`):
```
# 系统诊断(自签名证书使用 -k)
curl -sk https://localhost:8080/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_check","arguments":{}}}' | jq
# 因果关系链(为 AI 进行 TSC 压缩)
curl -sk https://localhost:8080/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_causal_chains","arguments":{}}}' | jq
# 详细输出(关闭 TSC)
curl -sk https://localhost:8080/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_trace_stats","arguments":{"tsc":false}}}' | jq
```
### `ingero dashboard`
启动由 SQLite 事件存储支持的、基于浏览器的 GPU 监控仪表板。显示实时的系统指标、CUDA 操作延迟、因果链以及能力清单(对于 Ingero 尚未收集的指标会显示为灰色的面板,并在工具提示中指明所需的外部工具)。需要 `ingero trace` 正在运行(或最近运行过)。
```
ingero dashboard # HTTPS on :8080 (self-signed TLS 1.3)
ingero dashboard --addr :9090 # custom port
ingero dashboard --db /path/to/ingero.db # custom database
ingero dashboard --tls-cert cert.pem --tls-key key.pem # custom TLS certificate
ingero dashboard --no-tls # plain HTTP (for fleet queries on trusted networks)
# 通过 SSH 隧道进行远程访问:
ssh -L 8080:localhost:8080 user@gpu-vm
# 然后在浏览器中打开 https://localhost:8080
```
**无需 sudo** - 仪表板从由 `ingero trace` 填充的 SQLite 数据库中读取数据。
**安全性:** 仅支持 TLS 1.3。如果未提供 `--tls-cert`/`--tls-key`,会自动生成短暂的自签名 ECDSA P-256 证书(有效期 24 小时)。DNS 重绑定保护会拒绝来自非 localhost 主机头的请求。
**API 端点:**
| 端点 | 描述 |
|----------|-------------|
| `GET /api/v1/overview` | 事件计数、链计数、最新系统快照、GPU 信息、顶级因果链 |
| `GET /api/v1/ops?since=5m` | 每个操作的延迟统计(百分位数或聚合模式) |
| `GET /api/v1/chains?since=1h` | 已存储的包含严重性、根因、时间线的因果链 |
| `GET /api/v1/snapshots?since=60s` | 系统指标时间序列 (CPU、内存、Swap、负载) |
| `GET /api/v1/capabilities` | 指标可用性清单(可用与显示为灰色并提示所需工具的对比) |
| `GET /api/v1/graph-metrics` | CUDA Graph 指标:捕获/启动速率、实例化持续时间 |
| `GET /api/v1/graph-events` | 最近的 CUDA Graph 事件及其句柄和持续时间 |
| `POST /api/v1/query` | 执行只读 SQL(由集群扇出查询使用) |
| `GET /api/v1/time` | 服务器挂钟时间戳(用于时钟偏移检测) |
### `ingero merge`
将来自多个 Ingero 节点的 SQLite 数据库合并为一个可查询的数据库,用于离线的跨节点分析。适用于气隙隔离环境,或者当你倾向于离线分析而非扇出查询的情况。
```
ingero merge node-a.db node-b.db node-c.db -o cluster.db # merge 3 node databases
ingero merge old.db --force-node legacy-node -o merged.db # assign node identity to legacy DBs
# 然后在合并的数据库上使用标准工具
ingero query -d cluster.db --since 1h
ingero explain -d cluster.db --chains
ingero export --format perfetto -d cluster.db -o trace.json
```
节点命名空间化的事件 ID(`{node}:{seq}`)确保合并时零冲突。堆栈跟踪通过哈希值去重。会话被重新键入。会检测并警告追踪之间的时钟偏移(可通过 `--clock-skew-threshold` 配置,默认 100ms)。
### `ingero export`
将事件数据导出为可视化格式。目前支持 Perfetto/Chrome Trace Event Format,用于在 [ui.perfetto.dev](https://ui.perfetto.dev) 或 `chrome://tracing` 中进行时间线可视化。
```
# 从本地或合并的数据库
ingero export --format perfetto -d ~/.ingero/ingero.db -o trace.json
ingero export --format perfetto -d cluster.db -o trace.json --since 5m
# 扇出模式(通过 fleet API 从多个节点获取)
ingero export --format perfetto --nodes node-1:8080,node-2:8080 -o trace.json
```
在 Perfetto UI 中打开,每个节点/rank 占用一个进程轨道,CUDA 事件显示为持续时间跨度,因果链显示为按严重性着色的即时标记。多节点追踪会显示并排的时间线,以便发现哪个 rank 在其他 rank 等待时发生了停滞。
### `ingero demo`
```
ingero demo # all 6 scenarios (incident first)
ingero demo incident # single scenario
ingero demo gpu-steal # also: gpu-contention, contention
ingero demo --no-gpu # synthetic mode
```
### `ingero version`
```
$ ingero version
ingero v0.10.0 (commit: , built: )
```
## 堆栈跟踪
堆栈跟踪**默认开启** - 每一个 CUDA/Driver API 事件都会捕获完整的用户空间调用链。显示**谁调用了 cudaMalloc** - 从 CUDA 库向上贯穿 PyTorch、你的 Python 代码,一直到 `main()`。实测 GPU 性能开销为 **0.4-0.6%**(在 RTX 3090 到 H100 上的噪音范围内)。如需要,可通过 `--stack=false` 禁用。
```
sudo ingero trace --json # JSON with resolved stack traces (stacks on by default)
sudo ingero trace --debug # debug output shows resolved frames on stderr
sudo ingero demo --gpu --json # GPU demo with stack traces (needs sudo)
ingero explain # post-hoc causal analysis from DB (no sudo)
sudo ingero trace --stack=false # disable stacks if needed
```
**最大深度**:64 个原生帧(eBPF `bpf_get_stack`)。这涵盖了从 CUDA → cuBLAS/cuDNN → PyTorch C++ → Python 解释器一直到 `main()` / `_start` 的深度调用链。
### Python 堆栈归因
对于 Python 工作负载(PyTorch、TensorFlow 等),Ingero 直接从进程内存中提取 **CPython 帧信息**。当一个原生帧位于 libpython 的 eval 循环内时,相应的 Python 源码帧将被注入到调用栈中:
```
[Python] train.py:8 in train_step()
[Python] train.py:13 in main()
[Python] train.py:1 in ()
[Native] cublasLtSSSMatmul+0x1d4 (libcublasLt.so.12)
[Native] cublasSgemm_v2+0xa6 (libcublas.so.12)
[Native] (libtorch_cuda.so)
```
支持的 Python 版本:**3.10、3.11、3.12**(涵盖 Ubuntu 22.04 默认版本、conda 默认版本以及大多数生产部署)。版本检测通过 `/proc/[pid]/maps` 自动完成。
#### 为什么你需要一个 Python 帧遍历器
仅靠原生堆栈跟踪只能停留在 `_PyEval_EvalFrameDefault` —— 这是运行 Python 字节码解释器的 C 函数。在其之上的“你的代码实际上在做什么”的每一帧都存在于解释器状态(`PyThreadState`、`_PyInterpreterFrame`、`PyCodeObject`)中,而不是 C 调用栈中。如果没有遍器,你会看到 `_PyEval_EvalFrameDefault` 重复 N 次,这无法告诉你哪个 `.py` 文件触发了缓慢的 `cuLaunchKernel`。
Python 帧遍历器会读取 CPython 自身的数据结构,并重建源码级别的调用链(`train.py:train_step`、`model.py:forward` 等)。这正是让你能回答“是哪行 Python 代码启动了这个缓慢的内核?”,而不是仅仅得到“解释器内部启动了它”的原因。
Ingero 为此内置了**两种遍历器实现**:
- **用户空间遍历器 (默认)** — 在事件到达后于 Go 进程中运行。通过 `/proc/[pid]/mem` 或 `process_vm_readv` 读取目标进程的内存。简单、灵活,能处理完整的 CPython 偏移回退链(`_Py_DebugOffsets` → 已知偏移数据库 → DWARF → 硬编码偏移)。
- **内核内 eBPF 遍历器 (选填)** — 通过 `bpf_probe_read_user` 辅助函数在内核探针内部遍历帧。不需要访问 `/proc/[pid]/mem`。在 `kernel.yama.ptrace_scope=3`(加固系统)下是必需的,并且当你希望帧捕获与 CUDA 事件同步发生而不是在事件到达时异步进行时非常有用。
#### 如何使用
**默认 (用户空间遍历器):** 只需传入 `--stack` — 对于支持的 Python 版本,帧信息会自动出现。
```
sudo ingero trace --stack --duration 30s
```
你会在 JSON 输出中看到 `py_file` / `py_func` / `py_line` 字段,或在表格/调试视图中看到 `[Python] : in ()` 条目。
**eBPF 遍历器 (选填):** 在 `--stack` 的同时传入 `--py-walker=ebpf`。
```
sudo ingero trace --stack --py-walker=ebpf --duration 30s
```
在以下情况使用 eBPF 遍历器:
- 你的系统设置了 `kernel.yama.ptrace_scope=3`(用户空间遍历器无法在那里读取进程内存)
- 你希望在 CUDA 事件发生的确切时刻获得有保证的同步帧捕获
- 你在访问 `/proc/[pid]/mem` 受阻的只读/加固主机上运行
在以下情况坚持使用默认设置(`--py-walker=auto`,它会解析为用户空间遍历器):
- 你在一个普通的 Linux 主机上(ptrace_scope 为 0、1 或 2)— 用户空间遍历器更简单,并具有完整的偏移回退覆盖,包括发行版打过补丁的 CPython 构建
- 你关心最低的单事件开销 — eBPF 遍历器会增加每个发出事件的 BPF 辅助调用成本
**排查丢失的帧:** 运行 `ingero check` — 它现在会报告你的 `kernel.yama.ptrace_scope` 值,并告诉你如果它阻碍了用户空间遍历器该怎么做。对于 CPython 3.12,你还将自动受益于自我描述的 `_Py_DebugOffsets` 结构体(无需调试符号);对于打过补丁的发行版构建上的 3.10/3.11,安装匹配的 `python3.X-dbgsym` 包可为用户空间遍历器提供回退所需的 DWARF 偏移量。
### 带 `--stack` 的 JSON 输出
来自在 A100 SXM4 上运行的 PyTorch ResNet-50 训练的真实输出 - 通过 Driver API uprobes 捕获的 cuBLAS 矩阵乘法内核启动,带有从 Python 到 cuBLAS 再到 GPU 的完整调用链:
```
{
"timestamp": "2026-02-25T12:06:24.753983243Z",
"pid": 11435,
"tid": 11435,
"source": "driver",
"op": "cuLaunchKernel",
"duration_ns": 10900,
"duration": "11us",
"stack": [
{"ip": "0x0", "py_file": "train.py", "py_func": "train_step", "py_line": 8},
{"ip": "0x0", "py_file": "train.py", "py_func": "main", "py_line": 13},
{"ip": "0x0", "py_file": "train.py", "py_func": "", "py_line": 1},
{"ip": "0x765bb62cfa44", "symbol": "cublasLtSSSMatmul+0x1d4", "file": "libcublasLt.so.12.8.4.1"},
{"ip": "0x765be7734046", "symbol": "cublasSgemm_v2+0xa6", "file": "libcublas.so.12.8.4.1"},
{"ip": "0x765c2517fa49", "file": "libtorch_cuda.so"}
]
}
```
这个内核启动对 CUDA Runtime 分析器是不可见的 - cuBLAS 直接调用了 `cuLaunchKernel`。只有 Ingero 的 Driver API uprobes 能够捕获它。
### 带 `--stack --debug` 的调试输出
```
[DEBUG] stack trace for cuLaunchKernel (PID 11435, TID 11435, 6 frames):
[DEBUG] [0] [Python] train.py:8 in train_step()
[DEBUG] [1] [Python] train.py:13 in main()
[DEBUG] [2] [Python] train.py:1 in ()
[DEBUG] [3] cublasLtSSSMatmul+0x1d4 (libcublasLt.so.12)
[DEBUG] [4] cublasSgemm_v2+0xa6 (libcublas.so.12)
[DEBUG] [5] (libtorch_cuda.so)
```
## OTEL 集成 (可选)
OTEL 导出**默认关闭** - 仅在你传入 `--otlp` 或 `--prometheus` 时启用。
```
# Prometheus 指标端点(拉取)
sudo ingero trace --prometheus :9090
curl localhost:9090/metrics
# OTLP 推送(HTTP JSON 至任何兼容 OTEL 的接收器)
sudo ingero trace --otlp localhost:4318
sudo ingero trace --otlp localhost:4318 --debug # see OTLP push logs on stderr
```
OTLP 使用 HTTP JSON 传输 (`POST /v1/metrics`)。兼容:OpenTelemetry Collector、Grafana Alloy、Grafana Cloud、Datadog Agent、New Relic 以及任何兼容 OTLP 的接收器。
指标使用 OTEL 语义约定:`gpu.cuda.operation.duration`、`gpu.cuda.operation.count`、`system.cpu.utilization`、`system.memory.utilization`、`ingero.anomaly.count`。按操作、按来源的粒度。
零外部依赖 - 无需导入 OTEL SDK。JSON 负载直接使用 Go 标准库构建。
## Ingero Fleet Collector (跨节点落后检测)
与 Ingero 内置的多节点 `query --nodes` 扇出(参见上文的 [`ingero query`](#ingero-query))分开,[Ingero Fleet Collector](https://github.com/ingero-io/ingero-fleet) 是一个定制的 OpenTelemetry Collector 发行版,它根据代理报告的健康评分计算集群范围内的落后节点阈值,并通过 OTLP 响应头将其返回给代理。
当你希望集群本身实时分类落后节点,而不是事后跨节点临时查询时,请使用 Fleet。代理在 `ingero trace --record` 的同时运行 `ingero fleet-push` 子命令;命令参考请参见 [`docs/fleet-push.md`](docs/fleet-push.md)。要开始使用,请跳转至顶部的[快速开始 (多节点 GPU 集群)](#quick-start-multi-node-gpu-cluster)。
## 工作原理
```
┌────────────────────────────────────────────────────────────────┐
│ User Space │
│ │
│ ┌─────────┐ ┌─────────────┐ ┌───────┐ ┌─────────────┐ │
│ │ CUDA │ │ ingero │ │SQLite │ │MCP Server │ │
│ │ App │ │ agent │─►│ DB │◄───│(stdio/HTTPS)│ │
│ │(PyTorch)│ │ │ │ │ └─────────────┘ │
│ │ │ │ │ │ │ ┌───────────┐ │
│ │ │ │ │ │ │◄──│ Dashboard │ │
│ │ │ │ │ └───────┘ │ (HTTPS) │ │
│ └──┬──┬───┘ │ ┌──────────┐│ └───────────┘ │
│ │ │ │ │ causal ││ ┌───────────┐ │
│ │ │ │ │ engine ││ │ OTLP / │ │
│ │ │ │ └──────────┘│──►│ Prometheus│ │
│ │ │ └──┬──┬──┬────┘ └───────────┘ │
│ │ │ │ │ │ ▲ │
│ │ │ │ │ │ │ ring buffers │
│─────┼──┼───────────┼──┼──┼─┼───────────────────────────────────│
│ │ ▼ │ ▼ ▼ │ │
│ │ ┌─────────┐ │ ┌────────────────────┐ │
│ │ │libcuda │◄─┤ │ eBPF uprobes │ (Driver API) │
│ │ │ .so │ │ │ cuLaunchKernel │ │
│ │ └─────────┘ │ │ cuMemcpy/Alloc │ │
│ ▼ │ └────────────────────┘ │
│ ┌─────────┐ │ ┌────────────────────┐ │
│ │libcudart│◄──────┘ │ eBPF uprobes │ (Runtime API) │
│ │ .so │◄────────│ cudaLaunchKernel │ │
│ └─────────┘ │ cudaMalloc/Memcpy │ │
│ │ Graph: Capture, │ │
│ │ Instantiate,Launch│ │
│ └────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ eBPF tracepoints (sched_switch, mm_page_alloc, oom, │ │
│ │ sched_process_exec/exit/fork) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Kernel Space /proc → CPU%, Mem%, Load, Swap │
└────────────────────────────────────────────────────────────────┘
```
1. **发现** - 扫描 `/proc` 查找链接到 `libcudart.so` 的进程,自动发现 `libcuda.so`
2. **附加** - 将 eBPF 探针加载到 CUDA runtime uprobes、driver uprobes 和主机 tracepoints 上
3. **捕获** - eBPF 程序将 PID、TID、时间戳记录到按层划分的环形缓冲区中
4. **系统** - 每秒一次从 `/proc` 读取 CPU/内存/负载/交换分区信息
5. **统计** - 计算每个操作的滚动 p50/p95/p99,标记异常
6. **关联** - 按时间戳和 PID 组装因果链 (SYSTEM + HOST + CUDA Runtime + CUDA Driver + CUDA Graph)
7. **存储** - 将事件写入带有基于大小修剪的 SQLite(默认 `--max-db 10g`)。使用 `--record=false` 禁用记录
8. **导出** - 通过 OTLP 推送指标或提供 Prometheus `/metrics` 服务(可选)
9. **服务** - 通过 MCP (stdio 或 HTTPS/TLS 1.3) 向 AI 代理暴露诊断信息
10. **仪表板** - 基于浏览器的 HTTPS 仪表板从 SQLite 读取数据,通过自动轮询显示操作/链/快照/能力清单
11. **集群** - 通过仪表板 API 跨多个节点进行扇出查询,合并离线数据库,检测时钟偏移,导出到 Perfetto 时间线
## 集成测试
已在 3 个云提供商(TensorDock、Lambda Labs、Azure)的 6 款 GPU 型号上完成验证。堆栈跟踪默认开启。实测 GPU 性能开销:**0.4-1.7%**(在噪音范围内)。
| GPU | 显存 | 测试数 | 通过 | 失败 | 警告 | 堆栈开销 | 堆栈覆盖率 |
|-----|------|-------|------|------|------|----------|-----------|
| GH200 | 480 GB | 80 | 76 | 0 | 4 | +1.6% | 99.8% |
| A100 SXM4 | 40 GB | 80 | 76 | 0 | 4 | +0.9% | 99.4% |
| A10 | 24 GB | 80 | 76 | 0 | 4 | -0.1% | 99.2% |
| H100 (PCIe / SXM5) | 80 GB | 62 | 62 | 0 | 0 | +1.7% | 99.5% |
| RTX 4090 | 24 GB | 34 | 34 | 0 | 0 | +0.6% | 99.9% |
| RTX 3090 | 24 GB | 34 | 34 | 0 | 0 | - | - |
在 v0.8 测试的 GPU 上,80 个集成测试中有 76 个通过 (0 失败,4 个警告)。测试架构:x86_64 和 aarch64 (GH200 Grace Hopper)。
## Ingero 目前解决的问题
Ingero 解决了涵盖训练、推理和 AI 代理工作负载的 25 个已记录的 GPU 问题:
| # | GPU 问题 | 严重性 | Ingero 如何检测 |
|---|-------------|----------|----------------------|
| 1 | NCCL 挂起与分布式训练死锁 | CRITICAL | `sched_switch` 显示被阻塞的 rank + CUDA 同步时序。TCP 重传追踪识别由网络引起的挂起 |
| 2 | GPU 利用率不足 / 数据管道匮乏 | CRITICAL | 主机调度器 + `cudaStreamSync` + `cudaMemcpy` 管道泡沫诊断。Block I/O 显示 DataLoader 磁盘瓶颈 |
| 3 | CUDA OOM 与内存碎片化 | CRITICAL | `cudaMalloc`/`cuMemAlloc` 分配模式追踪。`cudaMallocManaged` 增加了托管内存超额订阅检测 |
| 4 | 静默数据损坏 (SDC) | CRITICAL | 异常的内核时序作为间接信号(有限) |
| 5 | 推理成本爆炸 (多步代理) | CRITICAL | 每个代理会话的 CUDA API 突发/空闲模式 |
| 6 | KV 缓存压力与抢占级联 | CRITICAL | `cudaMalloc` 模式 + 抢占期间的 `cudaStreamSync` 激增。托管内存缺页检测 |
| 6b | CUDA Graph 重新捕获延迟激增 (vLLM, torch.compile) | HIGH | Graph 生命周期追踪:捕获/实例化/启动频率,池耗尽检测,捕获期间 OOM,启动期间 CPU 争用 |
| 7 | 规模化下的 GPU 硬件故障 | HIGH | `cudaMemcpy` 基线漂移,`sched_switch` 频率异常 |
| 8 | GPU 服务中的 CPU 瓶颈 | HIGH | 推理进程上的 `sched_switch` + `cudaStreamSync` 空闲间隙 |
| 9 | 代理工具执行期间的 GPU 空闲浪费 | HIGH | 与主机进程活动关联的 CUDA API 静默期。TCP 追踪显示 "GPU 在 2 秒的 HTTP 工具调用期间处于空闲" |
| 10 | 长时间运行服务中的 GPU 内存泄漏 | HIGH | 随时间推移的 `cudaMalloc`/`cudaFree` 失衡追踪,通过 cgroup 按容器划分 |
| 11 | 混合精度 (AMP) 不稳定性 | HIGH | 异常的内核时序 (跳过更新 = 快速同步) |
| 12 | 有效吞吐量损失 (训练效率差距) | HIGH | 调度器抢占、memcpy 延迟、管道泡沫。Block I/O 显示检查点写入 + 数据读取开销 |
| 13 | GPU 调度与编排故障 (K8s) | HIGH | 每 cgroup 的 `sched_switch` 延迟 + pod/namespace 元数据。自动发现 `nvidia.com/gpu` pod |
| 14 | 模型交换延迟 (多模型代理) | HIGH | 模型加载期间的 `cudaMalloc` + `cudaMemcpy` 模式。Block I/O 显示磁盘→CPU 传输时间 |
| 15 | CUDA 设备端断言与非法内存访问 | MEDIUM | 崩溃前的 CUDA API 调用序列 + 堆栈跟踪 |
| 16 | NVIDIA 驱动 / CUDA 版本不兼容 | MEDIUM | Uprobe 附加失败 = 库/驱动不匹配信号 |
| 17 | 热量降频与功耗限制降频 | MEDIUM | 随时间推移的内核持续时间趋势 |
| 18 | 嘈杂邻居 / 多租户 GPU 干扰 | MEDIUM | 每 cgroup 的 `sched_switch` 延迟 + CUDA API 延迟关联。通过 cgroup_schedstat 进行嘈杂邻居检测 |
| 19 | 冷启动 / 模型加载延迟 | MEDIUM | 通过 CUDA API 时序展示完整的冷启动序列。Block I/O 负责完成磁盘→CPU→GPU 管道 |
| 20 | 多 GPU 张量并行通信开销 | MEDIUM | 通过 `sched_switch` + CUDA 同步进行主机端落后节点检测。NCCL 端口上的 TCP 重传追踪 |
| 21 | RAG 管道 GPU 争用 | MEDIUM | 每个进程的 CUDA API 细分(`explain --per-process`) - 显示哪个进程正在占用 GPU 时间 |
| 22 | 检查点保存/加载失败 | MEDIUM | 内存激增检测 + `cudaStreamSync` 中的 I/O 阻塞。Block I/O 显示实际写入延迟 + NFS 超时 |
| 23 | PCIe 瓶颈 (KV 缓存交换,模型加载) | MEDIUM | `cudaMemcpy` 按操作追踪,包含方向/大小/持续时间。`cudaMallocManaged` 页面迁移 + Block I/O 显示 NVMe-PCIe 争用 |
| 24 | 损失激增 (非 AMP) | LOW-MED | 系统事件与损失时机的关联 |
| 25 | Triton Inference Server 多 GPU 错误 | LOW-MED | Triton 进程上的 CUDA API 追踪 |
## 常见问题
**生产环境使用安全吗?**
是的。eBPF 程序在加载前会经过内核验证 - 它们不会导致系统崩溃。探针(包含堆栈跟踪)增加的开销 <2%(在 RTX 3090、RTX 4090、A10、A100、H100 上通过 PyTorch 工作负载实测为 0.4-0.6%)。
**需要修改代码吗?**
不需要。Ingero 在操作系统层面附加到 `libcudart.so` 和内核 tracepoints 上。你的应用代码原封不动。可以追踪任何语言 - Python、C++、Java - 只要链接了 libcudart.so 即可。
**支持哪些 GPU?**
任何带有驱动 550+ 和 CUDA 11.x/12.x 的 NVIDIA GPU。已在 GH200 (aarch64)、H100、A100、A10、RTX 4090、RTX 3090 (x86_64) 上测试通过。适用于 AWS 深度学习 AMI(自动发现带有版本号的 `libcudart.so`)。
**在容器中也能工作吗?**
是的。eBPF 程序在内核空间执行 - 容器只是通过系统调用加载它们。使用 `--privileged`(或 `--cap-add=BPF,PERFMON,SYS_ADMIN`)、`--pid=host` 运行,并挂载 `/proc`、`/sys/kernel/debug` 和 `/sys/kernel/btf`。主机内核必须启用 BTF。预构建的镜像可在 `ghcr.io/ingero-io/ingero` 获取 - 请参阅 [Docker 镜像](#docker-image)安装部分。这与 Falco、Tetragon 和其他 eBPF DaemonSets 使用的模式相同。
**数据存储在哪里?**
存储在本地 `~/.ingero/ingero.db` (SQLite)。没有任何数据会离开你的机器。基于大小的修剪默认将数据库保持在 10 GB 以下。使用 `--record-all` 时,这可以覆盖几个小时的高负载 GPU 负载;在使用选择性存储(默认)时,持续的时间要长得多。可通过 `--max-db` 配置(例如,`--max-db 500m`,`--max-db 0` 表示无限制)。使用 `--db /path/to/file.db` 指定自定义位置。
**会检查更新吗?**
是的。在交互式命令(`trace`、`demo`、`explain`、`check`)上,ingero 会检查 GitHub Releases 以获取较新版本(每 24 小时一次,缓存在 `~/.ingero/update-check` 中)。检查在后台运行,绝不会延迟你的命令。设置 `INGERO_NO_UPDATE_NOTIFIER=1` 以禁用。对于 `query`、`mcp`、`version` 和开发版构建会跳过此检查。
## 已知问题
- **通过 `fork()` 实现的多进程 CUDA。** NVIDIA 的 CUDA 驱动在父进程初始化 CUDA 上下文后不支持 `fork()` —— 子进程无法使用 CUDA。eBPF 遍历器在 fork 时同步将父进程的遍历器状态继承给子进程,但无法调用 CUDA 的子进程无论如何也不会触发遍历器。对于多进程 CUDA 工作负载,请使用 `torch.multiprocessing.set_start_method('spawn')`(或 Ray/torchrun 的 spawn 等效方法);全新 spawn 风格的进程会初始化自己的 CUDA 上下文,并通过正常的动态 PID 路径获得完整的遍历器覆盖。
- **Ubuntu 24.04 + 用户空间遍历器上的发行版补丁版 CPython 3.12。** Ubuntu 打过补丁的 CPython 的结构偏移量与上游不同,并且 Ubuntu 24.04 的主归档中不提供 `python3.12-dbgsym`。用户空间遍历器会回退到硬编码的上游偏移量并产生错误的帧数据。**eBPF 遍历器通过其运行时偏移收割器绕过了此问题** — 在 dbgsym 变得易于获取之前,请在 Ubuntu 24.04 上使用 `--py-walker=ebpf`。(从 `ddebs.ubuntu.com` 安装 `python3.12-dbgsym` 也可以解决用户空间遍历器的路径问题。)
- **`kernel.yama.ptrace_scope=3` 下的追踪所有模式。** eBPF 遍历器在带有显式 `--pid X` 目标的情况下可在 ptrace_scope=3 下工作(特定于 PID 的 uprobe 附加)。如果没有 `--pid`(追踪所有 / 动态发现模式),cuda/driver uprobes 可能无法触发 — 会记录一条启动警告。解决方法:传递 `--pid X` 或将 `kernel.yama.ptrace_scope` 降低至 `1`(Ubuntu 默认值)。
### 遍历器路线图 — 用户空间遍历器弃用
内核内 eBPF 遍历器(`--py-walker=ebpf`)现在是向前的战略路径。它具有适用于打过补丁的发行版构建(包括 Ubuntu 24.04 CPython 3.12)的运行时偏移收割、多库 libcudart 覆盖、按 CUDA 追踪器的状态广播、用于多进程工作负载的 fork 继承,并且可以通过 `--pid` 在任何 `ptrace_scope` 值下运行。
**用户空间遍历器 — 当前的默认设置 — 将在即将发布的版本中被弃用。** 一旦上述剩余项目被修复或被接受为狭隘的权衡,eBPF 遍历器将被提升为 `auto` 默认设置。如果你正在设置新的部署,建议现在优先使用 `--py-walker=ebpf`。在默认设置切换后,`userspace` 模式将仍然可用(通过 `--py-walker=userspace`),直到弃用窗口关闭。
## 已知模式
Ingero 自动检测到的循环出现的 GPU 工作负载问题及其记录的修复方案。
### CUDA Graph 捕获立即失败 (cuBLAS 延迟初始化)
**现象:** `cudaStreamBeginCapture` 之后跟 cuBLAS 或 cuDNN 调用立即失败。错误表现为 `CUBLAS_STATUS_NOT_INITIALIZED`、失败的 `cudaStreamEndCapture` 或无效的图句柄。在追踪中,捕获区域异常短(持续时间 < 1ms)且不包含任何内核启动。
**原因:** cuBLAS 和 cuDNN 在首次 API 调用时会延迟创建其内部句柄、内存池和工作缓冲区。这些初始化步骤调用的 CUDA Runtime API(`cudaMalloc`、`cudaEventCreate` 等)在流捕获区域内是不允许的。当首次 cuBLAS/cuDNN 调用在捕获期间发生时,运行时会拒绝这些不允许的调用,导致捕获中止或产生无效的图。
**修复方案:** 在调用 `cudaStreamBeginCapture` 之前,执行 3 次或更多次你打算捕获的工作的预热迭代。预热会强制 cuBLAS/cuDNN 在捕获上下文之外完成延迟初始化。
```
# BAD - 在首次 cuBLAS 调用时捕获中止
g = torch.cuda.CUDAGraph()
with torch.cuda.graph(g):
y = torch.matmul(a, b)
# GOOD - 预热强制在捕获外部进行 cuBLAS 初始化
s = torch.cuda.Stream()
s.wait_stream(torch.cuda.current_stream())
with torch.cuda.stream(s):
for _ in range(3):
y = torch.matmul(a, b)
torch.cuda.current_stream().wait_stream(s)
g = torch.cuda.CUDAGraph()
with torch.cuda.graph(g):
y = torch.matmul(a, b)
```
或者,使用 `torch.cuda.make_graphed_callables()`,它会自动处理预热序列。
**自动检测:** `ingero explain` 会将此模式作为 `graph-capture-warmup` 因果链(中等严重性 MEDIUM)呈现。当你怀疑存在 CUDA Graph 捕获问题时,在追踪后运行它即可。
### Python 源码帧缺失
**现象:** 堆栈跟踪中出现原生帧,但 Python 文件、函数和行字段为空。追踪仅显示 `[Native]` 帧;没有 `[Python]` 帧与 CPython eval 循环交错。
**原因:**
- `kernel.yama.ptrace_scope >= 1` 阻止了 `/proc/[pid]/mem` 访问,而这是用户空间遍历器所依赖的。
- 发行版打过补丁的 CPython,其结构偏移量与上游不同。
- CPython 版本早于 3.10 或晚于受支持的集合 (3.10, 3.11, 3.12)。
**修复方案:**
1. 检查 `ingero check` 中的 `ptrace_scope` 建议。在级别 0 或 1 时,当 ingero 以 root 身份或具有 `CAP_SYS_PTRACE` 运行时,用户空间遍历器可以工作(`process_vm_readv` 回退机制会自动处理级别 1)。
2. 对于 `ptrace_scope=2` 或 `=3` 的加固系统,传递 `--py-walker=ebpf` 以通过 eBPF 将帧遍历路由到内核中。内核内遍历器直接从任务的用户内存读取 CPython 帧状态,完全绕过对 `/proc/[pid]/mem` 的依赖。
3. 对于偏移量与上游不同的发行版构建,安装 `python3-dbgsym` 可让 ingero 使用 DWARF 偏移量。CPython 3.12 在存在的情况下还会使用自我描述的 `_Py_DebugOffsets` 结构体 — 无需调试符号。
### 负载下出现高事件丢弃率
**现象:** 表格 UI 页脚显示 `Events dropped: cuda=N driver=N ...` 且计数非零,或出现 `>5% of events dropped` WARN 行。每当发生丢弃时,追踪输出中都会显示按追踪器统计的丢弃计数器。
**原因:** 环形缓冲区或用户空间通道在持续的事件速率(通常高于约 500 万事件/秒)下饱和。驱动/运行时环形缓冲区的填充速度超过了用户空间读取器的排空速度。
**修复选项(按优先顺序排列):**
1. 让自适应采样介入:`--sampling-rate 0`。自适应路径会在持续丢弃事件时提升采样率,并在事件流平静时重置。无需手动调优。
2. 增加高吞吐量探针的环形缓冲区大小:`--ringbuf-size 32m`(或更大,必须是 2 的幂)。此标志适用于 cuda/driver/host 环形缓冲区;低吞吐量探针保留其编译默认值。
3. 对于持续的极端速率,修复采样率:`--sampling-rate 10` 每 10 个事件发出 1 个。
关键事件(OOM kill、进程 exec/exit/fork)流经一个专用的较小环形缓冲区,永远不会受采样或聚合的影响。即使在主事件流大量丢弃的情况下,它们也依然可见。
## 故障排除
针对常见操作问题的症状到修复条目。上文的已知模式部分包含完整上下文;以下是更精简的备忘单版本。
**问:我的 venv 工作负载没有被追踪。**
多库发现是自动的。Ingero 现在会定位 `libcudart.so` 的每一个副本(系统安装版以及由 `nvidia-cuda-runtime` pip 包附带的 venv/conda 副本),并将探针附加到所有这些副本上。使用 `--debug` 确认:你应该会看到每个副本对应的 `INFO discover: found libcudart.so path=...` 行。如果自动发现了错误的库,请使用 `--cuda-lib /path/to/libcudart.so` 强制指定。
**问:我的堆栈跟踪中没有出现 Python 源码帧。**
完整上下文请参阅上文的已知模式条目。快速检查:`ingero check | grep ptrace_scope`,确保你以 root 身份或具有 `CAP_SYS_PTRACE` 运行,对于加固系统请尝试 `--py-walker=ebpf`。CPython 3.12 通过自我描述的 `_Py_DebugOffsets` 结构体获得了最佳体验 — 无需调试符号。
**问:事件正在被丢弃。**
完整缓解列表请参阅已知模式。首先让自适应采样处理它(`--sampling-rate 0`,这也是可变工作负载的推荐默认设置)。仅当自适应路径不够用时才调整 `--ringbuf-size`。无论主事件流上的丢弃率如何,OOM、进程 exec/exit 和 fork 事件都能保证送达。
**问:如何降低 ingero 的开销?**
默认的开销目标是比工作负载基线高 `<2%` (NFR3)。如果你看到更高的开销:
- 禁用低价值探针:`--no-io --no-tcp --no-net` 可关闭 Block I/O、TCP 重传和网络套接字追踪器。CUDA 和主机探针保留- 跳过堆栈捕获:如果不需要用户空间堆栈跟踪,省略 `--stack`(或传递 `--stack=false`);这是最昂贵的单事件成本。
- 使用采样:在极高事件工作负载上使用 `--sampling-rate 10`。对于偶尔出现开销激增的工作负载,自适应(`--sampling-rate 0`,默认)通常就足够了。
- 保留用户空间遍历器(默认 `--py-walker=auto`),除非你需要 eBPF 路径 — eBPF 遍历器会增加每个事件的辅助调用成本。
### 高级配置
面向高级用户的参考资料。默认值已针对典型的训练和推理工作负载进行了调优;仅在有特定原因时才调整这些设置。
**环形缓冲区大小设置。** 默认大小反映了预期的事件速率(cuda/driver 为 8MB,host 为 1MB,tcp/net/block-io 更小)。如果你的工作负载持续超过约 100 万-500 万事件/秒,请增加高吞吐量探针缓冲区。`--ringbuf-size` 标志仅适用于高吞吐量探针;低吞吐量探针保留其编译默认值。
**采样率语义。** `0` = 自适应(可变工作负载的推荐默认设置)。`1` = 发送每一个事件(确定性,可用于重现性测试)。`N > 1` = 每 CPU 事件计数器;发送第 N 个事件。**不**适用于主机探针(`sched_switch`、`mm_page_alloc`、OOM、exec/exit/fork 从不被采样)。
**Python 遍历器选择。** `auto`(默认)运行用户空间遍历器;它支持 3.10/3.11/3.12,并通过 `process_vm_readv` 回退处理最高到级别 2 的 `ptrace_scope`。`ebpf` 运行内核内遍历器;同样支持 3.10/3.11/3.12,并且可在 `ptrace_scope=3` 下工作。`userspace` 强制使用用户空间遍历器(禁用任何自动提升)。
**关键事件可靠性。** OOM、进程 exec、exit 和 fork 事件流经一个独立于主 8MB/1MB 缓冲区的专用 256KB 环形缓冲区。它们从不被采样,从不被聚合,并且用户空间读取器会阻塞而不是丢弃 — 保证关键信号(fork 继承、OOM 关联、编排器修复所需)的送达。
## 许可证
**Ingero 是 100% 免费开源的。** 将它用于任何用途 - 个人、商业、企业,嵌入到你的产品中,修改它,重新分发它。没有使用限制,没有回传电话。
采用遵循标准 eBPF 分拆许可模型(与 Cilium、Falco 和大多数 eBPF 项目相同)的双重许可:
* **用户空间** (Go 代理、CLI、因果引擎、SQLite、MCP):[Apache License 2.0](LICENSE) - 最大程度的企业兼容性,无 copyleft 限制。
* **内核空间** (位于 `bpf/` 中的 eBPF C 代码):[GPL-2.0](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html) 或 [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - Linux 内核的 BPF 子系统要求使用 GPL-2.0;BSD-3-Clause 则允许嵌入到非 GPL 工具链中。
## 架构
sudo ingero check — system readiness
sudo ingero trace — live event stream
ingero explain --since 5m — automated diagnosis
sudo ingero trace — CUDA Graph lifecycle events
ingero explain — graph causal chain diagnosis
ingero demo --no-gpu incident — try without a GPU
标签:API集成, CUDA, Docker镜像, Go语言, GPU, Linux内核, MCP, Profiling, SRE, Vectored Exception Handling, 人工智能, 偏差过滤, 分布式系统, 可观测性, 响应大小分析, 因果分析, 客户端加密, 开源, 性能优化, 性能分析, 日志审计, 检测绕过, 深度学习, 用户代理, 用户模式Hook绕过, 监控, 程序破解, 自定义请求头, 请求拦截