14

Tetrate刘晗:SkyWalking原生eBPF探针实战

 1 year ago
source link: https://blog.csdn.net/m0_46700908/article/details/124953997
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
219e8c90ffc610a5de5a9b7009282ecd.gif

嘉宾 | 刘晗   整理 | 郑远程

出品 | CSDN云原生

2022年5月10日,在CSDN云原生系列在线峰会第4期“Apache SkyWalking峰会”上,Tetrate工程师、Apache SkyWalking PMC成员刘晗分享了SkyWalking原生eBPF探针的应用实践。

戳👇观看刘晗分享视频

Tetrate刘晗透彻解析eBPF与可观测性的联系

77f4340b908511d370c04ac7f0e9a859.png

为什么需要eBPF

eBPF可以做什么

fb6476abb940cd2402d09c31a5aa4de2.png

从上往下看,最上面部分代表的是使用案例:

  • 用eBPF进行一些网络上的优化

  • 对安全进行监控、处理

  • 进行可观测性领域建设

对于User Space:

  • eBPF技术有很多项目,比如bcc、cilium

  • 当开发eBPF程序时,它有很多不同语言的框架,比如最常见的Golang、 C++、Rust

对于Kernel:

  • 当eBPF程序嵌入到Linux内核过程中,同时可以对用户态程序进行监控,也可以对内核的一些运行过程进行监控处理

程序和内核交互方式

4148f4b2ef5b0e053228fb39821150ea.png

举例来说,现在有两个微服务进程,a服务调用b服务,在调动过程中会涉及一些网络通信问题,在进程内部无论如何处理,它最终都会通过syscall进行调用系统函数,比如调用sendmsg发送消息,内核接收到来自业务进程的请求就会进行相关的处理,最终到达一些硬件部分,不管是读取文件还是网络或者其他操作都会涉及到内核。

eBPF程序可以把它理解为是一个基于事件驱动的程序回调。

1ac235b5482ee2bbf57b5c95f5e2aa42.png
  • 比如进行调用系统syscall时,可以嵌入eBPF程序,进行相关处理。

  • 进行网络请求时,可以嵌入eBPF程序,感知到这个程序并进行网络请求。

  • 进行自定义程序处理、网卡及其网卡涉及到的相关操作时,同样可以嵌入eBPF程序进行处理。

eBPF程序执行

比如有一个进程,它准备执行一个系统函数,此时内核就会检测到这个函数是否有eBPF程序,如果有就会先执行eBPF程序。

下面是一个简单的eBPF程序,执行完这个函数之后,才会真正调度执行内核函数。

a668b14f2c5419ca0e40c0705f5bcf07.png

eBPF框架bcc可以做哪些事情

下图展示了eBPF框架bcc可以做哪些事情。

a28e271fb84cda67c05753153d2a1db6.png

从中间往外看,中间相当于是Linux,上面有应用程序,中间有系统syscall interface,下面有内核方法,比如CPU执行会用到schedule或者内存执行时会用到的Visual Memory,再或者网络磁盘上可以进行调用的相关函数,包括最终的Device Driver驱动硬件。图中每个不同颜色的块代表不同模块。

在外层则代表对应的内核、应用程序或者硬件上,可以做出什么样的工具。最常见的是当业务程序产生内存溢出时,可以利用oomkill这个eBPF程序感知到内存溢出并输出结果到控制台中。

eBPF程序开发与运行

a4c37334f598a665063ed31e72344d59.png
  • 首先需要编写一套C代码,它由很多代码块所组成。

  • 通过Clang编译器编译成bytecode。

  • 通过go library 调用系统syscall 把程序加载到内核中,当加载到内核中时,会先验证eBPF程序合法性,比如是否有很多循环等,如果通过不了,就无法执行eBPF程序。从而保证eBPF程序在内核中运行时是安全且高效的。

当验证通过之后,通过JIT编译器,将bytecode编译成机器特定的指令集,以优化程序的执行速度。

内核程序如何与用户的程序进行程序交互呢?其实eBPF专门有一套Maps机制,可以把它理解为Map或者是Array。业务程序或者eBPF程序通过写入到eBPF Map中,双方就可以读取、修改对应的数据内容。

为什么选择eBPF

dfe0d670aabcd947fc88a3dccabb0867.png
  • 整个系统中遍布大量Event,都可以进行拦截,进行定制化的操作。

  • eBPF程序是动态加载的。动态加载理解为程序部署在服务器时,这个程序不用做任何代码改动,就可以动态加载、卸载eBPF程序。对应用程序没有任何侵入。

  • 所有的eBPF程序均在内核中执行,具有很高的执行效率。

  • eBPF有很多应用场景,包括可观测性、安全和网络等领域,并且可以从内核态和用户态两个层面对系统进行监控,以全局视角进行操作。                         

    52f761072d831dc9caafe7527352033a.png

      SkyWalking与eBPF

25f2050b661ca108399d75a1ff3c7cf1.png

从左往右看,SkyWalking可以收集很多方面的数据,比如日志、指标或者Tracing甚至Event,通过Kafka、gRPC或者HTTP接收到请求。

之后,通过Receiver集群接收到所有请求,同时再传递到真正的聚合集群,对所有数据进行聚合处理。随后进行存储,同时可以将数据导出,包括告警导出或其他指标导出。最终通过UI界面或者CLI的方式进行查询。

eBPF与可观测性

570197387560739bc1e62c36c8327069.png
  • 最常见的是指标,指标可以收集各个进程、服务的指标运行情况。其次基于网络分析,分析出多个服务进程、指标之间的关联关系,从而生成拓扑图。

  • 性能剖析分析可以对内核态和用户态的进程进行dump线程操作,从而生成火焰图,协助开发人员进行更快速的性能问题分析。

  • 对应用系统的网络进行优化,最常见场景是Service Mesh领域的Envoy,当业务程序与Envoy或者Envoy与业务程序进行交互流量时,内部会通过很多iptables规则来完成数据交互。通过eBPF程序,可以对请求进行短路优化,从而绕过iptables规则来实现性能提升。

在SkyWalking中,我们通过新建一个SkyWalking Rover子项目(一个eBPF探针),可以对当前机器、进程等信息进行采集,并且基于eBPF技术对程序进行Profiling操作。

6ea9c311e11e7321d2d3aa8d447c0e07.png

SkyWalking Rover系统需要部署在每一个Linux机器上,通过一系列的配置监控本机中的进程,并且会把进程信息上报到SkyWalking后端系统中进行相关处理,这就是SkyWalking Rover的整体架构。

SkyWalking实体

29bb253e248678250255b7e02f4bd8dd.png

SkyWalking有几个比较关键的实体概念,Service、Service Instance、Endpoint。

但SkyWalking Rover引入后,其实缺少一个概念。当前的Service Instance其实是逻辑上的概念,无法代表一个真实的进程,所以SkyWalking需要在Service Instance下面引入新的Process实体。Process可以理解为是真正的Linux进程,因此进程不是逻辑上的概念,而是物理上的概念。

eBPF Profiling

eBPF Profiling通过对内核态和用户态的执行状态分析,生成一张火焰图。

cfa676f14a7d46ff8377ee4142c4ccca.png

如上图所示,每一层就代表了栈中的每一个栈帧的名称。越往上走,说明该栈具有较高的深度。如果这个函数执行时间相对较长,那么该层的宽度越宽。基于以上两点,来帮助用户了解到哪些地方需要进行优化。

On CPU Profiling

如果某进程的CPU耗时较多,此时可以利用On CPU Profiling周期对进程和内核采样线程数据,­比如几毫秒进行一次采样,查看进程执行情况,帮助开发人员更好地分析出哪个代码行出现问题,哪个CPU消耗是比较多,从而帮助开发人员优化系统性能。

1cf89155c45727a193d376b0db1b9b11.png

如上图所示,从上往下看,上面是On CPU执行,下面是Off CPU,分别代表进程是否在CPU上执行。

当程序在CPU上执行时,先执行A方法,并且完成了一次采样,此时方法栈就是A。当A方法中调用了方法B之后再次执行一次采样,此时这个栈就是BA。

在B方法中需要进行文件读取操作,便通过调用syscall系统函数请求内核进行文件读取。因为文件读取需要与硬件交互,此时,内核就会将读取任务下发至硬件来执行,并将当前程序让出CPU。这时候,程序就会从On CPU切换至Off CPU,因为我们是On CPU Profiling,所以此时没法办法采样到数据。当进程重新回归到On CPU执行时,则可以继续进行采样工作。这就是一个最典型的On CPU Profiling的实践原理。

Profiling数据生成

当有了On CPU Profiling数据之后,需要把这份数据合成为可以理解的内容。

在内核中,数据内容依旧是以栈的形式存储的,但栈的每一个栈帧其实是个内存地址,此时就需要通过进程符号表来将地址和真正的符号进行映射。这个符号可以理解为是代码中的每一个代码行,通过将这两者结合,可以得知真正的栈的情况。

525621f4bdb48f5f07108311cf100ca0.png

SkyWalking在9.0版本之前有一个Profiling功能,但这个Profiling可以理解为Trace Profiling。

那么Trace Profiling和eBPF Profiling有什么关联性,它们有什么区别?如下表所示。

4c6696f490b25467b2805871ed629d14.png
  1. 在数据关联维度上可以理解Trace Profiling只能和单个Trace进行相互关联,而eBPF Profiling是整个Process全局的,和某个Trace没有任何关系。

  2. 目前Trace Profiling支持Java、Python这两个语言, eBPF主要支持C、C++、Golang语言。

  3. Trace Profiling只能Dump单一线程数据,而eBPF Profiling可以Dump多个线程。

  4. Trace Profiling只支持周期性的采样,比如9毫秒取样一次。而eBPF Profiling除了支持周期采样以外,同时支持基于事件进行采样。

  5. 在数据内容层面上,Trace Profiling只能支持用户进程,而eBPF Profiling可以将内核和用户进程的执行情况进行相互关联,帮助以更全局视角来分析这个程序的执行情况。

  6. eBPF Profiling还支持Service或单一Process维度的数据聚合,比如进行Profiling之后,可以把该服务下面所有服务实例的进程进行数据聚合,更全面地了解整个服务Profiling执行状态的样子。

56342f09de430447e315c1c1d0276092.png

功 能 演 示

准备阶段

c2c2d285447288a958a7d456a675c6f8.png
  • 第一部分需要有相关的应用程序,应用程序需要确保拥有符号表。

  • 第二部分需要部署一个Rover程序:配置进程寻找规则,并且启动Rover程序。

  • 第三部分需要创建一个Profiling任务,最终可以查看到火焰图。

创建demo程序

779bd365d5fa5e63b20219cbd3822f26.png

在上图中,左侧是创建了一个简单的Golang程序,这个程序会启动一个HTTP端口,当监听到HTTP请求后,会调用到sqrt函数,让程序一直保持在CPU执行;右侧是对该程序进行编译执行,发送请求之后就会让进程始终保持在CPU上执行。通过Top命令可以看到,该程序一直在CPU上执行,一直占用单个CPU的资源。

确认符号表

0017e90af3c8c41b18174bd9b6fcdb55.png

左侧这张图是在编译时,把符号表去掉了,这时候再通过objdump来查看到这个进程中的符号表时发现确实并没有任何的符号信息,此时是没法进行Profiling的,因为它无法将在栈中的内存地址转换成真正需要的信息,所以无法进行查看。

从右侧这张表可以看到,有很多符号信息,此时SkyWalking Rover可以将内存地址转换为真实的符号信息。

让Rover感知到进程的三种方式

b793284504be5019ccdb181cdd799bcb.png
  • 第一种方式是通过Command Line匹配方式,可以理解为通过ps命令来查询到进程的完整命令行,Rover则会根据配置的正则信息与命令行进行匹配。

  • 第二种方式是Agent感知,该方式需要对Agent进行一定的改造工作。当改造后的Agent与程序共同启动后,Rover可以动态感知到该进程。目前我们已经在Go2Sky项目中完成了该Agent的改造工作。

  • 第三种方式是最常见的——Kubernetes进程感知。SkyWalking Rover可以感知到所部属宿主机中所有Pod的启动和关闭,并且根据规则自动感知Pod中的进程信息。

任务界面

首先在界面中,可以看到一个eBPF Profiling控件,当点开之后,会形成右侧这个控件。

5b8eeee85f90708e7d48f9934adedc03.png

左侧是一个任务列表,展示了它所创建的Profiling任务列表,右侧是该任务的执行情况。每一个任务均与服务实体产生关联。

创建Profiling任务

6af9fbf43032c62c2f543fccdaf69b81.png

Profiling任务需要多方面的信息:首先是标签,这个标签可以理解为一个Process标签,比如可以给这个进程打很多的标签;当有了标签之后,就可以跨实例地筛选出符合标签的所有进程,从而确认进程,发送Profiling任务。

目标类型则说明如何对该进程进行Profiling,目前支持On CPU Profiling,与Trace Profiling类似,均是以指定的间隔时间来完成数据采样。

Profiling任务调度

d1a84cdc09c26976054701730b7c6258.png

左侧任务列表可以看到刚才所创建的任务,右侧蓝条代表着该Profiling任务执行过一次调度,调度的时间范围是9:51到9:56。

93b891e3d73e1e31b714390c52de9d96.png

当点击分析按钮之后,就可以看到该进程的火焰图,hello server这个方法执行比较耗时,该方法其实是刚才在代码中所看到的sqrt的所在函数。

b19f0b2c4e5f34ce691187e6dc7e13cf.png

未来展望

6c31d58a4fd5c05fb6081baecbe00a12.png

在指标层面,可以配置化地进行采集指标或者event。其次可以利用监控网络形成相关拓扑图。

在Profiling层面,目前已经有了On CPU Profiling,之后可以引入网络请求,内存分配或者磁盘写入的Profiling。比如磁盘写入Profiling可以理解为当某进程I/O占比较高的时候,可以进行网络Profiling,从而分析出是因为那一部分代码导致磁盘写入量比较大。

在网络方面,主要是针对于Service Mesh中的应用与Envoy交互,实现iptables短路优化。


聚焦云原生新技术、新实践,帮助开发者群体赢在开发范式转移的新时代。欢迎关注CSDN云原生微信公众号~  

扫这里↓↓↓加入CSDN云原生交流群

2235fab7a47450c2089d9e4ba251cae0.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK