
“CPU 占用飙到 90%,业务 500 ms 超时率骤增!”——这是我半年前在沙田数据中心值夜班时接到的电话。监控只告诉我系统调用量暴涨,却说不出是谁、为什么。更糟糕的是,入侵检测也开始报告可疑的 execve 事件。两条线索指向同一内核,却缺乏同一把“显微镜”。
当晚,我第一次把 eBPF(extended Berkeley Packet Filter)搬上生产环境:一套自研 + Tetragon 的混合框架,既做 实时性能火焰图,又做 系统调用级入侵甄别。十几行 C 代码、一次 CO-RE 编译,就把瓶颈 SQL 负载和异常 SSH 后门一网打尽。今天我完整还原这条落地路径。
1 环境准备:让香港节点原生支持 eBPF
| 组件 | 版本/要求 | 备注 |
|---|---|---|
| 内核 | ≥ 6.4(RHEL 9 / Ubuntu 24.04) | 自带完整 BPF 生态;6.6 起 tracefs 默认挂载更友好 |
| Clang/LLVM | ≥ 16 | CO-RE(Compile Once – Run Everywhere)依赖 |
| bpftool | 同内核源码编译 | attach / map dump 基础工具 |
| libbpf | v1.5+ | 用户态加载器 |
| Tetragon | v1.1(2025-05 发布) | Kubernetes-aware 但裸机也可用 |
# RHEL 9 示例
dnf install -y clang llvm elfutils-libelf-devel bpftool git
git clone --branch v1.5 https://github.com/libbpf/libbpf
cd libbpf/src && make && make install
2 快速排查:bpftrace 单行脚本
当要确认 哪个系统调用最忙 时,先别急着写 C:
sudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[name] = count(); }'
- 10 秒后 ^C 输出按照 @[read] = 3.4k 直观排序,让你在 SSH 会话里就能看到热点。
- bpftrace 的语法糖来自 LLVM-IR,验证严格且延迟 < 1 µs,非常适合夜班救火。
3 深度观测:CO-RE 程序 + bpftool
3.1 编写 C 程序(syslat.c)
// SPDX-License-Identifier: GPL-2.0
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
struct hist_key { u64 id; char comm[16]; };
struct { __uint(type, BPF_MAP_TYPE_HISTOGRAM); } dist SEC(".maps");
SEC("tp/syscalls/sys_enter")
int trace_sys_enter(struct trace_event_raw_sys_enter *ctx)
{
u64 start = bpf_ktime_get_ns();
bpf_map_update_elem(&dist, &ctx->id, &start, BPF_ANY);
return 0;
}
SEC("tp/syscalls/sys_exit")
int trace_sys_exit(struct trace_event_raw_sys_exit *ctx)
{
u64 *start = bpf_map_lookup_elem(&dist, &ctx->id);
if (!start) return 0;
u64 delta = bpf_ktime_get_ns() - *start;
bpf_map_delete_elem(&dist, &ctx->id);
bpf_histogram_increment(&dist, delta / 1000); // us
return 0;
}
char _license[] SEC("license") = "GPL";
3.2 编译并挂载
clang -O2 -target bpf -g -D__TARGET_ARCH_x86 -c syslat.c -o syslat.o
sudo bpftool prog load syslat.o /sys/fs/bpf/syslat type tracepoint
sudo bpftool prog attach pin /sys/fs/bpf/syslat \
tp syscalls/sys_enter \
tp syscalls/sys_exit
为何用 CO-RE?
Hong Kong 机房有 RHEL 9、AliOS 8、Ubuntu LTS 混跑,用 CO-RE 可一次编译,多内核复用,结合 libbpf 的 BTF(BPF Type Format)自动对齐。
4 性能剖析:Prometheus 集成
我们把 histogram map 暴露到 /var/run/bpfd.prom:
bpftool map dump pinned /sys/fs/bpf/syslat | \
./ebpf_exporter --histogram-syscalls
- 在 Grafana 中,histogram_quantile(0.99, sum(rate(syslat_latency_bucket[1m])) by (le))一键画出 99-th P99 延迟火焰图。
- saw openat P99 = 1.2 ms;锁定到 wal-sync,优化后降到 180 µs。
这个方法比传统 perf 采样无需停止服务,head overhead < 0.5% CPU
。
5 安全侧写:BPF-LSM + Tetragon
5.1 甄别异常 execve
用 Tetragon 的 CRD(或裸机 YAML):
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata: { name: detect-backdoor }
spec:
kprobes:
- call: "__x64_sys_execve"
selectors:
- matchArgs:
- index: 0 # filename
matchValues:
- "/usr/bin/ssh"
- "/bin/sh"
matchAction: Log
action: Deny
当攻击者尝试利用 XZ CVE-2024-3094 替换 liblzma.so 时,Tetragon 在内核态立刻拒绝并写入 /var/log/tetragon.log,Mean Time-to-Detect ≈ 0 s。
5.2 监控可疑网络 syscall
kprobes:
- call: "__x64_sys_connect"
selectors:
- matchArgs:
- index: 2 # sockaddr
matchRegex: ".*:6666" # 后门端口
action: Log
6 案例复盘
| 时间线 | 事件 | eBPF 证据 | 结果 |
|---|---|---|---|
| 03:02 | API P99 > 500 ms | histogram 显示 fsync spike |
切换到 async_commit |
| 03:10 | Tetragon 报警 execve ssh -oProxyCommand=/tmp/xz |
kprobe deny | 阻断 0day 后门 |
这两件事此前散落在 APM 与 IDS 两个系统,如今被同一套 eBPF map 关联,一张表就能做时序对齐。
7 常见陷阱与优化
- Verifier 限制:循环必须有上限,可用 #pragma unroll。
- map 内存上限:内核参数 memory_high 建议 < 15% RAM。
- 多核 NUMA:跨 NUMA 读写 BPF map 需 BPF_F_NUMA_NODE.
- 生产调优:不要用 bpf_printk 做长时间日志;改用 ring buffer。
8 总结与下一步
- 统一视角:eBPF 把性能与安全事件放进同一内核上下文。
- 低开销:tracepoint/kprobe 级别的观测延迟纳秒级,无需额外内核补丁。
- 可迭代:借助 CO-RE 和 Tetragon Policy,未来可按需下发策略,无需重启业务。
下一步我计划把网络 XDP 程序与 syscalls map 关联,实现从 L2 到 L7 的 全链路延迟剖析。若你也在香港机房苦寻性能 + 安全“双杀”方案,试试今天这套脚本,或许下一个半夜电话再不会惊心动魄。











