Kindling开源我的项目是一款基于eBPF技术的云原生可观测性我的项目。本文将次要介绍探针的具体架构设计。

Kindling探针的架构设计理念

Kindling架构设计中有一个很重要的理念:关注点拆散(Separation of Concerns)。eBPF技术或者内核模块是一种内核技术,须要的背景常识是C语言和操作系统常识。而可观测开发者关注的是要输入什么样的指标,同时因为平时应用Go、Java这一类语言较多,对C也比拟陌生,所以咱们的设计是基于两层的分层畛域。上层是eBPF的开发能力,次要是为事件透出服务;下层是可观测性需求开发,次要是为数据分析和指标产生服务,同时能够不便扩大可观测场景化需要。

另外一个重要的理念是:不反复造轮子,咱们指标是把eBPF的能力以简略的形式透出给用户应用。所以kindling的设计是以falcosecurity-libs为根底的。目前这个开源我的项目承当的主要职责就是零碎调用的事件透出,对于可观测方面的能力须要进一步扩大。然而它有一个劣势是它会将原始内核数据和cgroup信息进行关联,不便后续将数据关联到k8s相干的resources, 同时falcosecurity-libs也对原始数据做了预处理,比方将网络数据进行更丰盛的关联,让用户可能间接拿到某个对fd操作的网络事件属于哪个四元组的信息,所以咱们复用了这部分能力。但falcosecurity-libs自身并不反对kprobe、uprobe等能力,kindling目前曾经对其扩大了kprobe能力,后续也会继续一直的扩大uprobe等能力,同时还会集成其余开源工具的数据能力。
一般来说,eBPF探针次要由两局部程序组成:内核态程序用作采集数据以及用户态程序用作剖析数据。但基于以上两个理念,咱们的架构并不是传统的两局部。咱们基于关注点拆散理念,为了让cloud-native畛域的开发者可能更不便的应用eBPF的能力,把原来falcosecurity-libs的C/C++用户态程序拆分成了一个Go程序和一个C/C++程序,让用户能更关注本人善于的畛域。

Kindling探针架构介绍

Kindling探针整体蕴含三个局部:用户态Go程序、用户态C/C++程序和内核态drivers程序。用户态Go程序满足的是下层可观测需要的开发,其余两个局部实现的是内核需要的开发。这样不同畛域的人能够用本人善于的语言开发本人关注的内容,同时探针也有较好的松耦合个性。Kindling具体组件形容如下:

内核态程序:drivers

为了更好的反对低版本内核的可观测能力,kindling的探针应用内核模块的模式反对低版本内核,所以drivers又分为eBPF probe以及内核模块。drivers次要负责采集内核事件,将事件放入由间接内存映射技术创立的数据结构,供用户态程序获取并解决。在事件采集这一层后续会继续将其余开源工具集成进来,比方BCC、bpftrace。

用户态C/C++程序:kindling-probe

kindling-probe是一个由C/C++语言编写的程序,运行时以一个独自的container运行在pod中,其次要的职责是负责和内核态程序进行交互并将内核事件裸露给下层处理程序。目前负责的工作次要有三个局部:

  • 负责调用bpf API加载eBPF内核态的字节码或者装置低版本内核的内核模块
  • 负责从mmap映射进去的ringbuffer构造中读取内核产生的原始事件并对原始事件进行预处理,最初转换为规范事件格局发送给kindling-collector
  • 负责提供动静配置通道,例如能够通过配置实现内核数据过滤,缩小原始数据量以及无关数据

用户态Go程序:kindling-collector

用户态Go程序是一个可扩大模块,用户能够订阅本人关注的内核事件,基于本人的应用场景扩大本人的分析程序。目前kindling实现的分析程序被称为kindling-collector,它在运行时也是以一个独自的container运行在pod中,其主要职责是负责获取事件并进行剖析,并对数据进行label的丰盛。kindling-collector局部模块集成了opentelemetry的SDK,这样kindling的指标在输入时有较高的灵活性,能够输入到opentelemetry collector 、prometheus 、kindling标准版后端等多种可观测性平台。目前kindling-collector订阅的事件只是probe裸露进去的局部事件,次要是以零碎调用以及kprobe为主,具体订阅信息如下:
subscribe:

    - name: syscall_exit-writev      category: net    - name: syscall_exit-readv      category: net    - name: syscall_exit-write      category: net    - name: syscall_exit-read      category: net    - name: syscall_exit-sendto      category: net    - name: syscall_exit-recvfrom      category: net    - name: syscall_exit-sendmsg      category: net    - name: syscall_exit-recvmsg      category: net    - name: kprobe-tcp_close    - name: kprobe-tcp_rcv_established    - name: kprobe-tcp_drop    - name: kprobe-tcp_retransmit_skb

程序间通信形式

drivers和kindling-probe程序之间通信形式

eBPF程序采纳BPF MAP 数据结构通信;内核模块采纳mmap结构的ringbuffer进行通信。下图是eBPF程序应用的架构模型,为了兼容4.X内核,BPFMAP构造应用的是BPF_MAP_TYPE_PERF_EVENT_ARRAY,每个cpu都有相应的一个perf-event的map,在kindling-probe层会对事件进行排序组合。

kindling-probe和kindling-collector之间通信形式

这个过程应用了unix domain socket,它是一种IPC形式,尽管应用了socket,然而因为数据不须要通过网络协议栈,所以有比拟好的传输性能。目前probe裸露的kindling event会进行序列化发送给unix domain socket,collector会从socket中读取数据并进行反序列化,而后失去标准化事件进行剖析。

Kindling通信标准化事件格局

正如不同微服务间的通信大多都基于一份RESTful的HTTP接口定义,kindling在分层后也须要一种标准化的事件格局来屏蔽不同畛域的实现细节,目前kindling设计的事件格局被称为kindling-event。Kindling-event是kindling-probe程序裸露进去的内核事件,底层eBPF程序会遵循kindling-event的规范格局裸露数据,这样下层利用也能基于规范去剖析事件。具体的kindling event的构造如下:
message KindlingEvent {
Source source = 1;
// Timestamp in nanoseconds at which the event were collected.
uint64 timestamp = 2;
// Name of Kindling Event
string name = 3;
// Category of Kindling Event, enum
Category category = 4;
// Native attributes of hook point, including arguments or return value.
Property Native_attributes = 5;
// User-defined Attributions of Kindling Event, now including latency for syscall.
repeated KeyValue user_attributes = 6;
// Context includes Thread information and Fd information.
Context ctx = 7;
}

对Kindling感兴趣或者在云可观测性方面有问题的小伙伴欢送与咱们分割:

分割咱们

关注咱们