凌晨 3 点,服务延迟飙升。你需要知道:哪个函数耗时最长?哪个系统调用最频繁?哪个进程在疯狂分配内存?传统的 APM 方案需要修改代码、部署 Agent,而 eBPF 提供了一种全新的可观测性范式——零侵入、低开销、内核级的追踪能力。
Brendan Gregg 将 eBPF 称为”Linux 可观测性的未来”——它不仅替代了 strace/perf 的部分功能,更提供了传统工具无法实现的内核级深度可观测性。
一、eBPF 可观测性的优势
1.1 与传统方案对比
| 维度 | 传统 APM | eBPF 可观测性 |
|---|---|---|
| 侵入性 | 需修改代码/部署 Agent | 零侵入 |
| 开销 | 5-15% | <1-3% |
| 覆盖范围 | 应用层 | 内核 + 应用层 |
| 部署 | 需要应用配合 | 即插即用 |
| 语言依赖 | 需 SDK | 语言无关 |
| 动态性 | 需重启 | 动态加载/卸载 |
1.2 eBPF 可观测性工具链
二、bpftrace:一行命令追踪内核
2.1 bpftrace 语法
bpftrace 使用类似 DTrace/awk 的高级语法:
probe[,probe...] [if (filter)] { action }| 语法元素 | 说明 | 示例 |
|---|---|---|
| probe | 探针定义 | kprobe:do_sys_open |
| filter | 过滤条件 | /pid == 1234/ |
| action | 动作块 | { printf(...); } |
| @map | Map 变量 | @count[comm] = count() |
| builtins | 内置变量 | pid, comm, tid, args |
| functions | 内置函数 | printf(), count(), sum() |
2.2 常用内置变量
| 变量 | 类型 | 说明 |
|---|---|---|
| pid | u64 | 进程 ID |
| tid | u64 | 线程 ID |
| uid | u64 | 用户 ID |
| comm | string | 进程名 |
| nsecs | u64 | 纳秒时间戳 |
| elapsed | u64 | 程序运行时间 |
| cpu | u32 | CPU 编号 |
| curtask | u64 | 当前 task_struct 指针 |
| args | struct | tracepoint 参数 |
2.3 常用内置函数
| 函数 | 说明 |
|---|---|
| printf(fmt, …) | 格式化输出 |
| count() | 计数 |
| sum(val) | 求和 |
| avg(val) | 平均值 |
| min(val) | 最小值 |
| max(val) | 最大值 |
| hist(val) | 直方图(log2) |
| lhist(val, min, max, step) | 线性直方图 |
| stack() | 内核调用栈 |
| ustack() | 用户态调用栈 |
| str(ptr) | 读取字符串 |
| exit() | 退出 bpftrace |
2.4 bpftrace 实战示例
# 统计每个进程的系统调用次数sudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @syscalls[comm] = count();}'
# 追踪文件打开,按进程统计sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @opens[comm] = count();}'
# I/O 延迟直方图sudo bpftrace -e 'kprobe:vfs_read { @start[tid] = nsecs; }kretprobe:vfs_read /@start[tid]/ { @latency = hist(nsecs - @start[tid]); delete(@start[tid]);}'
# 追踪 TCP 连接延迟sudo bpftrace -e 'kprobe:tcp_v4_connect { @start[tid] = nsecs; }kretprobe:tcp_v4_connect /@start[tid]/ { @connect_ns = hist(nsecs - @start[tid]); delete(@start[tid]);}'
# 火焰图数据采集sudo bpftrace -e 'profile:hz:99 { @[ustack()] = count();}'2.5 bpftrace 的 Map 操作
# 定期打印并清空 Mapsudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @syscalls[comm] = count();}interval:s:5 { print(@syscalls); clear(@syscalls);}'
# 打印后不清空(累计值)sudo bpftrace -e 'kprobe:vfs_read { @bytes[comm] = sum(retval);}interval:s:10 { print(@bytes);}'三、BCC:Python 前端 + 丰富工具集
3.1 BCC 工具一览
| 工具 | 用途 | 替代传统工具 |
|---|---|---|
| execsnoop | 追踪进程执行 | strace -e execve |
| opensnoop | 追踪文件打开 | strace -e openat |
| biolatency | I/O 延迟直方图 | iostat |
| biosnoop | I/O 事件追踪 | iotop |
| biotop | I/O Top | iotop |
| tcplife | TCP 连接生命周期 | ss |
| tcptracer | TCP 连接事件 | ss |
| tcpconnect | TCP 主动连接 | netstat |
| tcpaccept | TCP 被动连接 | netstat |
| tcpretrans | TCP 重传 | — |
| sockstat | Socket 统计 | ss |
| slabratetop | Slab 分配速率 | slabtop |
| memleak | 内存泄漏检测 | valgrind |
| offcputime | 离 CPU 时间 | perf |
| profile | CPU 性能分析 | perf top |
| stackcount | 调用栈计数 | perf |
| syncsnoop | 同步原语追踪 | — |
| vfsstat | VFS 统计 | — |
| writeback | 页写回追踪 | — |
3.2 BCC Python 前端
#!/usr/bin/env python3"""追踪进程执行,过滤指定父进程"""
from bcc import BPFimport argparse
# eBPF C 代码bpf_text = """#include <uapi/linux/ptrace.h>
struct event { u32 pid; u32 ppid; char comm[16]; char filename[256];};
BPF_RINGBUF(events);
TRACEPOINT_PROBE(sched, sched_process_exec) { struct event *e = events.ringbuf_reserve(sizeof(*e)); if (!e) return 0;
e->pid = bpf_get_current_pid_tgid() >> 32; e->ppid = PPID_FILTER; // 运行时替换
bpf_get_current_comm(&e->comm, sizeof(e->comm)); bpf_probe_read_kernel_str(&e->filename, sizeof(e->filename), args->filename);
events.ringbuf_submit(e, 0); return 0;}"""
# 解析参数parser = argparse.ArgumentParser()parser.add_argument("--ppid", type=int, default=0)args = parser.parse_args()
# 替换过滤器bpf_text = bpf_text.replace("PPID_FILTER", str(args.ppid))
# 加载 eBPF 程序b = BPF(text=bpf_text)
# 事件回调def handle_event(cpu, data, size): event = b["events"].event(data) print(f"PID={event.pid} PPID={event.ppid} " f"COMM={event.comm.decode()} FILE={event.filename.decode()}")
b["events"].open_ring_buffer(handle_event)
print("Tracing process exec... Hit Ctrl-C to end")
while True: try: b.ring_buffer_consume() except KeyboardInterrupt: break3.3 BCC 工具使用示例
# 追踪所有进程执行sudo /usr/share/bcc/tools/execsnoop
# 追踪文件打开sudo /usr/share/bcc/tools/opensnoop
# I/O 延迟直方图sudo /usr/share/bcc/tools/biolatency
# TCP 连接生命周期sudo /usr/share/bcc/tools/tcplife
# 内存泄漏检测(追踪 malloc/free 不匹配)sudo /usr/share/bcc/tools/memleak -p 1234
# CPU 火焰图sudo /usr/share/bcc/tools/profile -F 99 -f > out.stacks四、Beyla:零侵入应用性能监控
4.1 Beyla 架构
Beyla 是 Grafana 开源的零侵入 APM 工具,基于 eBPF 自动发现和监控应用:
4.2 Beyla 的核心能力
| 能力 | 说明 |
|---|---|
| 自动发现 | 基于进程/端口自动发现 HTTP/gRPC 服务 |
| 零侵入 | 无需修改应用代码或部署 SDK |
| 分布式追踪 | 自动生成 OpenTelemetry Span |
| RED 指标 | Rate/Errors/Duration 自动采集 |
| 语言无关 | 支持 Go/Java/Node.js/Python/Rust 等 |
| K8s 原生 | 自动关联 K8s 元数据 |
4.3 Beyla 部署
# Beyla 部署配置apiVersion: v1kind: ConfigMapmetadata: name: beyla-configdata: beyla-config.yml: | open_telemetry: traces: endpoint: "http://otel-collector:4318/v1/traces" metrics: endpoint: "http://otel-collector:4318/v1/metrics" discovery: services: - name: myapp namespace: default instrumentations: [http]---apiVersion: apps/v1kind: DaemonSetmetadata: name: beylaspec: selector: matchLabels: app: beyla template: spec: containers: - name: beyla image: grafana/beyla:latest securityContext: capabilities: add: ["CAP_BPF", "CAP_PERFMON", "CAP_SYS_RESOURCE"] volumeMounts: - name: config mountPath: /etc/beyla volumes: - name: config configMap: name: beyla-config五、性能分析方法论
5.1 USE 方法
Brendan Gregg 提出的 USE 方法(Utilization / Saturation / Errors):
| 指标 | 说明 | bpftrace/BCC 工具 |
|---|---|---|
| Utilization | 资源使用率 | profile, biolatency |
| Saturation | 资源饱和度(排队) | offcputime, runqlat |
| Errors | 错误数 | tcpretrans, biotop |
5.2 性能分析工作流
5.3 常用分析命令
# CPU 分析sudo bpftrace -e 'profile:hz:99 { @[ustack()] = count(); }' # 火焰图sudo /usr/share/bcc/tools/offcputime # 离 CPU 时间
# 内存分析sudo /usr/share/bcc/tools/memleak -p <pid> # 内存泄漏sudo bpftrace -e 'kprobe:kmalloc { @size[comm] = hist(arg1); }' # 分配大小
# I/O 分析sudo /usr/share/bcc/tools/biolatency # I/O 延迟sudo /usr/share/bcc/tools/biosnoop # I/O 事件
# 网络分析sudo /usr/share/bcc/tools/tcplife # TCP 连接生命周期sudo /usr/share/bcc/tools/tcpretrans # TCP 重传sudo bpftrace -e 'kprobe:tcp_drop { printf("drop: %s\n", comm); }' # 丢包六、动手实践
6.1 使用 bpftrace 进行 CPU 性能分析
# 采样 CPU 调用栈(99 Hz)sudo bpftrace -e 'profile:hz:99 { @[ustack()] = count(); }' > cpu.stacks
# 生成火焰图(使用 FlameGraph 工具)git clone https://github.com/brendangregg/FlameGraphsudo bpftrace -e 'profile:hz:99 { @[ustack()] = count(); }' | \ ./FlameGraph/stackcollapse-bpftrace.pl | \ ./FlameGraph/flamegraph.pl > cpu.svg6.2 使用 BCC 进行 I/O 分析
# I/O 延迟直方图sudo /usr/share/bcc/tools/biolatency# usecs : count distribution# 0 -> 1 : 0 | |# 2 -> 3 : 0 | |# 4 -> 7 : 0 | |# 8 -> 15 : 12 |******** |# 16 -> 31 : 45 |***************************** |# 32 -> 63 : 89 |************************************************|# 64 -> 127 : 23 |*************** |
# I/O Topsudo /usr/share/bcc/tools/biotop6.3 部署 Beyla 实现零侵入 APM
# Docker 部署 Beyladocker run -d --name beyla \ --pid=host \ --cap-add=CAP_BPF \ --cap-add=CAP_PERFMON \ -e BEYLA_OPEN_TELEMETRY_TRACES_ENDPOINT=http://otel:4318/v1/traces \ -e BEYLA_DISCOVERY_SERVICES_0_NAME=myapp \ -e BEYLA_DISCOVERY_SERVICES_0_INSTRUMENTATIONS=http \ grafana/beyla:latest七、BCC → libbpf-tools 迁移
7.1 为什么要迁移
BCC 工具运行时依赖内核头文件编译,在不同内核版本上经常编译失败。libbpf-tools 预编译 eBPF 程序,通过 CO-RE 实现一次编译到处运行:
| 维度 | BCC | libbpf-tools |
|---|---|---|
| 编译时机 | 运行时编译 | 预编译 |
| 内核头文件 | 必须安装 | 不需要 |
| 启动速度 | 慢(编译耗时) | 快(直接加载) |
| 可移植性 | 差 | 好(CO-RE) |
| 依赖 | clang、llvm、kernel-headers | 仅 libbpf |
bpftrace 和 BCC 在生产环境中可能因编译失败导致诊断工具不可用。对于关键排障场景,建议使用 libbpf-tools 的预编译二进制,确保工具随时可用。
7.2 追踪上下文传播
eBPF uprobe 可以拦截用户态函数调用,将内核级追踪数据与 OpenTelemetry Span 关联:
// uprobe 拦截 HTTP 请求处理函数,提取 trace contextSEC("uprobe/http_server_handle_request")int trace_http(struct pt_regs *ctx){ // 从函数参数中提取 trace_id // 通过 Ring Buffer 发送到用户态 // 用户态 Agent 关联 OTEL Span return 0;}这种关联方式让 eBPF 追踪数据可以与分布式追踪系统(Jaeger/Zipkin)整合,实现从内核到应用的全链路可观测。
Beyla 目前支持 Go、Java、Node.js、.NET、Python 和 Rust 应用的自动仪器化。对于不支持的运行时,可以通过手动注入 OpenTelemetry SDK 配合 eBPF 追踪使用。
八、本章小结
上一章从全景视角介绍了eBPF 安全与 LSM。 本章详解了 eBPF 可观测性的三大工具链:
| 主题 | 核心要点 | 关键词 |
|---|---|---|
| bpftrace | 一行命令追踪内核,适合快速诊断和临时分析 | bpftrace |
| BCC | Python 前端 + 丰富工具集,适合日常运维和自动化分析 | BCC |
| Beyla | 零侵入 APM,自动发现和监控应用,适合持续可观测性 | Beyla |
选择工具的决策:快速诊断用 bpftrace,日常运维用 BCC,持续监控用 Beyla。
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






