1. Agent变成黑盒,不是模型的错,是链路没有眼睛
前阵子一个做AI客服产品的朋友跟我抱怨:他们的Agent在生产环境跑了一个月,突然开始批量输出奇怪的话术,用户投诉量暴涨。整个团队围着大模型转了一个星期,一会儿怀疑prompt被污染,一会儿怀疑温度参数被调错,一会儿怀疑模型版本回滚——最后谁也没找出问题。
其实这个场景这两年我见过太多了。AI Agent跑起来之后,外部看起来就是“输入一个问题,输出一个结果”,但中间发生了什么,几乎没人能说清楚。Agent调了哪个工具?工具返回了什么?上下文窗口被什么东西撑爆了?哪一轮推理开始走向偏离的?这些问题在传统的日志和监控体系里,基本上是盲区。
这不是模型的错。模型本身只负责做概率预测,真正让Agent变得不可控的,是它外围那套复杂的执行链路——LLM调用、工具编排、上下文组装、状态维护、外部API交互,每一环都有可能是问题源头,但每一环都缺乏足够的观测手段。
更要命的是,Agent的很多执行路径是动态的。它今天可能走A工具链,明天可能走B工具链,你没法在开发阶段把所有可能的分支都埋好点。传统的埋点监控在静态服务上很好用,但放到Agent这种高度动态的执行体上,就明显跟不上了。
所以就有了这个思路:既然从应用层、从外部去观测Agent就像隔着一堵墙看里面,那就把观测能力下沉到内核态,从系统层面把Agent所有的行为轨迹完整记录下来。这就是eBPF做的事——它可以在不侵入应用代码的前提下,对进程的每一次系统调用、每一次网络请求、每一次文件读写做细粒度的采集。
所谓四层监控链路,指的就是把AI Agent从底层算力消耗到顶层业务行为,拆成系统资源层、网络调用层、运行时协议层、Agent语义层四个层面,用eBPF统一采集、统一关联,让Agent的每一次“思考”和“行动”都有据可查。
这篇就完整聊聊我实践下来的方案设计、技术选型逻辑,以及落地过程中踩过的那些坑。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 为什么传统监控在Agent面前集体失灵
在讲eBPF方案之前,先得说清楚一个现实问题:我们之前用的那套监控体系,到底哪里不够用。
2.1 应用日志:Agent的输出不等于Agent的行为
传统的日志监控思路,是在代码里打点。Agent框架通常也会给自己留日志,比如打印每一次LLM请求的参数和返回结果。但实际用下来你会发现,框架日志能覆盖的只是它自己定义好的那部分流程,Agent真正干的事远不止这些。
举个例子,Agent要查询订单状态,它可能会先调一个意图识别工具,再调订单查询API,最后把结果拼装成自然语言回复。框架日志能记录“查询到了订单状态”,但它记录不了这个过程中网络层发生了什么——API超时重试了几次?TLS握手是不是慢了?目标服务的响应体是不是被截断了?这些信息分布在系统栈的不同层级,应用日志根本覆盖不到。
更要命的是,Agent的上下文是动态拼装的。很多框架只记录最终的prompt,但中间那些工具调用结果、历史对话摘要、上下文截断逻辑,日志里往往是残缺的。一旦Agent的行为开始偏离预期,你想从日志里回溯是哪一轮哪一步开始歪的,大概率只能靠猜。
2.2 APM与OpenTelemetry:埋点始终是绕不开的坎
APM(应用性能监控)和OpenTelemetry这套体系,解决的是分布式链路追踪问题,原理是在应用层生成trace_id、span_id,然后跨服务传递。这套逻辑在传统的微服务架构里很成熟,但放到Agent场景有三个别扭的地方。
第一是侵入性。OTel要求业务代码里集成SDK,或者至少用agent方式做字节码注入。但Agent框架的更新迭代极快,今天用的框架接口,下周可能就换版本了,埋点代码跟着框架变动反复调整,维护成本非常高。很多团队Agent还没跑稳,先被埋点代码折腾了个半死。
第二是上下文断裂。Agent请求第三方大模型API时,你只能观测到“发出了一个HTTP请求”,但模型内部究竟把注意力分配到了哪些tokens上、为什么会生成某个句子,这些你无从得知。也就是说,OTel能告诉你“哪一步慢”,但说不清楚“哪一步蠢”。
第三是短连接追不到。Agent经常会通过连接池复用连接,或者在极短时间内发起大量并发请求。传统的trace机制在这种高并发、短连接的场景下,丢span的概率很高,很多时候你拿到的链路图是不完整的。
2.3 基础设施监控:看得见资源,看不见行为
还有人会说,直接用Prometheus那套基础设施监控不就行了,CPU高就是算力瓶颈,内存涨就是泄漏。确实,基础设施监控能给你一个宏观的资源水位,但它给不了你行为层面的信息。
你想知道的是:Agent这次推理到底因为什么失败了?是模型返回了错误格式,还是工具调用的参数拼错了,还是外部服务的限流策略触发后重试逻辑有bug?这些问题的答案,全部不在基础设施监控的视野范围内。
所以总的问题就是:应用层观测不到行为全貌,APM层扛不住动态性和高频性,基础设施层看不到语义信息。三层各管一段,但中间全是空洞。
2.4 Agent的“系统复杂性”要求完全不同的观测思路
传统软件是人写死的逻辑,输入输出是可预期的,出问题大概率是代码bug或者资源不够。Agent不一样,它的行为是模型基于概率生成的,本身就是不确定的。这种不确定性放大到复杂的工具调用链路上,会让故障的定位维度变得非常多:
- 是模型本身的问题(幻觉、上下文丢失、指令遵循能力不足)
- 是工具层的问题(参数拼错、返回结构变化、权限不足)
- 是基础设施的问题(网络抖动、限流、资源争抢)
- 是编排逻辑的问题(循环检测失效、重试策略错误、状态错乱)
这四类问题可能同时存在,也可能互相叠加。你要想定位清楚,就必须在同一时间轴上拿到同一Agent实例的完整行为数据——系统调用、网络请求、API交互、语义轨迹,缺一样都可能误判方向。
这正是eBPF擅长的领域。
3. eBPF为何能补上这块短板
3.1 eBPF的工作原理,以及它和“摄像头”的类比
eBPF(Extended Berkeley Packet Filter)本质上是一个运行在内核态的安全沙箱环境。你可以把一段用C语言(或其他支持的语言)写好的程序,动态加载到内核里,在特定事件触发时执行,采集你关心的数据。
打个比方:传统监控是在餐厅的每个座位上装一个服务员,让每个服务员记录客人说了什么、吃了什么,这就是埋点。eBPF的做法不一样,它是在餐厅的走廊、厨房、收银台、洗手间门口装上摄像头,不需要客人配合,也不需要服务员额外工作,就能把所有人的动线记录下来。
摄像头的好处显而易见——不打扰任何人,也不依赖任何人的配合意愿。eBPF对Agent进程来说就是这样的存在:不需要改代码、不需要重启服务、不需要在Agent运行时里插入任何探针,就能观测到它的每一次系统调用、网络收发、文件读写。
3.2 安全沙箱机制决定了它的“无侵入”优势
有人可能会担心:往里执行一个程序,内核安全怎么办?eBPF的设计考虑到了这点。它在加载时会做严格的字节码校验,拒绝任何可能让系统崩溃或产生非法访问的程序。运行时有JIT编译,执行效率接近native code。同时它有专门的调试和追踪工具,比如bpftrace、bpfcli,可以把观测能力快速地插到生产系统上而不用担心稳定性。
这个“无侵入”对Agent场景价值太大了。Agent框架一周一个版本、依赖天天变、代码库动不动就改个架构,任何基于埋点的方案都要跟着框架走,维护成本极高。eBPF的观测程序是绑定在内核事件上的,内核事件可不会跟着Agent框架的版本变动而变动——你的观测能力就有了稳定性。
3.3 CO-RE与BTF:跨版本兼容的工程基础
早些年eBPF有个痛点:内核版本一变,eBPF程序可能就挂不上了,要针对不同内核重新编译。后来BPF CO-RE(Compile Once, Run Everywhere)机制解决了这个问题。配合内核的BTF(BPF Type Format)信息,eBPF程序可以在不同内核版本间移植,不再像以前那样需要逐版本适配。
这在实际部署中太重要了。Agent可能跑在Kubernetes集群里,节点内核版本参差不齐,有的是5.4,有的是5.15,有的是6.1。没有CO-RE,你就要为每个版本维护一套编译产物,运维噩梦。有了CO-RE,一套eBPF程序包打天下,部署复杂度直线下降。
3.4 为什么eBPF适合Agent这种“高速动态”场景
Agent的行为有两个突出的工程特征:高动态性和突发性。
动态性很好理解:Agent每一步做什么是模型决定的,不是代码写死的。你不可能预判它会在哪一步调用哪些系统资源,所以观测点必须覆盖所有可能的路径。eBPF可以挂载几乎所有内核函数、tracepoint、用户态函数,只要内核和用户态在跑,就能采集到,不存在“观测盲区”。
突发性是指Agent的请求模式通常是短促的、高并发的。用户一个问题进来,Agent可能在几百毫秒内发起数十个LLM调用和工具请求,然后陷入一段空闲。这种脉冲式负载,传统定期采样的监控方案很容易漏掉关键数据。eBPF是事件驱动的,理论上可以做到每事件必采集、每事件必追踪,不会因为采样周期错开而错过关键时刻。
4. 四层监控链路的设计与落地
确定了用eBPF解决Agent可观测性问题之后,接下来就是具体的设计。我在实践中把监控拆成四个层面,每个层面解决一类核心问题,各层之间用统一的数据模型关联起来。
4.1 第一层:系统资源层——先看算力有没有被“喂饱”
第一层是最基础的:Agent进程到底消耗了多少CPU、内存、磁盘、网络带宽?这层数据传统的Prometheus也能采集,但eBPF能做得更精细:可以精确到某个Agent实例的某个线程,在哪个时间窗口消耗了多少资源。
实际挂载点我主要用这几个:
- cpu scheduler tracepoint:追踪进程的调度事件,可以知道Agent在每个CPU核心上的运行时间分配
- kmalloc/kfree kprobe:追踪内存分配和释放,配合内存cgroup一起看,能还原Agent的内存水位波动
- block io tracepoint:记录磁盘IO事件,观察Agent是否有异常的磁盘读写
- netif receive/tx kprobe:记录网络设备层的收发流量,配合网络层数据一起分析
这层数据解决的核心问题是:“Agent到底是不是因为资源不足才变慢的”。很多Agent性能问题,排查到最后发现是GPU/CPU共享争抢、内存压缩导致swap剧烈、磁盘IO抖动等基础设施层面的原因。先把这一层的数据打底,后续几层的数据才有参照系。
4.2 第二层:网络调用层——LLM API的每一次往返都要记录
这一层是Agent监控的重中之重。Agent的外围动作中,最频繁、最重要的就是对外部API的调用——尤其是对LLM服务的HTTP请求。网络层的数据能直接反映Agent的“行动轨迹”。
这里我用eBPF挂载的关键位置包括:
- tcp connect kprobe:捕获每次TCP连接的发起,记录目标IP和端口,可以及时发现Agent连接了哪些外部服务
- tcp sendmsg/recvmsg ktracepoints的挂载点思路:在发送和接收消息这两个内核函数上挂探针,记录每次网络请求的数据大小、时间戳、四元组信息
- tcp close/state tracepoint:捕捉连接关闭事件,结合连接持续时间,可以分析连接池的使用效率
对LLM服务,我会单独识别目标端口和目标域名,然后在这个粒度上额外记录:请求体大小、响应体大小、首字节延迟、完整往返延迟、HTTP状态码(需要SSL解密支持,后面讲到)。有了这些,你就可以画出Agent每一次调LLM的完整时间轴——从发起到返回,每个毫秒花在哪了,一目了然。
这层数据解决的核心问题是:“Agent到底在跟谁说话,每一句话说了多久”。它能定位网络层面的所有故障:API限流、网络抖动、DNS解析超时、TLS握手缓慢。
4.3 第三层:运行时协议层——从HTTP/gRPC到消息队列
如果说第二层看到的是“包”,那么第三层要看到的是“协议语义”——Agent发出的HTTP请求,请求行和body究竟是啥?调用的gRPC方法名是什么?Redis命令具体是哪个?
这一层通常是用用户态探针,也就是uprobe方式实现。
我挂载了这么几个关键点:
- HTTP/2帧处理函数的uprobe:解析HTTP请求的method、path、headers
- gRPC消息处理函数的uprobe:捕获gRPC调用的方法名和序列化消息
- SSL_read/SSL_write的uprobe:TLS加密流量解密的关键位置,加密后的流量如果想看清明文内容,需要对SSL库函数做hook
- libc read/write的uprobe:通用IO追踪,覆盖文件读取、管道通信等
这里分享一个实践细节:TLS解密是很多团队卡住的地方。核心思路是在进程调用了SSL_write的时候,从第一个参数里取出SSL结构体,通过结构体偏移拿到读写缓冲区指针,再读到明文数据。但SSL结构体在不同版本、不同操作系统里的布局不一样,所以必须结合调试符号或运行时探测来获取偏移量。我自己的经验是,用uprobe挂SSL_write/SSL_read的函数入口,先获取到ssl结构体,再通过CO-RE的灵活性在运行时读取合适的偏移,兼容性会比硬编码偏移好很多。
有了这层数据,你就能看到Agent发出的每个HTTP请求的完整内容——喊了什么、拿到了什么、花了多久。这是故障定位最核心的依据。
4.4 第四层:Agent语义层——把内核事件翻译成“Agent的语言”
前三层数据解决的是“Agent做了什么动作”,但还没回答“Agent为什么要这么做”。把内核层面的IO事件和Agent的业务语义对齐,才是真正把四层链路打通的关键。
这一层做法比较特殊,大多时候需要借助Agent框架自身的执行日志,结合eBPF采集到的网络、协议数据,做事件对齐。我个人实现的思路是这样的:
- eBPF层把每次LLM调用、工具调用的事件上报,附带精确的内核时间戳
- Agent业务层把每一次“决策动作”也打点上报——比如“调用了订单查询工具”“把工具结果加入上下文”——也带上业务时间戳
- 通过时间对齐,把内核事件映射到Agent的业务动作上,这样就能看出:Agent在业务上“决定调用工具”之后,内核里实际发生了多少次网络请求、多少次重试
这层是很多人容易忽略的,但我觉得它恰恰是四层链路中最有价值的一层。因为你最终要给业务方一个可信的结论,而不是一堆网络包的原始数据。eBPF负责数据“抓得全”,语义层负责“看得懂”,两者合在一起才是完整链路。
5. 链路数据怎么串起来:TraceID下沉与全链路关联
四层数据都采集到了之后,还差最关键一步:把它们串成一条完整的时间线。否则每层都是孤岛,价值大打折扣。
5.1 以cgroup/pid/tgid为基准锚定Agent实例
Agent实例的边界怎么确定?我们在Kubernetes里通常用cgroup路径来锚定:同一个Pod里的Agent进程共享一个cgroup路径。eBPF事件里可以拿到pid、tgid、cgroup_id,用cgroup_id作为第一层分组键,就能把同一Agent实例的所有事件归到一起。
如果你的Agent是一个单体进程,用tgid就能区分;如果是多进程协作模式,就用cgroup_id。实践中我还是推荐用cgroup_id做基准锚定,容错性更高——即使Agent以后拆成多进程,数据归属也不会乱。
5.2 构造统一事件模型,时间戳对齐
数据关联首先要统一事件格式。我设计了一个最小事件模型,四层共用:
code复制event {
timestamp_ns uint64 // 内核时间戳,纳秒精度
cgroup_id uint64 // 实例标识
pid uint32 // 进程ID
tgid uint32 // 线程组ID
event_type uint16 // 事件类型(资源/网络/协议/语义)
trace_id [16]byte // 关联ID(如果有)
payload []byte // 事件具体内容
}
统一格式之后,用时间戳做排序,用cgroup_id做分组,你就能还原出一个Agent实例在任意时间窗口内的完整行为时间线。
5.3 trace_id的两种下沉方式
串链路时,最好能让用户态的业务上下文跟内核态的事件关联起来。这里有两种实践路径:
第一种是从用户态主动注入:Agent框架在发起一个请求时,生成trace_id,并通过某种方式传给内核态。比如可以把trace_id写入一个BPF map,键为进程的pid/tgid和连接四元组,eBPF程序在采集网络事件时,从map里查出来补充到事件里。这个方案需要业务侧做少量配合,但关联精度最高。
第二种是从内核态反推向用户态:eBPF层在采集到TCP连接事件时,生成一个基于四元组的hash作为关联键,同时通过ring buffer把事件推到用户态;用户态的收集器再跟Agent日志里的HTTP请求信息做碰撞关联。适用于不想动业务代码的场景,代价是关联精度相对低一些。
我的建议是:如果Agent框架是自研的,优先选第一种,trace_id下沉虽然要改少量代码,但换来的是整条链路都是严丝合缝的;如果用的是开源Agent框架不想改源码,那第二种也够用。
5.4 数据落库与查询设计
采集到的数据量会比较大,我建议落ClickHouse之类的高性能列式存储,按时间分区,按cgroup_id或trace_id建索引。查询场景主要是两类:按实例维度查时间线、按trace_id查单请求全链路。列式存储在这种聚合查询上的性能表现会很不错。
存储规划上有一个经验值可供参考:每个Agent实例每秒大概会产生500到2000条eBPF事件,单条事件平均100字节左右,一个中等规模的Agent服务群(20个实例)一天下来的数据量在100GB上下。存储成本要提前算好,不是所有事件都值得长期保留——基础资源层的明细数据可以只保留48小时,语义层的事件保留周期则可以长一些。
6. 实操中的五个坑
方案讲完了,说点实战中容易翻车的细节。这五个坑是我自己踩过的,写出来帮大家省点时间。
6.1 性能开销:eBPF不是零成本
eBPF虽然比埋点侵入性小,但也不是零开销。在频繁触发的事件(比如网络收发、调度切换)上,如果处理逻辑写得粗糙,采集程序本身的CPU消耗会相当可观。
我一开始对每个TCP包都做完整的事件上报,结果采集程序消耗了节点10%的CPU,业务方直接在群里开喷。后来优化了几个方向:
- 用采样替代全量:在不需要精确分析的场景,按比例采样,比如每100个事件采集1个
- 用聚合下推替代逐条上报:eBPF程序里先做map聚合,比如按秒窗口统计连接数、字节数,用户态只收聚合结果,事件量能降几个数量级
- 用ring buffer替代perf event:现代内核推荐使用BPF ring buffer,批量处理事件,减少用户态和内核态的切换开销
经过这几轮优化,整体CPU消耗控制在1%以内,效果是完全可以接受的。
6.2 TLS解密:不同运行环境下的偏移量不一致
前面说了TLS解密要用uprobe挂SSL_write/SSL_read。这里非常容易踩的坑是:同一个libssl版本,在Alpine和Ubuntu上的结构体偏移可能完全不一样。如果你硬编码了某个偏移量,换一个镜像就抓瞎。
我最后的方案是用运行时符号探测来做动态偏移解析:程序启动时,先通过调试信息或二进制的结构体布局,自动解析出SSL结构体里关键字段的偏移量,然后再加载eBPF程序。这样虽然启动流程复杂了一点,但跨环境兼容性有了保证。
另外一个注意点:Agent如果用了BoringSSL或其他TLS库,函数名不同,挂载点也要跟着换。所以这一层具体实现时,一定要先确认Agent实际链接的是哪个SSL库。
6.3 短连接高频场景:连接复用导致的关联断链
Agent框架现在普遍用HTTP连接池,一个TCP连接会被复用几十次。如果你按连接四元组去关联请求和响应,会造成严重错位——同一个连接上多个请求并发交叉,很难区分哪个响应对应哪个请求。
解决办法是:在协议层解析HTTP/2的stream ID,或者HTTP/1.1的request ID。以HTTP/2为例,每个请求有独立的stream ID,用stream ID作为请求维度的关联键,才能把并发请求正确配对。这个细节如果不做,后面的trace_id关联全是乱的。
顺带说一下,gRPC也是基于HTTP/2的,同样的逻辑适用于gRPC方法追踪。
6.4 内核版本与BTF:CO-RE也救不了老内核
CO-RE解决了编译一次处处运行的问题,但前提是内核要启用BTF支持。如果目标节点内核版本太老(比如4.9),根本拿不到BTF信息,CO-RE程序加载就会失败。
我在一个旧集群里遇到过这种问题:大部分节点内核是5.15,没问题;但有个别节点是4.9,eBPF程序直接起不来,导致监控数据断档。后来解决方案是:对老内核节点单独降级到一个不支持CO-RE、而是用kprobe事件的兜底版本。这里也分享一个部署经验:如果Agent集群的内核版本真的五花八门,建议先做一轮内核版本摸底,再决定要不要全面上eBPF方案。
6.5 事件风暴:没有流控的采集等于雪上加霜
当Agent出现异常时,往往是事件风暴最猛烈的时候——疯狂重试、疯狂连接、疯狂报错。而这恰恰是监控系统最容易扛不住的时候。如果没有流控机制,eBPF采集程序先被击穿,你连故障现场都拿不到。
我在设计里加了两道流控:eBPF程序内部的map容量限制,超过阈值直接丢弃并计数;用户态收集器的背压机制,ring buffer满时暂停读取而不是无限积压。牺牲一部分事件完整性,换采集程序的稳定存活,这样在真正的故障场景里,你至少还能拿到部分字段,不至于全盲。
7. 一次真实排障复盘:四层链路如何定位“Agent卡死”
理论说多了容易飘,拿一个真实案例完整走一遍排查链路。
7.1 故障现象
Agent集群运行了一段时间后,有用户反馈:Agent偶尔会“卡死”,表现为长时间不回复,最后超时。而且不是每次都有,大概有5%的请求会触发,没有任何规律。
7.2 第一层排查:系统资源层一切正常
拿到时间窗口,我先看系统资源层的数据——CPU正常、内存正常、磁盘IO正常,网络流量也没有突刺。这一层排除掉资源瓶颈导致的可能性。
7.3 第二层排查:网络调用层抓到异常重试
接着看网络调用层的时间线,异常现象就出来了:Agent在“卡死”前的几秒内,对某个上游工具服务发起了一连串TCP重连,每次connect之后,不到100ms就断开了,反复重试了将近20次,最后才成功拿到响应。
这个现象说明,问题不在Agent自身,而在上游工具服务的连接稳定性上。进一步分析源端和目标端的tcp close事件,发现是目标服务主动发送了RST报文。
7.4 第三层排查:协议层看到了限流响应
光知道RST还不够,不知道服务为什么拒绝。我再去协议层查,用SSL_read的uprobe解密了TLS流量的明文,还原出目标服务的HTTP响应。结果发现,目标服务在Agent连接后立刻返回了HTTP 429 Too Many Requests,并且Connection: close,连接就被服务端主动关掉了。
真相浮出水面——不是网络抖动,是Agent的请求触发了目标服务的限流策略。
7.5 第四层排查:语义层还原了Agent的误判
到了语义层,再把Agent业务日志和eBPF事件对齐,进一步发现了Agent的bug:Agent在收到429之后,把重试逻辑当成了“网络瞬断”,于是采用非常激进的快速重连策略,而不是退避等待。结果每次重试都在很短的时间内触发限流,形成一个恶性循环,最终导致请求超时。
如果只靠传统监控,你大概率会把问题定位成“上游服务不稳定”,需要很长时间才能发现Agent自身的重试策略也有问题。四层链路打通之后,从现象到根因,只用了不到半天的时间。而且因为链路是完整的,给出的结论是有底层数据支撑的,团队内部不会有争议。
8. 落地建议:不要一步到位,从单层验证开始
最后给想动手的朋友一些落地建议,避免一上来就铺大摊子,最后把自己套进去。
8.1 先从网络调用层做起,解决最痛的1个问题
不要一上来就四层全上。四层数据采集和存储的成本都不低,如果一开始就全面铺开,光是数据处理就会让团队焦头烂额。
我的建议是先做网络调用层。这个层面解决的问题最普遍——LLM API延迟、外部工具调用失败、网络抖动,这些都是Agent生产环境最常见的问题。先把这一层的数据采集稳定跑通,建立一套完整的“Agent请求时间线”能力。
8.2 验证路径:一个高频问题的完整回放
选一个你当前最头疼的高频问题,用eBPF把它的完整链路数据采集下来。比如你经常遇到“Agent答非所问”,那就把每一次LLM请求的输入输出、上下文长度、工具调用结果都记录下来,验证能否从数据链路里找到问题的模式。
如果验证顺利,说明方案可行,再逐步扩展到其他层。如果验证不顺利,就回到方案设计阶段补漏,成本远比全面铺开后再返工低得多。
8.3 工具选型参考
eBPF生态现在已经有不少成熟工具,不用什么都自己造。我实践下来觉得这几个可以作为起点:
- Pixie:Kubernetes原生的可观测性平台,可以快速抓取应用层请求和进程信息
- Cilium Hubble:偏网络层的eBPF可观测性,适合看服务间的连接和网络策略
- DeepFlow:国产的开源可观测平台,对eBPF的支持比较完整,上手门槛低
如果想深度定制,直接用libbpf或cilium/ebpf库自己写也是完全可以的,但前提是团队里有懂内核的人。没有这个人力配置,还是先站在开源工具的肩膀上更稳妥。
8.4 边跑边迭代,把eBPF当成“数字行车记录仪”
我自己的体会是,eBPF监控链路更像车上的行车记录仪,不是事故发生后才装的,而是常驻运行的。因为很多Agent的异常行为是概率性的、偶发的,你不常驻采集,等用户报障了再回头看,数据早就没了。
但常驻采集不等于所有数据都无条件保留。我的实践是:原始明细数据只保留短期(24到48小时),用于诊断近期事故;聚合数据保留中期(30天),用于趋势分析;关键事件比如语义层的重要决策,保留长期(90天以上),用于回溯Agent行为的演化。
这样既控制了存储成本,又不影响诊断能力。Agent的可观测性建设,说到底是给不确定性加一层确定性——你没法预知Agent会做哪些“意外”的事,但至少能在事后讲清楚它到底做了什么。
