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


    嘉宾 | 刘晗   整理 | 郑远程

    出品 | CSDN云原生

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

    戳👇观看刘晗分享视频

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

    为什么需要eBPF

    eBPF可以做什么

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

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

    • 对安全进行监控、处理

    • 进行可观测性领域建设

    对于User Space:

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

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

    对于Kernel:

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

    程序和内核交互方式

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

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

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

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

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

    eBPF程序执行

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

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

    eBPF框架bcc可以做哪些事情

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

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

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

    eBPF程序开发与运行

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

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

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

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

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

    为什么选择eBPF

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

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

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

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

          SkyWalking与eBPF

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

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

    eBPF与可观测性

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

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

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

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

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

    SkyWalking实体

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

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

    eBPF Profiling

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

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

    On CPU Profiling

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

    如上图所示,从上往下看,上面是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数据之后,需要把这份数据合成为可以理解的内容。

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

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

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

    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执行状态的样子。

    功 能 演 示

    准备阶段

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

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

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

    创建demo程序

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

    确认符号表

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

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

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

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

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

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

    任务界面

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

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

    创建Profiling任务

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

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

    Profiling任务调度

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

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

    未来展望

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

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

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


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

  • 相关阅读:
    Nginx之 location 详解
    【AI大模型】驱动的未来:穿戴设备如何革新血液、皮肤检测与营养健康管理
    关于Java面试:GC机制的一些总结
    实践笔记-docker安装及配置镜像源
    Nginx安全加固
    SpringBoot项目的创建(三):手动创建一个Maven工程,然后引入SpringBoot所需的dependency来完成 (不需联网,但复杂)
    逆向工程:Python反编译工具的一点介绍(如何还原pyc文件)
    如何开发一个求职招聘小程序?详细步骤解析与教程
    预编译、函数变量提升
    设计模式 - 观察者模式
  • 原文地址:https://blog.csdn.net/m0_46700908/article/details/124953997