【可观测性工程】内核追踪:ftrace、kprobe、uprobe、tracepoint 生产实战

💡 原文中文,约13000字,阅读约需31分钟。
📝

内容提要

本文介绍Linux内核追踪技术栈,包括ftrace、tracepoint、kprobe、uprobe和bpftrace,用于定位用户态工具无法解释的延迟问题。通过生产案例说明其价值,并详述各工具用法、生产安全模型、决策树及工程坑点,强调限时、过滤和审计等安全实践。

🔎

延伸解读

内核追踪的适用边界

文章强调,当 Metrics、Logs、Traces、Profile 都正常而用户仍报障时,内核追踪才是最后一层工具。它并非日常手段,而是针对内核调度延迟、文件系统写回、网络栈丢包等用户态工具无法覆盖的路径。因此,SRE 应首先用常规观测手段排查,仅在确认用户态无异常时才考虑下钻内核,避免过度使用。

生产安全:限时与过滤是关键

内核追踪在生产环境风险较高,文章反复强调限时、过滤和审计。例如,ftrace 的 function_graph 全量追踪会导致节点不可用,kprobe 挂热路径可能引发死锁。建议使用 bpftrace 的 -d 参数、trap cleanup 脚本、白名单 attach 点,并将追踪视为变更管理,记录审计日志。

工具选择:从 tracepoint 到 bpftrace

文章提供了清晰的决策树:优先使用 tracepoint(稳定、开销低),其次 kprobe(动态但风险高),uprobe 用于用户态库函数,bpftrace 适合快速探索,perf 用于正式火焰图,持续剖析则用 Parca/Pixie。同时提醒,strace 因 ptrace 开销大,不适合生产环境。

常见坑点与规避

文章列举了多个工程坑点:function_graph 无过滤导致节点不可用、kprobe 挂不可重入函数可能死锁、容器内 uprobe 路径错误、WSL debugfs 权限受限、BTF 缺失导致 bpftrace 无法解析结构体、追踪忘记关闭等。这些坑点提醒读者在实施内核追踪时需谨慎,并遵循文中给出的规避方法。

Q&A

什么是内核追踪?它和用户态可观测性工具有什么区别?

内核追踪是用于观测Linux内核内部行为的工具,如ftrace、tracepoint、kprobe、uprobe和bpftrace。用户态工具(如Metrics、Logs、Traces、Profile)通常只能看到应用层,而内核调度延迟、文件系统写回、网络栈丢包等问题只能在内核层面捕获。当用户态工具显示正常但用户仍报障时,内核追踪是最后一层手段。

ftrace的function_graph tracer如何帮助定位内核函数调用耗时?

function_graph tracer可以追踪指定内核函数的调用链,并显示每个子函数的耗时。例如,通过设置set_graph_function为ext4_file_write_iter,可以查看write()路径中各个函数的耗时,从而定位瓶颈。使用时需先关闭tracing_on,设置current_tracer为function_graph,然后开启追踪,最后读取trace文件。

tracepoint和kprobe有什么区别?生产环境为什么优先使用tracepoint?

tracepoint是内核提供的稳定API,字段布局跨版本稳定,语义由子系统作者定义,开销低;kprobe是动态探针,可以挂载任意内核函数,但函数名可能变化,需要读源码,开销较高。生产环境优先使用tracepoint,因为它更稳定、开销更低,而kprobe仅用于低频函数且需限时。

如何使用bpftrace追踪文件系统fsync延迟?

可以使用bpftrace挂载kprobe:vfs_fsync_range和kretprobe:vfs_fsync_range,记录开始时间并计算耗时直方图。示例命令:sudo bpftrace -e 'kprobe:vfs_fsync_range { @start[tid] = nsecs; } kretprobe:vfs_fsync_range /@start[tid]/ { @fsync_us = hist((nsecs - @start[tid]) / 1000); }' -d 30。注意使用-d参数限制运行时间。

在生产环境中使用内核追踪有哪些安全注意事项?

生产环境使用内核追踪需注意:1) 限时运行,使用timeout、bpftrace -d或trap cleanup脚本;2) 白名单attach点,平台团队维护允许的tracepoint/kprobe列表;3) 监控ring buffer溢出,丢失事件时需减小输出或加强过滤;4) 将内核追踪视为变更,需要工单、值守和回滚计划;5) 记录审计日志,包括谁在哪个节点运行了什么脚本。

uprobe在容器环境中有什么坑?如何解决?

uprobe需要宿主机上二进制的绝对路径,容器内路径可能无法直接使用。例如,需要解析overlay2合并层路径,如/var/lib/docker/overlay2/<id>/merged/usr/bin/app。此外,Go 1.17+使用寄存器传参,bpftrace默认arg0读取可能失效,需查文档或使用USDT。

如何选择内核追踪工具?有没有决策树?

选择工具可参考决策树:若需知道发生了哪些内核事件,用tracepoint;若需分析某内核函数耗时,用kprobe+hist;若需追踪用户态库函数,用uprobe/USDT;若需快速探索,用bpftrace;若需正式火焰图,用perf;若需持续剖析,用Parca/Pixie。

内核追踪和strace相比有什么优势?

strace使用ptrace,开销数量级更高,仅适合短窗口单进程,不适合生产环境。内核追踪(如ftrace、bpftrace)开销更低,可以追踪内核内部路径,且支持限时和过滤,更适合生产排障。

🏷️

标签

➡️

继续阅读