云原生动态追踪

知识的领域是无限的,我们的学习也是无限期的。这篇文章主要讲述云原生动态追踪相关的知识,希望能为你提供帮助。
追踪面向的是请求,可以通过获取请求执行的相关数据,轻松分析出请求中的异常点,针对云原生架构下的追踪,大体可以分为针对主机的动态追踪(DynamicTracing),以及针对微服务的应用行为追踪。
动态追踪是一种高级的内核调试技术,通过探针机制,采集内核态或者用户态程序的运行信息,而不需要修改内核和应用程序的代码。这种机制性能损耗小,不会对系统运行构成任何危险。因此,它能够以非常低的成本,在短时间内获得丰富的运行信息,进而可以快速地分析、排查、发现系统运行中的问题。
要实现动态追踪,通常需要在Linux中使用相应的探测手段,甚至涉及编写并编译成内核模块,这可能会在生产系统中导致灾难性后果。经过多年的发展,尽管它们的执行已经变得更加安全了,但是编写和测试仍然很麻烦。
eBPF似乎为上述问题找到了解决的福音,eBPF通过一种软件定义的方式,提供并支持了丰富的内核探针类型,提供了强大的动态追踪能力。开发者通过编写eBPF程序,可实现相应的追踪脚本,而eBPF利用自身的实现机制,保障了在内核执行动态追踪的效率以及安全性。
eBPF正是设计和实现了一种对内核进行软件定义(SoftwareDefine Kernel)的方式。控制平面是用户空间的各种eBPF程序,实现eBPF程序在内核的加载点以及执行逻辑;数据平面则是内核各种操作的执行单元,这些加载点可以是一个系统调用,甚至是一段确定的实现代码;控制平面和数据平面通过bpf()系统调用进行通信,将用户空间的控制平面逻辑,加载到内核空间数据平面的准确位置。
eBPF程序的类型分为两个方面:追踪(Tracing)和网络(Networking)。

  • 追踪:eBPF可以通过各种类型的追踪点访问与特定程序相关的内存区域,从正在运行的进程中提取信息并执行跟踪。这样开发者就可以获取关于系统的行为及其所运行的硬件的直接信息,甚至还可以直接访问为每个特定进程分配的资源,包括文件描述符、CPU、内存等的使用情况。
  • 网络:对内核网络的操作。eBPF程序允许开发者监控并且操作计算机系统中的网络流量,这也是BPF原始设计时的核心功能点。eBPF允许对来自网络接口的数据包进行过滤,甚至可以完全拒绝这些数据包。不同类型的eBPF程序可以加载到内核网络中不同的处理阶段。
在网络数据包的处理上,eBPF通常会与Linux内核的另外一个重要功能XDP(Express Data Path)一起实现。XDP是一个安全的、可编程的、高性能的、内核集成的包处理器,它位于Linux网络数据路径中,当网卡驱动程序收到包时就会执行eBPF程序,XDP程序会在尽可能早的时间点对收到的包进行删除、修改或转发到网络堆栈等操作。XDP程序是通过bpf()系统调用控制的,使用eBPF程序实现相应的控制逻辑。
BPFTrace是eBPF的高级追踪语言。它允许开发者用简洁的领域特定语言(DSL)编写eBPF程序,并将它们保存为脚本,开发者可以执行这些脚本,而不必在内核中手动编译和加载它们。它的灵感来自其他一些著名的追踪工具,比如awk和DTrace等,一些用户甚至认为BPFTrace将会是DTrace的一个很好的替代品。
无论是DTrace、SystemTap,还是BPFTrace,其实现动态追踪都是通过探针的机制,依赖于在追踪点实现的探针,进而获取相应的追踪数据。探针是用于捕获事件数据的检测点,BPFTrace在实现内核行为追踪时使用的探针主要包括动态探针(Kprobe/Kretprobe)和静态探针(Tracepoint)两种,这些探针延续了以往常见的动态追踪工具所使用的探针设计。
  1. 动态探针:Kprobe/Kretprobe
eBPF支持的内核探针功能,允许开发者在几乎所有的内核指令中以最小的开销设置动态的标记或中断。当内核运行到某个标记的时候,就会执行附加到这个探测点上的代码,然后恢复正常的流程。对内核行为的追踪探测,可以获取内核中发生任何事件的信息,比如系统中打开的文件、正在执行的二进制文件、系统中发生的TCP连接等。
内核动态探针可以分为两种:Kprobe和Kretprobe。二者的区别在于,根据探针执行周期的不同阶段,来确定插入eBPF程序的位置。Kprobe类型的探针用于跟踪内核函数调用,是一种功能强大的探针类型,让我们可以追踪成千上万的内核函数。由于它们是用来跟踪底层内核的,开发者需要熟悉内核源代码,理解这些探针的参数、返回值的意义。
  1. 静态探针:Tracepoint
【云原生动态追踪】Tracepoint是在内核代码中所做的一种静态标记,是开发者在内核源代码中散落的一些hook,开发者可以依托这些hook实现相应的追踪代码插入。
开发者在/sys/kernel/debug/tracing/events/目录下,可以查看当前版本的内核支持的所有Tracepoint,在每一个具体Tracepoint目录下,都会有一系列对其进行配置说明的文件,比如可以通过enable中的值设置该Tracepoint探针的开关等。
与Kprobe相比,它们的主要区别在于,Tracepoint是内核开发人员已经在内核代码中提前埋好的,这也是为什么称它们为静态探针的原因。而Kprobe更多的是跟踪内核函数的进入和返回,因此将其称为动态的探针。但是内核函数会随着内核的发展而出现或者消失,因此Kprobe对内核版本有着相对较强的依赖性。

    推荐阅读