ingero-io/ingero

GitHub: ingero-io/ingero

基于 eBPF 的 GPU 因果可观测性代理,零代码修改即可追踪从 Linux 内核事件到 CUDA API 调用再到 Python 源码行的完整因果链,实现低开销的 GPU 工作负载故障诊断。

Stars: 79 | Forks: 8

# Ingero - GPU 因果可观测性 [![Go Report Card](https://goreportcard.com/badge/github.com/ingero-io/ingero)](https://goreportcard.com/report/github.com/ingero-io/ingero) [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) [![GitHub Release](https://img.shields.io/github/v/release/ingero-io/ingero)](https://github.com/ingero-io/ingero/releases) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/9740a8fc76012008.svg)](https://github.com/ingero-io/ingero/actions/workflows/ci.yml) [![MCP](https://img.shields.io/badge/MCP-server-blue)](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% 的性能开销**、**零代码修改**,并且只需**一个二进制文件**。 ingero demo incident — CPU contention causes GPU latency spike, full causal chain diagnosis with root cause and fix recommendation ## 架构 Ingero architecture: per-node agent emits OTLP to Fleet collector; Fleet aggregates via ingeroprocessor + providerprocessor; backends are Prometheus, Grafana, MCP clients, and UDS sinks 代理在每个 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,也没有十六进制地址。工程师用纯英语提问,就能得到生产环境的根因回复。 ## 观看演示
sudo ingero check — system readiness
ingero check verifying kernel, BTF, NVIDIA driver, GPU model, CUDA libraries, and active processes
sudo ingero trace — live event stream
ingero trace showing live CUDA Runtime and Driver API statistics with rolling p50/p95/p99 latencies and host context
ingero explain --since 5m — automated diagnosis
ingero explain producing incident report with causal chains, root cause analysis, and fix recommendations
sudo ingero trace — CUDA Graph lifecycle events
ingero trace showing CUDA Graph capture, instantiate, and launch events alongside CUDA runtime and host events
ingero explain — graph causal chain diagnosis
ingero explain showing causal chain linking CUDA Graph launch to CPU contention with fix recommendations
ingero demo --no-gpu incident — try without a GPU
ingero demo running in synthetic mode without GPU, showing full causal chain diagnosis
``` 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 工具链中。
标签:API集成, CUDA, Docker镜像, Go语言, GPU, Linux内核, MCP, Profiling, SRE, Vectored Exception Handling, 人工智能, 偏差过滤, 分布式系统, 可观测性, 响应大小分析, 因果分析, 客户端加密, 开源, 性能优化, 性能分析, 日志审计, 检测绕过, 深度学习, 用户代理, 用户模式Hook绕过, 监控, 程序破解, 自定义请求头, 请求拦截