使用 OpenTelemetry 实现 eBPF 零代码插桩
OpenTelemetry Go 自动插桩通过 eBPF 探针挂载到运行中的 Go 进程上,并将 trace 导出至 SigNoz。eBPF 是 Linux 内核的一项特性,可以在进程中的指定位置运行小型程序。该 agent 以独立进程的形式运行在你的应用旁边,你的二进制文件无需重新构建。
本项目仍在开发中OpenTelemetry 官方将这个 eBPF agent 标记为开发中状态。它目前只支持追踪四个库,需要提升权限,并且在新的探针 API 就绪之前不接受新增的插桩探针。如果你能重新构建应用,建议改用 Go 编译时插桩,它更稳定、覆盖的库更多,而且不需要特殊权限。
提示当你无法修改构建过程时,可以使用 eBPF agent。常见场景包括:厂商提供的二进制文件、冻结的发布产物,或不归你管理的容器镜像。如果能修改构建,OpenTelemetry SDK 和编译时插桩能覆盖更多库。Go 插桩概览对这三种方式做了对比。
该 agent 只产出 trace,不产出 metrics 和 logs。SigNoz 会基于 span 派生出速率、错误和延迟指标。
使用自托管的 SigNoz?大部分步骤相同,只需按照 Cloud -> Self-Hosted 的说明更新 endpoint 并移除 ingestion key 请求头即可。
前提条件
- 一台 amd64 或 arm64 架构的 Linux 主机,内核版本为 4.19 或更高。在 macOS 和 Windows 上,请在 Linux 容器或虚拟机中运行该 agent。
- 拥有以 root 运行特权容器的权限。
- 一个至少使用了 受支持库 之一的 Go 应用。
- 应用二进制文件的路径,且该路径对 agent 可读。
- 一个 SigNoz 实例(Cloud 或 Self-Hosted 均可)。
工作原理
该代理以独立进程形式与你的应用并行运行。它会读取目标二进制文件,识别其支持的函数,并为其附加 uprobe(用户态探针)。uprobe 是一种内核钩子,当进程执行到指定指令时触发。
eBPF 程序将事件写入内核环形缓冲区(Ring Buffer)。代理读取该缓冲区,构建 span(链路追踪单元),并将其导出至 SigNoz。正因如此,代理需要共享的进程命名空间、root 权限以及特权容器:它需通过 /proc 读取其他进程信息,并将程序加载至内核。
将追踪数据发送至 SigNoz
步骤 1. 将代理部署在应用旁侧
代理通过 OTEL_GO_AUTO_TARGET_EXE 定位目标进程,该变量存储二进制文件的完整路径。由于代理与应用共享进程命名空间,因此可以读取该路径。
在 docker-compose.yaml 中将代理配置为服务。它需要挂载宿主机的进程命名空间、/proc 目录,并与应用保持一致的二进制文件路径:
services:
myapp:
image: <your-image>
volumes:
- ./bin:/app:ro
command: /app/myapp
go-auto:
image: otel/autoinstrumentation-go:v0.24.0
privileged: true
pid: "host"
environment:
OTEL_GO_AUTO_TARGET_EXE: /app/myapp
OTEL_SERVICE_NAME: "<service-name>"
OTEL_EXPORTER_OTLP_ENDPOINT: "https://ingest.<region>.signoz.cloud:443"
OTEL_EXPORTER_OTLP_HEADERS: "signoz-ingestion-key=<your-ingestion-key>"
volumes:
- ./bin:/app:ro
- /proc:/host/proc
depends_on:
- myapp在两个容器中将二进制文件挂载到相同路径。由于代理通过 /proc 读取路径,两者的路径必须一致。
启动服务栈:
Copydocker compose up -d在应用所在的同一 Pod 中,将代理作为第二个容器运行。
将摄入密钥(Ingestion Key)存储在 Secret 中:
Copykubectl create secret generic signoz-ingestion \
--from-literal=otlp-headers="signoz-ingestion-key=<your-ingestion-key>"
添加 agent 容器,并在 Pod 中设置 shareProcessNamespace:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
# agent 和应用程序必须共享进程命名空间。
shareProcessNamespace: true
containers:
- name: myapp
image: <your-image>
ports:
- containerPort: 8080
- name: autoinstrumentation-go
image: otel/autoinstrumentation-go:v0.24.0
imagePullPolicy: IfNotPresent
env:
- name: OTEL_GO_AUTO_TARGET_EXE
value: /usr/local/bin/myapp
- name: OTEL_SERVICE_NAME
value: "<service-name>"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "https://ingest.<region>.signoz.cloud:443"
- name: OTEL_EXPORTER_OTLP_HEADERS
valueFrom:
secretKeyRef:
name: signoz-ingestion
key: otlp-headers
securityContext:
runAsUser: 0
privileged: true
OTEL_GO_AUTO_TARGET_EXE 指向应用容器内的二进制文件路径。agent 通过共享的进程命名空间解析该路径。
应用配置:
Copykubectl apply -f deployment.yaml
如果集群启用了 Pod 安全准入控制,该 namespace 必须允许 privileged Pod。在应用 Deployment 前先给 namespace 打上标签:
Copykubectl label namespace <your-namespace> pod-security.kubernetes.io/enforce=privileged
针对宿主机上的进程运行 agent 容器。将二进制文件以只读方式挂载到与宿主机相同的路径:
Copydocker run --rm \
--privileged \
--pid=host \
-v /usr/local/bin/myapp:/usr/local/bin/myapp:ro \
-v /proc:/host/proc \
-e OTEL_GO_AUTO_TARGET_EXE=/usr/local/bin/myapp \
-e OTEL_SERVICE_NAME="<service-name>" \
-e OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443" \
-e OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>" \
otel/autoinstrumentation-go:v0.24.0
如果不使用 Docker,可以从源码构建 agent。构建需要 Linux 环境以及 clang、llvm 和 libbpf-dev,具体见构建说明。该项目不提供预编译的二进制文件。
请确认以下参数:
<region>:你的 SigNoz Cloud 区域。<your-ingestion-key>:你的 SigNoz ingestion key。<service-name>:服务在 SigNoz 中显示的名称,例如payment-service。OTEL_GO_AUTO_TARGET_EXE:应用程序二进制文件的完整路径,而不是进程名。
如果应用尚未启动,agent 会等待它启动。
第 2 步:确认 agent 已附加
查看 agent 日志,成功启动时会以这几行结尾:
Copyloading probe
instrumentation loaded successfully, starting...
此外,agent 在从二进制文件读取每个字段时会记录 Offset not cached, analyzing directly。当你的 Go 版本或依赖库版本比 agent 发布版本更新时,就会看到这条日志。此时 agent 会直接从二进制文件中的 DWARF 数据读取偏移量。
这种回退机制依赖调试信息。如果二进制文件是用 -ldflags "-s -w" 构建的,其中不含调试信息,agent 就会加载探针失败并直接退出。解决办法是构建时不做裁剪,或者使用缓存覆盖了你所用版本的 agent 发行版。
验证
向应用发送几个请求,然后打开 SigNoz。
进入 Services 页面,一两分钟内你的服务就会出现,并显示请求速率、错误率和延迟。
进入 Traces 页面,按服务名过滤。HTTP server span 会以路由作为名称,例如 GET /rolldice。

打开一条 Trace,数据库调用和出站 HTTP 请求会作为该请求的子 Span 展示。

支持的库
该 Agent 支持追踪以下四个库:
| 库 | Span 类型 |
|---|---|
net/http(客户端和服务端) | HTTP |
google.golang.org/grpc(客户端和服务端) | RPC |
database/sql | 数据库客户端 |
github.com/segmentio/kafka-go | 消息队列 |
在新探针 API 就绪之前,Agent 暂不添加新的探针,因此预期不会有太多新增支持。关于本指南所固定版本的兼容性矩阵,请参阅 v0.24.0 的兼容性矩阵。
需要支持更多库?编译时插桩支持 13 个库,包括 gin、go-redis、MongoDB 和 AWS SDK。这种方式需要重新编译,但无需特殊权限。
记录 SQL 语句
默认情况下,database/sql Span 不包含查询文本,且名称仅为 DB。可以通过以下两个变量更改这一行为:
OTEL_GO_AUTO_INCLUDE_DB_STATEMENT=true
OTEL_GO_AUTO_PARSE_DB_STATEMENT=trueOTEL_GO_AUTO_INCLUDE_DB_STATEMENT 用于在 Span 中添加 SQL 文本。OTEL_GO_AUTO_PARSE_DB_STATEMENT 还会根据操作类型和表名来命名 Span,例如 SELECT rolls。第二个变量仅在第一个变量已设置时才生效。
查询文本可能包含个人数据。只有在确保查询内容可以安全存储时,才开启这些功能。
捕获 OpenTelemetry API 的 span
如果应