土法炼钢兴趣小组的算法知识备份

【可观测性工程】eBPF 可观测性全景:bcc、bpftrace、libbpf 的工程路径

文章导航

分类入口
architectureobservability
标签入口
#ebpf#bcc#bpftrace#libbpf#co-re#btf#kprobe#uprobe#tracepoint#pixie#deepflow#observability#linux-kernel

目录

eBPF 可观测性技术栈

在可观测性的演进史上,扩展的伯克利包过滤器(eBPF,extended Berkeley Packet Filter)是近十年最具颠覆性的内核技术之一。它让我们可以在不修改一行业务代码、不重启任何进程的前提下,将自定义的、经过安全校验的小程序动态挂载到内核与用户态进程的执行路径上,采集系统调用、网络报文、调度事件、文件 I/O、块设备延迟、加密流量等几乎一切可被观察的事件。这种能力对传统监控体系是一次根本性的改写:应用程序不需要主动暴露埋点,运维和 SRE 也不需要打包内核模块,只要一条合适的 eBPF 程序就能让一台在生产环境跑了五年的机器回答”谁打开了这个文件”、“哪个容器的哪条 SQL 慢了”、“TLS 握手卡在哪一步”这类问题。

本篇是可观测性工程系列的第 14 篇,目标是从钩子机制出发,系统梳理 eBPF 可观测性的三条主流工程路径:字节码编译器集合(bcc,BPF Compiler Collection)、bpftrace 脚本语言、以及 libbpf 配合一次编译到处运行(CO-RE,Compile Once – Run Everywhere)的现代工程方案。在此之上再穿过 BPF 类型格式(BTF,BPF Type Format)、BPF 骨架(skeleton)、内核版本兼容矩阵、生产部署中的权能(capability)与验证器(verifier)坑点,直到 Pixie、DeepFlow、Grafana Beyla、Parca 等商业或开源产品的落地路径,最后给出面向国内生产环境的选型建议。阅读完本篇后,你应当能够独立判断:一个具体的可观测性需求,到底应该用 bpftrace 写两行一把梭、用 bcc 拉起一个 Python 脚本、还是沉到 libbpf + CO-RE 做一个可发布的二进制。

一、eBPF 的可观测性意义

在 eBPF 普及之前,Linux 生态里做深度可观测性的方法大致分三类,每一类都有显著的工程代价。

第一类是用户态插桩,典型做法包括动态链接预加载(LD_PRELOAD)、应用层 SDK 埋点、代理(Agent)注入字节码。这类方案的优势是语义清晰,可以精确拿到业务上下文;缺点是必须修改部署,重启进程,语言和运行时绑定严重,且看不到内核发生了什么。比如一个请求在内核 TCP 层丢包,在 sidecar 里什么也抓不到。

第二类是内核跟踪工具,如 strace、ltrace、perf、ftrace,甚至更早年的 SystemTap 和 DTrace。strace 基于进程跟踪(ptrace),对目标进程的性能影响是数量级的,典型场景下会让系统调用慢十倍以上;perf 和 ftrace 能力强,但输出需要后处理,不便直接做生产级别的聚合;SystemTap 要编译内核模块,意味着每次升级、每个发行版都要适配,在生产里几乎没人敢用。DTrace 在 Solaris 上一骑绝尘,但在 Linux 上始终因为许可和工程代价没有进入主流。

第三类是内核模块,直接在内核态写 C 代码。能力无上限,代价是风险无上限:一行空指针解引用就会导致宿主机崩溃,一个不小心的持锁会让整台机器软锁死(softlockup)。没有哪个 SRE 团队会允许把一个未经严格评审的第三方内核模块装到线上机器上。

eBPF 的出现,同时解决了这三条路线的核心痛点:

从历史上看,eBPF 的前身是 1992 年提出的经典 BPF(cBPF,classic BPF),最初仅用于 tcpdump 的过滤表达式编译。2014 年,Alexei Starovoitov 将其扩展为 eBPF 并合入 Linux 3.18,自此 BPF 从”内核包过滤器”演化为”内核可编程平面”。2016 年 XDP(eXpress Data Path)合入,BPF 开始承担高性能网络;2018 年 BTF 合入,为 CO-RE 铺路;2020 年前后 fentry/fexit、BPF 蹦床(trampoline)、LSM BPF 陆续进入主线。到本文成稿时(对应 Linux 6.x 主线),eBPF 已覆盖跟踪、网络、安全、调度等几乎所有内核子系统。

对可观测性工程而言,eBPF 的意义不仅是”多了一种工具”,而是让内核第一次真正成为可观测性数据平面的一等公民。过去我们习惯把应用层指标、日志、追踪视作三大支柱,但只要内核是黑盒,所有排障路径都会在某个节点遇到”不知道”。eBPF 把这层黑盒打开,且代价可控。

二、eBPF 的钩子类型与发展路线

eBPF 程序本身并不神奇,真正决定它能观察到什么的是挂载点(attach point / hook)。不同挂载点对应不同的程序类型(program type),每种程序类型有不同的上下文结构、能调用的内核辅助函数(helper)集合、稳定性保证和性能特征。对于可观测性场景,最常用的挂载点可以分为七大类。

2.1 kprobe / kretprobe

内核探针(kprobe)是 eBPF 可观测性中最朴素也最强大的机制。原理是在目标内核函数的入口处动态替换一条指令为 int3 断点(x86 上),命中断点时陷入异常处理程序,执行挂载的 eBPF 程序,然后返回原指令继续执行。kretprobe 类似,但挂在函数返回路径上,用于拿到返回值。

kprobe 的杀手级优势是几乎可以挂到任何内核符号上tcp_connectvfs_readdo_sys_openat2ext4_file_write_iter 等等。只要这个符号没有被内联(inlined),kprobe 就能生效。缺点是:

一个用 bpftrace 写的示例,跟踪所有 TCP 主动连接:

bpftrace -e 'kprobe:tcp_connect { printf("%s -> pid=%d\n", comm, pid); }'

2.2 uprobe / uretprobe

用户态探针(uprobe)是 kprobe 在用户态的镜像,原理同样是 int3 替换。uprobe 可以挂到任何用户态二进制或共享库的任意符号、甚至任意偏移上。这使得我们可以在不重编应用的情况下,跟踪应用自己的函数、跟踪标准库函数、跟踪加密库函数。

一个典型用例是加密流量可观测性:HTTPS 流量在网卡上是密文,传统抓包无法看到 HTTP 请求内容,但如果我们在 OpenSSL 的 SSL_read / SSL_write 上挂 uprobe,就能在加密前和解密后的明文缓冲区拿到完整 HTTP 报文。这正是 Pixie、DeepFlow 等商业化 eBPF 产品实现”零侵入 HTTPS 可观测”的底层机制。

bpftrace -e '
uprobe:/usr/lib/x86_64-linux-gnu/libssl.so.3:SSL_write {
    printf("pid=%d comm=%s wrote %d bytes\n", pid, comm, arg2);
}'

uprobe 的开销比 kprobe 更高(每次命中约 1–3μs),因为涉及用户态到内核态的往返,因此在热路径(如每次调用都命中的小函数)上要谨慎使用。

2.3 静态跟踪点(Tracepoints)

静态跟踪点是内核开发者预先在关键路径上埋下的稳定探针,格式为 subsystem:event_name,例如 sched:sched_switch(进程切换)、syscalls:sys_enter_read(read 系统调用入口)、block:block_rq_issue(块设备请求下发)、net:netif_receive_skb(网卡收包)。

相比 kprobe,tracepoint 的优势是:

查看当前内核支持的所有 tracepoint:

sudo ls /sys/kernel/debug/tracing/events/

生产环境可观测性程序应优先选 tracepoint,只有在 tracepoint 无法覆盖时才退化到 kprobe。

2.4 USDT:用户态静态跟踪点

USDT(User Statically-Defined Tracing)是用户态程序在源码里预先埋好的静态探针,最早由 DTrace 引入。应用通过 DTRACE_PROBE 宏(或 systemtap 的 STAP_PROBE)在关键路径上放置一条空指令(nop),eBPF 可以把这条 nop 替换为 int3,从而实现类似 tracepoint 的用户态稳定探针。

大量主流软件内置 USDT,包括:

相比 uprobe 挂到 SSL_write 这种”第三方函数名”,USDT 是应用作者主动暴露的”稳定可观测接口”,可移植性好得多。

2.5 fentry / fexit(BPF 蹦床)

从 Linux 5.5 开始,内核引入了 BPF 蹦床(BPF trampoline)机制,对应的程序类型是 fentry(function entry)和 fexit(function exit)。与 kprobe 相比,fentry/fexit 的实现方式完全不同:它不依赖 int3 断点,而是通过 ftrace 的函数跟踪器(ftrace function tracer)直接在函数入口处跳转到一段生成的蹦床代码,蹦床调用 BPF 程序后再跳回原函数。

实测数据(Brendan Gregg 等人的基准测试)显示,fentry 的单次命中开销约为 kprobe 的 1/2 到 1/3(在约 30–80ns 量级),特别是在多个 BPF 程序挂到同一函数时,fentry 可以批量调用,效率进一步提升。此外,fexit 与 kretprobe 的一个关键区别是,fexit 能同时拿到入参和返回值,而不需要像 kretprobe 那样用 map 存入参、在返回时取。

SEC("fentry/tcp_connect")
int BPF_PROG(trace_tcp_connect, struct sock *sk) {
    bpf_printk("tcp_connect: sk=%p\n", sk);
    return 0;
}

SEC("fexit/tcp_connect")
int BPF_PROG(trace_tcp_connect_ret, struct sock *sk, int ret) {
    bpf_printk("tcp_connect returned %d\n", ret);
    return 0;
}

建议:目标内核 ≥ 5.5 时,优先使用 fentry/fexit;需要兼容老内核时才用 kprobe/kretprobe。

2.6 XDP / TC / Socket Filter

网络方向的 eBPF 钩子不是本篇重点,但可观测性视角需要略作介绍。

性能上,XDP 处理 64 字节小包可以达到每秒千万级(10Mpps+),TC 次之,socket filter 最慢。可观测性用例里,XDP 通常用于全网抓包采样,TC 用于 L4 连接追踪,socket filter 用于按进程过滤。

2.7 LSM BPF 钩子

从 Linux 5.7 开始,eBPF 可以挂载到 Linux 安全模块(LSM,Linux Security Module)钩子上,如 file_opensocket_connectbprm_check_security。这条路径主要面向安全场景(如运行时威胁检测),但对可观测性的意义是:LSM 钩子位于权限检查路径,能稳定地观察到”谁在访问什么资源”,比 kprobe 挂到 VFS 函数更规范。

Tetragon(Isovalent 开源)和 Falco(CNCF 毕业项目)都大量使用 LSM BPF 做运行时观测。

三、bcc(BPF Compiler Collection)

bcc 是 IO Visor 项目于 2015 年开源的 BPF 工具集,是 eBPF 在可观测性领域进入大众视野的第一代工程方案,也是 Brendan Gregg 著作《BPF Performance Tools》中绝大部分示例的基础。

3.1 bcc 的架构与工作流程

bcc 的核心设计是”Python 前端 + Clang/LLVM 后端 + 运行时内核头文件”:

  1. 用户编写一个 Python 脚本,其中内嵌 C 语言的 BPF 程序字符串。
  2. Python 侧的 bcc 库在运行时调用 Clang/LLVM,把 C 字符串编译为 BPF 字节码。
  3. 字节码通过 bpf() 系统调用加载进内核,挂载到指定钩子。
  4. Python 侧通过 BPF 映射(BPF map)读取内核态采集的数据,做展示或聚合。

这个架构的核心问题是:Clang/LLVM 和内核头文件必须在目标机器上可用。在最初几年的 bcc 部署里,运维要在每台被观测机器上装几百兆的 LLVM 工具链和 kernel-devel / linux-headers 包,并且每次内核升级都要同步更新。这在大规模生产集群里是巨大的工程负担,也是后来 libbpf + CO-RE 要解决的核心痛点。

3.2 bcc 工具集:bcc-tools 开箱即用清单

bcc 项目附带数十个现成的诊断工具,位于 /usr/share/bcc/tools/ 目录。以下是最常用的子集:

工具 功能 典型用法
execsnoop 跟踪进程创建(exec) 谁在偷偷跑 shell 脚本
opensnoop 跟踪文件打开 哪个进程在读敏感文件
tcpconnect 跟踪 TCP 主动连接 容器在连哪些外部地址
tcpaccept 跟踪 TCP 被动接受 服务端连接情况
tcpretrans 跟踪 TCP 重传 网络质量诊断
biolatency 块设备 I/O 延迟直方图 磁盘慢请求定位
biosnoop 块设备 I/O 逐事件跟踪 哪个进程在大量写盘
runqlat 调度器运行队列延迟 CPU 竞争分析
offcputime 进程离 CPU 时间堆栈 阻塞在哪一步
profile 基于采样的 CPU 性能剖析 火焰图原始数据
cachestat 页缓存命中率 内存压力分析
funclatency 任意函数耗时分布 热点函数定位
filetop 按进程统计文件 I/O I/O 大户排查
sslsniff OpenSSL/GnuTLS 明文抓取 加密流量可观测

这些工具绝大多数只需 root 权限即可运行,不需要重启任何服务,也不需要修改被观察进程。

3.3 一个真实的 bcc Python 脚本

下面是一个简化版的 HTTP 请求跟踪器,挂 kprobe 到 tcp_sendmsg,在 payload 里识别 HTTP 请求行:

#!/usr/bin/env python3
from bcc import BPF
from time import strftime

bpf_text = r"""
#include <uapi/linux/ptrace.h>
#include <net/sock.h>
#include <bcc/proto.h>

struct event_t {
    u32 pid;
    u32 size;
    char comm[16];
    char payload[64];
};

BPF_PERF_OUTPUT(events);

int trace_tcp_sendmsg(struct pt_regs *ctx, struct sock *sk,
                      struct msghdr *msg, size_t size) {
    if (size < 4) return 0;
    struct event_t ev = {};
    ev.pid = bpf_get_current_pid_tgid() >> 32;
    ev.size = size;
    bpf_get_current_comm(&ev.comm, sizeof(ev.comm));

    struct iov_iter *iter = &msg->msg_iter;
    const struct iovec *iov = iter->iov;
    void *base = 0;
    bpf_probe_read_kernel(&base, sizeof(base), &iov->iov_base);
    bpf_probe_read_user(&ev.payload, sizeof(ev.payload), base);

    // 简单判断是否 HTTP 请求
    if (ev.payload[0] == 'G' && ev.payload[1] == 'E' && ev.payload[2] == 'T') {
        events.perf_submit(ctx, &ev, sizeof(ev));
    }
    return 0;
}
"""

b = BPF(text=bpf_text)
b.attach_kprobe(event="tcp_sendmsg", fn_name="trace_tcp_sendmsg")

def handle(cpu, data, size):
    ev = b["events"].event(data)
    print("%s pid=%d comm=%s size=%d payload=%s" % (
        strftime("%H:%M:%S"), ev.pid, ev.comm.decode(), ev.size,
        bytes(ev.payload).split(b'\r\n')[0].decode('utf-8', 'replace')))

b["events"].open_perf_buffer(handle)
while True:
    try: b.perf_buffer_poll()
    except KeyboardInterrupt: break

这个脚本展示了 bcc 的典型工作流:内嵌 C、Python 加载、perf buffer 回传、用户态解析。开发体验很不错,但部署到没有 LLVM 的机器上会直接报错。

3.4 bcc 的局限

这些问题在笔记本上做一次性排障时完全可以接受,但要把 bcc 工具嵌入生产 agent 就捉襟见肘了。libbpf + CO-RE 正是为此诞生。

四、bpftrace 脚本语言

如果说 bcc 是”给工程师用的 eBPF 开发框架”,bpftrace 就是”给 SRE 用的 eBPF 命令行”。bpftrace 由 Alastair Robertson 发起,现已成为 IO Visor 下与 bcc 并列的顶级项目,灵感直接来自 DTrace 的 D 语言。

4.1 bpftrace 的架构

bpftrace 的内部架构是”领域特定语言(DSL,Domain-Specific Language)→ LLVM IR → BPF 字节码”:

  1. 用户写一段 bpftrace 脚本或一行命令。
  2. bpftrace 前端解析脚本,输出 LLVM 中间表示(IR)。
  3. LLVM 后端把 IR 编译为 BPF 字节码并加载。
  4. bpftrace 运行时负责读 map、格式化输出、退出时自动打印聚合结果。

bpftrace 同样依赖 LLVM,但它对用户隐藏了所有 C 语言细节,上手门槛极低。

4.2 语法速览

bpftrace 的基本单元是探针块(probe block),格式为:

probe-type:target /predicate/ { action; }

例如:

bpftrace -e 'tracepoint:syscalls:sys_enter_openat /comm == "cat"/ {
    printf("%s opened %s\n", comm, str(args->filename));
}'

4.3 内建变量

bpftrace 提供一组开箱即用的上下文变量:

变量 含义
pid 当前进程 PID
tid 当前线程 TID
uid 当前用户 UID
comm 当前进程名(16 字节截断)
cpu 当前 CPU 编号
nsecs 当前纳秒时间戳
kstack 内核态调用栈
ustack 用户态调用栈
args 探针参数结构体(tracepoint 专用)
arg0..arg9 探针位置参数(kprobe/uprobe)
retval 返回值(kretprobe/uretprobe/fexit)

4.4 内建函数与聚合

bpftrace 的聚合语法是它的杀手锏:

聚合函数 作用
count() 计数
sum(x) 求和
avg(x) 平均
min(x) / max(x) 极值
hist(x) 指数直方图(2 的幂)
lhist(x, min, max, step) 线性直方图
stats(x) 聚合统计

聚合结果保存在映射(map)里,脚本退出时自动打印。

4.5 经典单行命令

文件打开跟踪(opensnoop)

bpftrace -e 'tracepoint:syscalls:sys_enter_openat {
    printf("%-6d %-16s %s\n", pid, comm, str(args->filename));
}'

调度器运行队列延迟(runqlat)

bpftrace -e '
tracepoint:sched:sched_wakeup    { @qt[args->pid] = nsecs; }
tracepoint:sched:sched_wakeup_new{ @qt[args->pid] = nsecs; }
tracepoint:sched:sched_switch {
    if (@qt[args->next_pid]) {
        @us = hist((nsecs - @qt[args->next_pid]) / 1000);
        delete(@qt[args->next_pid]);
    }
}'

块设备 I/O 追踪(biosnoop)

bpftrace -e '
tracepoint:block:block_rq_issue  { @start[args->dev, args->sector] = nsecs; }
tracepoint:block:block_rq_complete {
    $s = @start[args->dev, args->sector];
    if ($s) {
        printf("%-8d %-16s %d us\n", pid, comm, (nsecs - $s) / 1000);
        delete(@start[args->dev, args->sector]);
    }
}'

TCP 连接跟踪(tcpconnect)

bpftrace -e '
kprobe:tcp_v4_connect {
    @sk[tid] = arg0;
}
kretprobe:tcp_v4_connect /@sk[tid]/ {
    $sk = (struct sock *)@sk[tid];
    $dport = ((uint16)$sk->__sk_common.skc_dport);
    printf("%s pid=%d dport=%d\n", comm, pid, ($dport >> 8) | (($dport & 0xff) << 8));
    delete(@sk[tid]);
}'

基于采样的 CPU 剖析(profile)

bpftrace -e 'profile:hz:99 { @[kstack] = count(); }'

这一行就能以 99Hz 的频率采集内核栈,停止时自动输出每条栈路径的次数,直接可以扔进 FlameGraph 画火焰图。

4.6 bpftrace 与 bcc 的选择

经验法则:

五、libbpf + CO-RE(Compile Once – Run Everywhere)

libbpf 是内核团队维护的 C 语言 BPF 用户态库,位于 tools/lib/bpf/,也以独立项目 libbpf 发布。它是当前 eBPF 工程化的事实标准路径。

5.1 问题:bcc 的部署痛点

回顾 bcc 的架构:运行时编译依赖 Clang/LLVM 和内核头文件。这意味着:

  1. 发布包必须附带 LLVM,体积膨胀。
  2. 每台目标机器必须安装匹配的 linux-headers-$(uname -r)
  3. 启动延迟高,不能做”加载后即用”的轻量 agent。
  4. 同一份 C 源码在不同内核版本下,因 struct 字段偏移变化可能读错内存。

5.2 CO-RE 的核心思路

CO-RE 的目标是:一次在开发机上编译出 BPF 字节码,在任意内核版本上直接加载运行。它由三部分协同完成:

  1. BTF:内核把自身所有类型信息以紧凑格式嵌入 /sys/kernel/btf/vmlinux
  2. CO-RE 重定位(relocation):Clang 编译 BPF 程序时,对字段访问生成特殊的重定位记录(.BTF.ext 段),记录”我要读 struct task_structpid 字段”这种语义信息,而不是写死的偏移。
  3. libbpf 运行时修正:加载时,libbpf 读取目标内核的 BTF,查到 task_struct.pid 在当前内核的真实偏移,patch 进字节码,再提交给验证器。

这样即使内核版本变了、字段偏移变了、甚至字段换了位置,同一份字节码依然能正确工作。

5.3 BTF 简介

BTF 是一种极简的类型描述格式,比 DWARF 小两个数量级,专为 BPF 设计。一个现代 Linux 发行版(Ubuntu 20.10+、Fedora 31+、CentOS Stream 9+、RHEL 8.6+、openEuler 22.03+)都会默认开启 CONFIG_DEBUG_INFO_BTF=y,把 BTF 塞进内核镜像,运行时可以直接读:

ls -lh /sys/kernel/btf/vmlinux

对于老内核(如 CentOS 7 的 3.10),需要借助 BTFGen 或第三方维护的 BTF 包(BTFHub 项目维护了绝大部分主流发行版的 BTF 生成结果),把 BTF 单独带进去。

5.4 libbpf API 速览

libbpf 的核心 API 围绕”对象(object)→ 程序(program)→ 链接(link)→ 映射(map)“四个概念:

struct bpf_object *obj = bpf_object__open_file("prog.bpf.o", NULL);
bpf_object__load(obj);
struct bpf_program *prog = bpf_object__find_program_by_name(obj, "handle_exec");
struct bpf_link *link = bpf_program__attach(prog);
struct bpf_map *map = bpf_object__find_map_by_name(obj, "events");

5.5 BPF 骨架(skeleton)

手写这些 API 调用很繁琐。bpftool gen skeleton 能根据 .bpf.o 自动生成一个 C 头文件(.skel.h),把加载、挂载、读 map 全部封装成结构体成员调用:

#include "execsnoop.skel.h"

int main() {
    struct execsnoop_bpf *skel = execsnoop_bpf__open_and_load();
    execsnoop_bpf__attach(skel);
    // 读 skel->maps.events
    execsnoop_bpf__destroy(skel);
}

5.6 一个完整的 libbpf + CO-RE 程序

内核侧(execsnoop.bpf.c):

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>

char LICENSE[] SEC("license") = "GPL";

struct event {
    u32 pid;
    u32 ppid;
    char comm[16];
    char filename[128];
};

struct {
    __uint(type, BPF_MAP_TYPE_RINGBUF);
    __uint(max_entries, 256 * 1024);
} events SEC(".maps");

SEC("tracepoint/syscalls/sys_enter_execve")
int handle_execve(struct trace_event_raw_sys_enter *ctx) {
    struct event *e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
    if (!e) return 0;

    struct task_struct *task = (struct task_struct *)bpf_get_current_task();
    e->pid = bpf_get_current_pid_tgid() >> 32;
    e->ppid = BPF_CORE_READ(task, real_parent, tgid);
    bpf_get_current_comm(&e->comm, sizeof(e->comm));
    bpf_probe_read_user_str(&e->filename, sizeof(e->filename),
                            (const char *)ctx->args[0]);

    bpf_ringbuf_submit(e, 0);
    return 0;
}

用户态侧(execsnoop.c):

#include <stdio.h>
#include <signal.h>
#include <bpf/libbpf.h>
#include "execsnoop.skel.h"

static volatile bool exiting;
static void sig_handler(int sig) { exiting = true; }

static int handle_event(void *ctx, void *data, size_t sz) {
    const struct event *e = data;
    printf("%-6d %-6d %-16s %s\n", e->pid, e->ppid, e->comm, e->filename);
    return 0;
}

int main(int argc, char **argv) {
    struct ring_buffer *rb = NULL;
    struct execsnoop_bpf *skel;
    int err;

    signal(SIGINT, sig_handler);
    skel = execsnoop_bpf__open_and_load();
    if (!skel) return 1;

    err = execsnoop_bpf__attach(skel);
    if (err) goto cleanup;

    rb = ring_buffer__new(bpf_map__fd(skel->maps.events),
                          handle_event, NULL, NULL);
    if (!rb) goto cleanup;

    printf("%-6s %-6s %-16s %s\n", "PID", "PPID", "COMM", "FILENAME");
    while (!exiting) {
        err = ring_buffer__poll(rb, 100 /* ms */);
        if (err == -EINTR) { err = 0; break; }
        if (err < 0) break;
    }

cleanup:
    ring_buffer__free(rb);
    execsnoop_bpf__destroy(skel);
    return 0;
}

编译命令(在任意带 Clang 的开发机上执行一次即可):

clang -g -O2 -target bpf -D__TARGET_ARCH_x86 \
    -I. -c execsnoop.bpf.c -o execsnoop.bpf.o
bpftool gen skeleton execsnoop.bpf.o > execsnoop.skel.h
clang -O2 execsnoop.c -lbpf -o execsnoop

得到的 execsnoop 是一个约 1MB 的静态二进制,拷到任意支持 BTF 的内核上都能直接运行,不需要 Clang,不需要内核头文件,启动时间毫秒级。

5.7 vmlinux.h 生成

vmlinux.h 是从 BTF 反生成的”整合版内核头文件”,包含了你能用到的所有内核类型定义:

bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

开发时用它代替一堆 #include <linux/...>,既方便又稳定。

六、BTF 与 BPF 骨架

上一节介绍了 CO-RE 的思想,这一节展开 BTF 的工程细节。

6.1 BTF 格式

BTF 是一种类型描述格式,由以下若干”类型条目”组成:

与 DWARF 相比,BTF 省略了所有行号、变量位置、表达式等调试信息,只保留”类型布局”,体积只有 DWARF 的 1%–3%。

6.2 BTF 生成链

  1. 内核编译时,pahole(dwarves 项目)从 DWARF 调试信息抽取类型并转为 BTF。
  2. BTF 被嵌入 vmlinux 的 .BTF 段。
  3. 运行时通过 /sys/kernel/btf/vmlinux 暴露给用户态。
  4. 内核模块的 BTF 以 /sys/kernel/btf/<modname> 暴露。

6.3 查看 BTF

bpftool btf dump file /sys/kernel/btf/vmlinux | grep 'STRUCT task_struct'
bpftool btf dump file /sys/kernel/btf/vmlinux format c | less

6.4 骨架生成的内部细节

bpftool gen skeleton 输出的 .skel.h 文件本质上是:

  1. .bpf.o 的字节内容用 __attribute__((section)) 内嵌到 C 文件里。
  2. 为每个 map、每个 prog 生成对应的成员指针。
  3. 提供 openloadattachdestroy 四个封装函数。

这让用户态代码不再需要 bpf_object__open_file 这类 API,加载目标 BPF 对象就像使用一个普通的 C 库。

七、生产部署:内核版本兼容性

eBPF 生态的工程现实是:哪个特性能用,取决于你集群里最老的那台机器的内核版本。下表列出了可观测性相关特性在主要内核版本中的可用性:

内核版本 发布时间 关键特性 生产定位
3.18 2014-12 eBPF 初版,kprobe 不推荐
4.1 2015-06 BPF_PROG_TYPE_KPROBE 不推荐
4.4 2016-01 perf event output 老系统最低线
4.14 2017-11 LTS,basic BPF maps、socket filter 兼容底线
4.18 2018-08 BTF 最早版本 过渡
4.19 2018-10 LTS,XDP 稳定 网络场景
5.1 2019-05 BPF_PROG_TYPE_TRACING fentry 基础
5.2 2019-07 百万指令上限(1M insns) 复杂程序
5.4 2019-11 LTS,bpf_d_path,fentry 部分 生产可用
5.5 2020-01 fentry/fexit 完整 推荐起点
5.7 2020-05 LSM BPF、ring buffer 推荐
5.8 2020-08 BTF-enabled maps、CAP_BPF 雏形 推荐
5.10 2020-12 LTS,BPF 特性大量成熟 生产主流
5.11 2021-02 task local storage 可选
5.13 2021-06 CAP_BPF 完整拆分 容器友好
5.15 2021-10 LTS,BPF 生产最佳实践 强烈推荐
6.1 2022-12 LTS,kfunc 广泛可用 未来
6.6 2023-10 LTS,BPF open-coded iterators 未来

7.1 推荐的最低内核版本

7.2 CAP_BPF vs CAP_SYS_ADMIN

Linux 5.8 引入了独立的 CAP_BPF 权能(capability),5.13 完成拆分。在此之前,加载 BPF 程序需要 CAP_SYS_ADMIN,这是一种”近 root”权限,给容器授权风险极大。

5.13+ 之后,典型的 eBPF agent 容器只需要:

这远比 privileged: true 安全。

7.3 容器部署

在 Kubernetes 里部署 eBPF agent,典型的 DaemonSet 配置:

apiVersion: apps/v1
kind: DaemonSet
metadata: { name: ebpf-agent, namespace: obs }
spec:
  selector: { matchLabels: { app: ebpf-agent } }
  template:
    metadata: { labels: { app: ebpf-agent } }
    spec:
      hostPID: true
      hostNetwork: true
      containers:
      - name: agent
        image: registry.example.com/ebpf-agent:v1.2.0
        securityContext:
          capabilities:
            add: ["BPF", "PERFMON", "NET_ADMIN", "SYS_RESOURCE"]
          privileged: false
        volumeMounts:
        - { name: sys, mountPath: /sys, readOnly: true }
        - { name: debugfs, mountPath: /sys/kernel/debug }
        - { name: btf, mountPath: /sys/kernel/btf, readOnly: true }
      volumes:
      - { name: sys, hostPath: { path: /sys } }
      - { name: debugfs, hostPath: { path: /sys/kernel/debug } }
      - { name: btf, hostPath: { path: /sys/kernel/btf } }

几点关键:

7.4 红帽系(RHEL/CentOS)的 backport

红帽对 RHEL 有强烈的 ABI 稳定承诺,导致它会把大量新内核特性回溯(backport)到老版本号。比如 RHEL 8.6 标称 4.18,但实际 BPF 能力接近上游 5.4;RHEL 9.0 标称 5.14,能力接近上游 5.15。判断能力时不能只看 uname -r,应该直接测试某个 helper 或程序类型是否可用。

八、常用 eBPF Observability 程序

下表是生产环境最常用的 eBPF 可观测性程序清单,覆盖进程、文件、网络、存储、调度、CPU 六大方向:

程序 功能 最低内核 典型用法
execsnoop 跟踪新进程 exec 4.8 定位”谁在跑 shell”
exitsnoop 跟踪进程退出 4.9 诊断进程频繁重启
opensnoop 跟踪文件打开 4.8 找敏感文件读写者
statsnoop 跟踪 stat 调用 4.8 目录遍历风暴
biolatency 块 I/O 延迟直方图 4.9 磁盘慢
biosnoop 块 I/O 逐事件 4.9 I/O 大户
ext4slower ext4 慢操作 4.9 文件系统级延迟
xfsslower xfs 慢操作 4.9 同上
tcpconnect TCP 主动连接 4.8 出向连接审计
tcpaccept TCP 被动接受 4.8 入向连接统计
tcpretrans TCP 重传 4.8 网络质量
tcplife TCP 连接生命周期 4.15 连接时长分析
profile CPU 采样剖析 4.9 火焰图
offcputime off-CPU 时间分析 4.9 阻塞诊断
runqlat 运行队列延迟 4.9 CPU 竞争
runqlen 运行队列长度 4.9 调度压力
cpudist 进程 on-CPU 时长分布 4.9 批量长任务
funclatency 任意函数延迟 4.9 热点函数
funccount 任意函数调用次数 4.9 调用压力
hardirqs 硬中断统计 4.19 中断风暴
softirqs 软中断统计 4.19 同上
cachestat 页缓存命中率 4.9 内存压力
memleak 内存泄漏检测 4.9 长期内存问题
sslsniff SSL/TLS 明文抓取 4.18 加密流量观测
tcpstates TCP 状态转换 4.15 连接异常
netqtop 网卡队列 Top 5.0 网络队列不均
llcstat LLC 命中率 4.9 CPU 缓存分析

每一个都是一条命令就能跑起来的生产级工具。值得记忆:

sudo /usr/share/bcc/tools/execsnoop
sudo /usr/share/bcc/tools/biolatency 10 1
sudo /usr/share/bcc/tools/profile -F 99 30
sudo /usr/share/bcc/tools/tcpretrans
sudo /usr/share/bcc/tools/offcputime -df -p $(pgrep -n java) 30 > out.stacks

九、商业化 eBPF 可观测性工具

eBPF 的工程化红利已经催生了一批商业化或产品化的可观测性工具。以下几款是当前最具代表性的方案。

9.1 Pixie(New Relic)

Pixie 是 2020 年开源、后被 New Relic 收购的 Kubernetes 原生可观测性平台,定位是”无需埋点的 K8s 应用可观测”。它的架构由三部分组成:

关键能力:

Pixie 的优势是真正零改造:给 K8s 集群装一个 DaemonSet,五分钟内就能看到所有服务间的 HTTP/gRPC 调用链,包括 TLS 加密的流量。局限是保留窗口较短,复杂分析需要接入 New Relic 云。

9.2 DeepFlow(云杉网络,国产开源)

DeepFlow 是云杉网络开源的面向云原生应用的可观测性产品,2022 年 CNCF 沙箱项目,国产 eBPF 可观测性的代表。架构由代理(agent)和服务端(server)组成:

DeepFlow 的核心创新是:

中文文档完善,社区活跃,国内电信运营商、银行、云厂商的采用案例较多。对内部安全审查严格、偏好自主可控的企业,DeepFlow 是第一优先级选项。

9.3 Grafana Beyla

Beyla 是 Grafana Labs 2023 年推出的 eBPF 自动插桩工具,定位明确:“把任何 HTTP/gRPC 应用变成带 RED 指标(Rate/Errors/Duration)和分布式追踪的服务,无需改代码”。架构极简:单个二进制或容器,挂到目标进程或 K8s 集群上。

核心能力:

print_traces: true
otel_metrics_export:
  endpoint: http://otel-collector:4317
discovery:
  services:
    - name: my-service
      open_ports: "8080"

Beyla 是目前把 eBPF 零侵入和 OpenTelemetry 生态结合得最好的开源方案之一。

9.4 Parca

Parca 是 Polar Signals 开源的持续性能剖析(continuous profiling)平台,核心是一个基于 eBPF 的采样剖析器 parca-agent,以 19Hz 或 99Hz 的频率采集所有进程的调用栈,并使用 DWARF 栈回溯(DWARF-based stack unwinding)在没有帧指针(frame pointer)的情况下仍能拿到准确栈信息。

这一点非常关键:现代发行版为了代码密度,默认不保留帧指针(-fomit-frame-pointer),传统 perf 采样在这种二进制上栈回溯会断。Parca 通过解析 ELF 的 .eh_frame/.debug_frame 段,在 eBPF 里实现 DWARF 状态机,能在不改编译选项的情况下拿到正确栈。

Parca 的输出格式对齐 pprof,正在与 OpenTelemetry Profiles 信号规范融合,未来会成为”可观测性第四支柱”的标准实现之一。

十、国内落地案例

国内大型互联网公司和云厂商是 eBPF 可观测性的重度使用者:

这些案例反映了一个事实:在国内生产环境做 eBPF,内核版本兼容性是最大障碍。CentOS 7(3.10)存量巨大,大规模升级不现实,因此国内厂商普遍走两条路:一是在自研内核(如 Alibaba Cloud Linux、TencentOS Server、openEuler)里 backport eBPF 特性;二是在 agent 里做能力降级,高版本内核用 fentry + ring buffer,低版本内核退化到 kprobe + perf buffer。

十一、工程坑点

以下是 eBPF 可观测性在生产里最常踩的坑,按严重程度排序。

11.1 内核版本与 backport 不一致

uname -r 报 4.18,但能否用 fentry/ring buffer 取决于发行版是否回溯。RHEL 8/9、阿里云 Linux、TencentOS Server 都做了大量 backport。务必在 agent 启动时做能力探测(feature probe),而不是只看版本号。libbpf 提供了 libbpf_probe_bpf_helper()libbpf_probe_bpf_prog_type() 等 API。

11.2 验证器复杂度上限

验证器对程序复杂度有硬限制:

典型症状是”在开发机上过了,在生产上报 BPF program is too large“,根因通常是内联不同、LLVM 优化级别不同。

11.3 栈回溯与帧指针

上文提到,现代发行版默认关闭帧指针。eBPF 采样拿到的 ustack 在这种进程上会只有 1–2 帧。解决方案:

11.4 容器内权限问题

早期内核(< 5.8)必须 CAP_SYS_ADMIN,在多租户集群里这是高风险授权。升级到 5.13+ 后可以用细粒度 CAP_BPF + CAP_PERFMON + CAP_NET_ADMIN 替代。Kubernetes PodSecurityStandard “restricted” profile 下,eBPF agent 必须用独立命名空间或专用 namespace 运行。

11.5 Map 与内存限制

BPF 映射默认受 RLIMIT_MEMLOCK 限制(Linux 5.11 前),生产 agent 通常需要设置:

struct rlimit rlim = { .rlim_cur = RLIM_INFINITY, .rlim_max = RLIM_INFINITY };
setrlimit(RLIMIT_MEMLOCK, &rlim);

Linux 5.11 之后改为 cgroup memcg 统计,这个问题自动消失。

11.6 BTF 不可用

CentOS 7 / Ubuntu 18.04 等老内核没有内置 BTF。CO-RE 无法工作。解决方案:

11.7 热路径开销

高频函数上挂 eBPF 程序会放大开销。实测案例:

规则:尽量使用 tracepoint 和 fentry;尽量在内核态过滤;尽量用 per-CPU map 减少竞争

11.8 ring buffer 与 perf buffer 选择

11.9 符号解析与 JIT 语言

剖析 Java、Node.js、Python 等 JIT 或动态语言时,eBPF 拿到的地址无法直接解析成符号。解决方案:

11.10 与 cgroup v2 的交互

cgroup v2 下,bpf_get_current_cgroup_id() 返回的是统一 cgroup ID;v1 下每个控制器有自己的 ID,易混淆。容器内 ID 解析要用 bpf_get_current_ancestor_cgroup_id(),并用 bpffs 固定住 cgroup 引用。

十二、选型建议与落地清单

12.1 决策矩阵

场景 推荐方案 理由
生产 SRE 一次性排障 bpftrace 单行命令 启动快,语义清晰
开发调试、短期工具 bcc Python 脚本 生态丰富,教程多
agent 产品化、发布到大规模集群 libbpf + CO-RE 零依赖、可移植、启动快
Kubernetes 应用可观测 Pixie / DeepFlow / Beyla 零埋点、开箱即用
持续性能剖析 Parca / Polar Signals DWARF 栈回溯
网络深度可观测 Cilium Hubble / DeepFlow L3–L7 全栈
安全与合规观测 Tetragon / Falco LSM BPF 可靠
国内主权可控 DeepFlow + 自研 libbpf agent 可审计、可改造

12.2 生产就绪清单

在上线一个 eBPF 可观测性方案之前,建议按以下清单检查:

12.3 分阶段采纳路线

推荐的落地节奏分三阶段:

  1. 体验期(1–2 周):在预发环境装 bpftrace 和 bcc-tools,开始用 execsnoopbiolatencytcpconnectprofile 四个命令解决日常排障。这一步几乎零成本,SRE 能立刻受益。
  2. 工具化期(1–2 月):选 1–2 个开源产品试点,推荐 DeepFlow 或 Grafana Beyla。覆盖 1 个典型业务集群,对比有无 eBPF 可观测的排障效率。
  3. 自研或深度集成期(3–6 月):根据业务特点自研 libbpf + CO-RE agent,解决开源产品覆盖不到的特殊场景(如私有协议、特殊内核版本、内部 trace 上下文传播)。同时把数据平面接入统一可观测平台。

每一步都要有明确的成功指标:排障 MTTR 下降、线上 incident 识别提前时间、高价值指标覆盖率。避免一上来就 all-in 自研。

十二附、常见问答与工程补充

这一节汇总在培训、代码评审、线上事故复盘中反复被问到的 eBPF 可观测性问题,给出工程答案。

附.1 eBPF 程序真的可以做到”永不崩内核”吗

几乎可以,但不是绝对。验证器会拒绝所有它能静态证明为不安全的程序。已知的”验证器漏洞导致内核崩溃”CVE 在 Linux 5.x 早期有过几例(如 CVE-2021-3490、CVE-2022-23222),都与操作数边界计算的不完备有关。现代内核(≥ 5.15)加入了更严格的寄存器范围跟踪后基本稳定。风险点在于:

生产建议:升级到受支持的 LTS 内核,禁用 kernel.unprivileged_bpf_disabled = 1 之外的放行配置,agent 代码走静态分析与复核流程。

附.2 eBPF 能替代 perf / ftrace 吗

互补而不是替代。

日常排障里三者共存,不需要二选一。

附.3 bpftrace 脚本怎么调试

几个实用技巧:

附.4 libbpf 的 C 程序怎么做单元测试

BPF 程序很难在传统意义上”单测”,因为它运行在内核里。业界常见做法:

Cilium、Tetragon 都有这方面成熟的 CI 实践可以参考。

附.5 为什么我的 uprobe 没触发

常见原因:

  1. 目标函数被内联了(特别是 inline 或 LTO 编译的库)。
  2. 目标函数被链接器去重(--gc-sections)。
  3. 符号名被 C++ 修饰(mangled),需要用 mangled 名或 /usr/bin/mybin:_ZN...
  4. 目标二进制有 PIE,地址偏移不同,但 libbpf 已处理,不是问题。
  5. 目标库在容器内路径与挂载路径不一致。

排查工具:readelf -Ws /path/to/lib | grep symbolobjdump -d

附.6 ring buffer 丢事件怎么办

eBPF ring buffer 是单生产者多消费者(SPMC)结构,生产速度超过消费速度时会直接丢弃并返回 -E2BIG。应对策略:

附.7 BTF 不可用的降级方案

目标机器上没有 /sys/kernel/btf/vmlinux 时的选择:

附.8 eBPF 数据如何接入 OpenTelemetry

几条主流路径:

  1. agent 侧直接产出 OTLP:Beyla 是典型例子。
  2. agent 产出私有格式 → 本地 Collector 转换 → OTLP 上行:DeepFlow、Pixie 属此类。
  3. OpenTelemetry eBPF Profiler(OTel 2024 投票通过将 elastic/otel-profiling-agent 并入):第四支柱 profiles 的参考实现。

对新项目,优先选方案 1 或 3,减少数据搬运。

附.9 eBPF 对 ARM64 支持如何

主流 ARM64(aarch64)服务器(鲲鹏、飞腾、Ampere Altra、AWS Graviton)对 eBPF 支持良好。几个要点:

国产化替代场景中,ARM64 上的 eBPF 可观测性已完全可用,龙蜥、openEuler 都做了广泛适配。

附.10 eBPF 与 WebAssembly 有什么关系

两者都是”带验证器的虚拟机字节码”,但定位完全不同:

2023 年起有一些尝试把 eBPF 程序编译成 Wasm 让其在用户态运行(如 wasm-bpf),但这属于跨界实验,不要把它当成 eBPF 的替代品。对可观测性工程师而言,只关心 eBPF 本身即可。

十二又、一个完整落地示例:Java 服务的 HTTP 可观测

以一个真实场景贯穿前面所有概念,落地一个”不改 Java 代码的 HTTP 可观测”方案。

场景

方案选型

候选有三:

  1. Pixie:覆盖完整,数据留在集群。
  2. Grafana Beyla:直接产 OTLP,对接现有 Grafana Stack。
  3. 自研 libbpf agent:完全可控,但开发成本最高。

假设我们已有 Grafana/Prometheus/Tempo,选方案 2。

关键配置

DaemonSet 部署 Beyla:

apiVersion: apps/v1
kind: DaemonSet
metadata: { name: beyla, namespace: obs }
spec:
  selector: { matchLabels: { app: beyla } }
  template:
    metadata: { labels: { app: beyla } }
    spec:
      hostPID: true
      serviceAccountName: beyla
      containers:
      - name: beyla
        image: grafana/beyla:1.6.0
        securityContext:
          capabilities:
            add: ["BPF", "PERFMON", "NET_ADMIN", "SYS_PTRACE", "CHECKPOINT_RESTORE"]
          privileged: false
          runAsUser: 0
        env:
        - { name: BEYLA_CONFIG_PATH, value: /config/beyla.yaml }
        - { name: BEYLA_KUBE_METADATA_ENABLE, value: "true" }
        volumeMounts:
        - { name: config, mountPath: /config }
        - { name: sys, mountPath: /sys, readOnly: true }
      volumes:
      - { name: config, configMap: { name: beyla-config } }
      - { name: sys, hostPath: { path: /sys } }

Beyla 配置:

attributes:
  kubernetes:
    enable: true
discovery:
  services:
    - k8s_namespace: prod
      k8s_deployment_name: "^(order|payment|checkout)-.*"
otel_metrics_export:
  endpoint: http://otel-collector.obs:4317
  interval: 10s
otel_traces_export:
  endpoint: http://otel-collector.obs:4317
  sampler: { name: parentbased_traceidratio, arg: "0.1" }
routes:
  unmatched: heuristic
  patterns:
    - /api/v1/orders/:id
    - /api/v1/payments/:id/confirm

工作原理

部署后,Beyla 会:

  1. 扫描节点上所有运行的进程,匹配 K8s 标签和可执行名。
  2. 识别 Java 进程的 libc read/writetcp_sendmsg/tcp_recvmsg,挂 uprobe/kprobe。
  3. 解析 HTTP 请求行,提取 method、path、status、耗时。
  4. 按路由归类(通过 routes.patterns 归一化),生成 http_server_request_duration_seconds_{bucket,count,sum} 等 OTLP 指标。
  5. 通过 K8s API 把 pod/namespace/deployment 注入标签。
  6. 以 10s 间隔推送到 OTel Collector。

对 HTTPS 的处理

Beyla 会对 JVM 内置 SSL 通过 Java 层定位失败,因此建议 Java 使用 OpenSSL(通过 netty-tcnative 或 Wildfly OpenSSL);或退化到”仅看响应时间+状态码,不看 body”模式。

容量评估

实测数据(参考 Grafana 官方 benchmark,QPS 20k 的 Java 服务):

上线检查单

这个例子刻画了 eBPF 可观测性”从选型到落地”的完整链路。它不是最强大的方案(自研 libbpf 更可控),也不是最省事的方案(托管 SaaS 更省事),但它在能控制性、部署成本、开源生态、国内可用性上达到了一个良好平衡,是多数企业起步阶段的合理选择。

十三、参考资料

  1. Linux Kernel Documentation, “BPF Documentation”, https://www.kernel.org/doc/html/latest/bpf/
  2. Brendan Gregg, BPF Performance Tools, Addison-Wesley, 2019.
  3. Cilium Project, “BPF and XDP Reference Guide”, https://docs.cilium.io/en/stable/bpf/
  4. bpftrace Authors, “bpftrace Reference Guide”, https://github.com/bpftrace/bpftrace/blob/master/docs/reference_guide.md
  5. Andrii Nakryiko, “BPF CO-RE (Compile Once – Run Everywhere)”, https://nakryiko.com/posts/bpf-portability-and-co-re/
  6. libbpf Project, https://github.com/libbpf/libbpf
  7. libbpf-bootstrap Project, https://github.com/libbpf/libbpf-bootstrap
  8. IOVisor BCC Project, https://github.com/iovisor/bcc
  9. Pixie Documentation, https://docs.px.dev/
  10. DeepFlow Documentation, https://deepflow.io/docs/zh/
  11. Grafana Beyla Documentation, https://grafana.com/docs/beyla/latest/
  12. Parca Documentation, https://www.parca.dev/docs/
  13. Isovalent Tetragon, https://tetragon.io/
  14. Falco Security, https://falco.org/
  15. BTFHub Project, https://github.com/aquasecurity/btfhub
  16. Alexei Starovoitov, “BPF as a universally applicable engine”, LWN, 2019.
  17. Arnaldo Carvalho de Melo, “pahole and BTF deduplication”, LPC 2020.
  18. Yonghong Song, “BPF Type Format (BTF)”, kernel.org, 2018.
  19. CNCF, “eBPF Landscape”, https://landscape.cncf.io/
  20. OpenAnolis Community, “Alibaba Cloud Linux eBPF Backports”, https://openanolis.cn/

上一篇Events 与变更关联:CloudEvents、发布打点、K8s 事件

下一篇网络可观测性:Cilium Hubble、Pixie、DeepFlow、Tetragon

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-22 · architecture / observability

【可观测性工程】网络可观测性:Cilium Hubble、Pixie、DeepFlow、Tetragon

从 L3/L4/L7 三层观测视角出发,讲解 eBPF socket filter/tc/XDP 的数据采集机制,深入 Cilium Hubble 流日志与指标体系、Tetragon 安全可观测、Pixie 自动化协议解析、国产 DeepFlow 的架构与实践,以及 TLS 解密、HTTP/2 解析、服务拓扑自动发现等核心工程挑战。

2026-04-22 · architecture / observability

持续性能分析(Continuous Profiling):Parca、Pyroscope、Grafana Beyla

深入剖析持续性能分析(Continuous Profiling)的原理、架构与落地实践,覆盖 Parca、Pyroscope、Grafana Beyla 三大主流方案,包含 eBPF 采样、符号解析、火焰图、差异分析以及字节跳动、美团的生产案例与工程坑点。

2026-04-22 · architecture / observability

可观测性工程

从 Metrics、Logs、Traces 到 Profiling、eBPF、OpenTelemetry 与 SLO 治理,面向中国工程团队的可观测性系统化手册。


By .