eBPF 工具链实战
eBPF 工具链实战(应用型)
一、bpftool — 审计内核里到底挂了什么
bpftool 是内核自带的多功能 CLI,是看清”黑盒里挂了什么 eBPF”的标准工具。子命令:prog / map / link / net / perf / btf。
prog — 已加载的程序
bpftool prog list
# 4: sched_cls tag 3a1f... gpl attached to clsact:docker0
# loaded_at 12:30 uid 0 xlated 512B jited 320B memlock 4096B
bpftool prog show id 4 # 详情:指令数、jit、map 关联
bpftool prog dump xlated id 4 # 反汇编字节码(人类可读)
bpftool prog dump jited id 4 # 反汇编机器码
字段解读:tag 是程序哈希(同程序同 tag,便于比对);xlated=字节码大小,jited=机器码大小;attached to 显示挂载点。
net — 网络挂载点
bpftool net
# xdp dev eth0
# tc dev eth0 ingress
# tc dev cilium_host egress
map — BPF Map
bpftool map list # 所有 map,看容量/条目数(排查 map 爆了)
bpftool map dump id <MAP_ID> # 看内容(如 Cilium LB 映射)
bpftool map lookup id <ID> key <HEX> # 查单键
link / perf — 挂载与性能事件
bpftool link list # 程序与 hook 的绑定关系(比 prog 更细)
bpftool perf list # 正在采样的 perf_event 程序
临时摘钩验证(排障用)
bpftool net detach xdp dev eth0 # 临时卸载 XDP,验证"是不是 eBPF 干的"
bpftool net detach tc ingress dev eth0
怀疑 eBPF 引起异常时:先
prog list确认有程序在跑 →net看挂哪 → 必要时临时 detach 验证。
二、bpftrace — 一行式排障语言
bpftrace 是应用型排障神器:不用写 C、不用编译,直接 attach 到内核 hook 看现场。语法类 awk。
核心语法
| 元素 | 含义 | 例子 |
|---|---|---|
probe | 挂载点 | kprobe:vfs_read、tracepoint:tcp:tcp_retransmit_skb、profile:hz:99、softirq:*、usdt:* |
{} | 触发时动作 | { @[comm]=count(); } |
@name | BPF Map(自动聚合) | @[pid]、@us |
filter | 前置条件 | /pid==12345/、/@start[tid]/ |
nsecs / ktime | 纳秒时间戳 | 算延迟 |
args->xxx | tracepoint 参数 | args->saddr、args->dev |
comm / pid / tid | 内置变量 | — |
ustack / kstack | 用户/内核栈 | 火焰图 |
Map 聚合函数
| 函数 | 作用 |
|---|---|
count() | 计数 |
sum(x) / avg(x) | 求和 / 平均 |
min(x) / max(x) | 最小 / 最大 |
hist(x) | 幂次直方图(2 的幂) |
lhist(x, min, max, step) | 线性直方图 |
delete(@x) | 删除键(避免泄漏) |
实用脚本库(SRE 直接抄,多数需 root)
CPU
# 谁在吃 CPU(每 5s 打印)
bpftrace -e 'profile:hz:99 { @[comm]=count(); } interval:s:5 { print(@); clear(@); }'
# 某进程 on-CPU 火焰栈
bpftrace -e 'profile:hz:99 /pid==12345/ { @[ustack]=count(); }'
# 内核态热点
bpftrace -e 'profile:hz:99 { @[kstack]=count(); }'
IO / 存储
# 块设备 I/O 延迟直方图
bpftrace -e 'tracepoint:block:block_rq_issue { @s[args->dev,args->sector]=nsecs; }
tracepoint:block:block_rq_complete /@s[args->dev,args->sector]/ {
@us=hist((nsecs-@s[args->dev,args->sector])/1000); delete(@s[args->dev,args->sector]); }'
# 谁在大量写盘
bpftrace -e 'tracepoint:block:block_rq_issue /args->rwbs[0]=='"'"'W'"'"'/ { @[comm]=count(); }'
网络
# TCP 重传计数(定位烂链路)
bpftrace -e 'tracepoint:tcp:tcp_retransmit_skb { @[args->saddr,args->daddr]=count(); }'
# 主动/被动连接建立数
bpftrace -e 'tracepoint:tcp:tcp_connect { @active[args->daddr]=count(); }
kprobe:tcp_v4_syn_recv_sock { @passive=count(); }'
# 某端口流量字节数
bpftrace -e 'kprobe:tcp_sendmsg /args->sk->__sk_common.skc_dport==htons(80)/ { @bytes=sum(args->size); }'
内存
# 缺页异常(可能内存压力)
bpftrace -e 'software:page-faults:1 { @[comm]=count(); }'
# 谁在 alloc 大块内存
bpftrace -e 'uretprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc /retval>1048576/ { @[comm]=sum(retval); }'
调度 / 延迟
# 进程唤醒→运行 的调度延迟
bpftrace -e 'tracepoint:sched:sched_wakeup { @w[args->pid]=nsecs; }
tracepoint:sched:sched_switch /@w[args->next_pid]/ {
@lat=hist((nsecs-@w[args->next_pid])/1000); delete(@w[args->next_pid]); }'
# 进程被抢占/阻塞时长(oncpu 时间)
bpftrace -e 'tracepoint:sched:sched_switch { @oncpu[args->prev_comm]=hist((nsecs-@last[args->prev_pid])/1000); @last[args->prev_pid]=nsecs; }'
进程 / 文件系统
# exec 泄漏 / fork bomb 检测
bpftrace -e 'tracepoint:syscalls:sys_enter_execve { @[comm]=count(); }'
# 某文件 open 延迟
bpftrace -e 'kprobe:vfs_open /str(args->filename)=="/var/log/app.log"/ { @s=nsecs; }
kretprobe:vfs_open /@s/ { @us=hist((nsecs-@s)/1000); delete(@s); }'
# 哪些文件被频繁读
bpftrace -e 'kprobe:vfs_read { @[str(args->file->f_path.dentry->d_name.name)]=count(); }'
锁 / 同步
# 谁持有了某个 mutex 久(futex 等待)
bpftrace -e 'tracepoint:futex:futex_wait_start { @s[tid]=nsecs; }
tracepoint:futex:futex_wake { @us=hist((nsecs-@s[tid])/1000); delete(@s[tid]); }'
容器 / cgroup
# 某 cgroup 内所有进程的 CPU 聚合(结合 [[../cgroup v2 详解]])
bpftrace -e 'profile:hz:99 { @[cgroup]=count(); }'
# fork 进程数(配合 pids 控制器限流)
bpftrace -e 'tracepoint:sched:sched_process_fork { @[args->parent_comm]=count(); }'
这些脚本本质就是”处理逻辑写在内核、只把聚合结果吐出来”——正是 eBPF 性能模型 那节讲的”每聚合一次”。
三、BCC — 现成工具集
BCC(BPF Compiler Collection)提供一堆开箱即用的 Python 工具,不用自己写:
| 工具 | 功能 |
|---|---|
execsnoop | 实时显示新进程(排查异常启动) |
biosnoop | 块设备 I/O 追踪(含延迟) |
tcplife / tcpconnect | TCP 连接生命周期 / 主动连接 |
opensnoop | 追踪 open() 系统调用 |
cachestat | 页缓存命中率 |
runqlat | CPU 运行队列延迟 |
biolatency | 块设备 I/O 延迟直方图 |
softirqs / hardirqs | 软/硬中断耗时 |
execsnoop -x # 只看失败 exec
biosnoop -Q # 排队延迟
tcplife -p 12345 # 只看某 pid 的 TCP 连接
四、Cilium CLI
cilium status # 整体状态(含 eBPF 程序加载与否)
cilium status --verbose
cilium connectivity test # 内置连通性测试
cilium endpoint list # 所有 Endpoint 及策略状态
cilium bpf lb list # Service→Endpoint 的 eBPF 映射
cilium bpf ct list # conntrack 表
cilium hubble port-forward & # 转发 Hubble UI
hubble observe --verdict DROPPED # 看被丢的流量
五、libbpf(开发型预览)
libbpf 是 C 库,生产级 eBPF 程序的加载框架(CO-RE、BTF)。应用型了解即可——Cilium/BCC/bpftrace 底层都基于它。真正自己写生产 BPF 程序时再深入(第 2 档)。
关联知识
- eBPF 核心机制与安全 — prog/map/attach 的底层机制
- eBPF 排障实战 — 这些工具怎么串成排障流程
- eBPF 与 Cilium K8s 实战 — Cilium CLI 在 K8s 里的用法
- ../cgroup v2 详解 — pids 控制器配合 exec/fork 监控
学习时间
| 阶段 | 时间 | 备注 |
|---|---|---|
| 工具链 | 2026-07-16 | bpftool 五子命令、bpftrace 语法+12 类脚本、BCC 工具集、Cilium CLI |