一、云原生可观测性挑战
云原生可观测性的核心价值在于快速排障。在日常工作中,很多时候即使使用了Log、Trace和Metrics三种手段,依然无法标准化定位问题的根因,极度依赖排障人员的个人经验,所以说当前可观测领域面临的最大挑战在于节点异常根因的定位。
程序执行过程对于开发者而言是个比较复杂的黑盒子。开发者所写的代码,是运行在常用的一些依赖库之上的,而这些依赖库有运行在JVM或者Go虚拟机之上,虚拟机又运行在glibc之上,最后调用到内核提供的系统调用接口。每一层都可能有bug。日志与Trace能够部分发现用户代码层面的问题,metric可以覆盖从用户代码到系统调用库的问题,但都不具有针对性,是一种统计结果。
在黑盒情况下,我们通过技术看到的只是程序执行过程的留痕,查找问题根因时需要人为想象还原程序的执行过程,推演出可能存在的问题,再去验证。
当前也有一些白盒化的方式,比如类jstack的线程剖析能够部分发现用户代码与类库代码现场。如果发现的是排障人员熟悉的代码,则可以在一定程度上解决问题,但是很多时候恰巧发现的是依赖库或者不熟悉的代码,则需要完整理解代码执行过程,才能进行排障。
另外,如果只能看到代码层面的堆栈,当面对生产环境突发问题,某个程序正常运行后突然执行变慢或者出错,特别是多实例场景,同样的代码理应执行过程一直,那又如何解释某一个实例有问题,而其他实例没有问题。
二、老刑侦的破案经验与光学摄像头
从刑侦学的知识和运用中我们可以总结出一个道理:从学会知识到到会用知识,需要经过不断练习。因此,程序员能够通过问题的表征想象出程序的运行方式也需要积累非常多的经验。
几年前中国推广光学摄像头之后,全国各地的破案率达到历史最高水平。这正式因为光学摄像头极大降低了破案门槛,即便没有受过刑侦训练的普通人都可以通过生活常识结合光学摄像头就能够理解罪犯犯罪过程。
三、基于eBPF的光学摄像头构想
我们从光学摄像头上收到了启发,开发了程序摄像头。
程序摄像头将应用层的代码首先转换成内核的Tracingpoint和kprobe 等基础信息,再转换成操作系统的资源消耗情况,方便用户直观地理解。我们希望开发者能够通过程序摄像头,理解程序每一秒甚至每一个毫秒的执行情况。
上图左侧为 CPU 的火焰图,代表程序进程在 CPU 上消耗的时间。但在对于常规在线业务,我们认为更重要的是 OffCPU 的火焰图,它体现了程序由于何种调用堆栈引发程序暂停执行。另外,不管是OnCPU与OffCPU,仅查看进程级别的数据,尚无法很好地解决可观测性问题,因为一个进程节点对外提供多个业务接口服务,我们更希望能够按照业务接口粒度去排查问题,这就需要到达线程级别的OnCPU与OffCPU数据。
我们需要按照程序执行过程对齐OnCPU和OffCPU到线程粒度。比如线程执行程序时在 CPU 上执行了什么代码,比如由于什么原因导致其离开了 CPU 最后恢复到 CPU 上继续执行。
然后在线程粒度上将OnCPU/OffCPU与主流的 Trace技术按照时间轴做对齐,加上线程输出的日志,即可将执行过程完整地还原。
上图为通过程序摄像头观察ES执行Bulk插入的执行情况。
图中紫色段为Bulk从开始执行到结束的过程。可以发现其执行了三个并发进程:epoll线程做网络的读写,显示多为空闲状态;而另外两个线程几乎将全部的时间用于IO读写,且被读写的文件一秒钟的增长量仅 3M/s。因此可以得出结论:机器的 IO 达到了瓶颈,瓶颈为3M/s。
使用程序摄像头之前,我们一直无法确认问题根因,因为我们认为3M/s并不是瓶颈,它属于正常范围。
通常来说, tracing 与 loging 天然地能够进行关联,因其均为代码维度。而 tracing 与 metrics难以关联,因为metrics是资源维度,代表程序执行完后的结论,而且metrics维度的数据量非常庞大,一一排查太消耗时间。
而Kindling的解法为:通过eBPF将线程代码过程转换为资源消耗的过程,然后每个环节在该时间段内关联相关metric,如果是 CPU 的时间段则关联 CPU 的指标,如果是网络则关联网络的指标,能够极大地减少无关指标的干扰。
我们参考Continuous Profiling,实现了Trace Profiling。Continuous Profiling大多针对进程,但我们认为,用户在排障时更需要针对业务请求来进行,大多情况下只是其中的某些业务请求出现问题;当进程出现问题时,通过metrics很容易识别这类问题。
Trace Profiling能够将所有线程执行的情况进行记录并重放,可以将执行的某 trace 线程标记为高亮,帮助用户理解请求时每一毫秒的具体执行内容,以便确认程序根因。并且能够有针对性地关联指标,比如 CPU 密集型则关联 CPU 相关指标。
四、程序摄像头构想的预期效果
总结来看,我们期望未来程序摄像头能够解决可观测领域越来越多的问题,包括但不限于以下几种场景:
① 程序执行过程中锁占比时间较长。
② 并发过高,线程池不够用:程序摄像头能够发现请求线程池满,请求过多导致线程池不够用,进程卡住。
③ 程序由于Java GC 导致执行时间较长:程序摄像头可以很明确地坐实两个不同的线程之间的相互影响。
④ 程序自身执行了CPU 开销非常大的代码。
⑤ 程序自身文件 IO 的读写时间较长。
⑥ 网络执行慢。
关于龙蜥峰会 eBPF & Linux 稳定性专场课件获取方式:
【PPT 课件获取】:关注微信公众号(OpenAnolis),回复“龙蜥课件” 即可获取。有任何疑问请随时咨询龙蜥助手—小龙(微信:openanolis_assis)。
【视频回放】:视频回放可前往龙蜥官网https://openanolis.cn/video 查看。