作者:炎寻

曩昔一年,ARMS 根据 eBPF 技能打造了 Kubernetes 监控,供给多言语无侵入的运用功用,体系功用,网络功用观测才能,并发布 Kubernetes 问题排查全景图,验证了 eBPF 技能的有效性。eBPF 技能和生态开展很好,未来远景广大,作为该技能的实践者,本文方针是经过答复 7 个中心问题介绍 eBPF 技能自身,为咱们解开 eBPF 的面纱。

重视【阿里云云原生】公众号,后台回复关键词【K8s全景图】获取全景图高清下载地址!

深入浅出 eBPF|你要了解的 7 个核心问题

eBPF 是什么

eBPF 是一个能够在内核运转沙箱程序的技能,供给了一种在内核事情和用户程序事情发生时安全注入代码的机制,使得非内核开发人员也能够对内核进行操控。跟着内核的开展,eBPF 逐步从开始的数据包过滤扩展到了网络、内核、安全、盯梢等,而且它的功用特性还在快速开展中,前期的 BPF 被称为经典 BPF,简称 cBPF,正是这种功用扩展,使得现在的 BPF 被称为扩展 BPF,简称 eBPF。

eBPF 的运用场景是什么?

网络优化

eBPF 兼具高功用和高可扩展特性,使得其成为网络计划中网络包处理的优选计划:

  • 高功用

JIT 编译器供给近乎内核本地代码的履行功率。

  • 高可扩展

在内核的上下文里,能够快速地增加协议解析和路由战略。

故障确诊

eBPF 经过 kprobe,tracepoints 盯梢机制兼具内核和用户的盯梢才能,这种端到端的盯梢才能能够快速进行故障确诊,与此同时 eBPF 支撑以愈加高效的方式透出 profiling 的统计数据,而不需求像传统体系需求将大量的采样数据透出,使得持续地实时 profiling 成为可能。

深入浅出 eBPF|你要了解的 7 个核心问题

安全操控

eBPF 能够看到一切体系调用,一切网络数据包和 socket 网络操作,一体化结合进程上下文盯梢,网络操作级别过滤,体系调用过滤,能够更好地供给安全操控。

功用监控

比较于传统的体系监控组件比方 sar,只能供给静态的 counters 和 gauges,eBPF 支撑可编程地动态搜集和边缘核算聚合自界说的目标和事情,极大地提升了功用监控的功率和想象空间。

eBPF 为什么会出现?

eBPF 的出现本质上是为了解决内核迭代速度慢和体系需求快速变化的矛盾,在 eBPF 领域常用的一个例子是 eBPF 相关于 Linux Kernel 相似于 Javascript 相关于 HTML,突出的是可编程性。一般来说可编程性的支撑一般会带来一些新的问题,比方内核模块其实也是为了解决这个问题,可是他没有供给很好的鸿沟,导致内核模块会影响内核自身的稳定性,在不同的内核版别需求做适配等。eBPF 选用以下战略,使得其成为一种安全高效地内核可编程技能:

  • 安全

eBPF 程序必须被验证器校验经过后才干履行,且不能包括无法抵达的指令;eBPF 程序不能随意调用内核函数,只能调用在 API 中界说的辅佐函数;eBPF 程序栈空间最多只要 512 字节,想要更大的存储,就必需求凭借映射存储。

  • 高效

凭借即时编译器(JIT),且因为 eBPF 指令依然运转在内核中,无需向用户态复制数据,大大提高了事情处理的功率。

  • 标准

经过 BPF Helpers,BTF,PERF MAP 供给标准的接口和数据模型供开发者运用。

  • 功用强大

eBPF 不只扩展了寄存器的数量,引进了全新的 BPF 映射存储,还在 4.x 内核中将本来单一的数据包过滤事情逐步扩展到了内核态函数、用户态函数、盯梢点、功用事情(perf_events)以及安全操控等领域。

eBPF 怎样用?

深入浅出 eBPF|你要了解的 7 个核心问题

5 个过程

1、运用 C 言语开发一个 eBPF 程序;

即插桩点触发事情时要调用的 eBPF 沙箱程序,该程序会在内核态运转。

2、凭借 LLVM 把 eBPF 程序编译成 BPF 字节码;

eBPF 程序编译成 BPF 字节码,用于后续在 eBPF 虚拟机内验证并运转。

3、经过 bpf 体系调用,把 BPF 字节码提交给内核;

在用户态经过 bpf 体系,将 BPF 字节码加载到内核。

4、内核验证并运转 BPF 字节码,并把相应的状况保存到 BPF 映射中;

内核验证 BPF 字节码安全,而且确保对应事情发生时调用正确的 eBPF 程序,假如有状况需求保存,则写入对应 BPF 映射中,比方监控数据就能够写到 BPF 映射中。

5、用户程序经过 BPF 映射查询 BPF 字节码的运转状况。

用户态经过查询 BPF 映射的内容,获取字节码运转的状况,比方获取抓取到的监控数据。

一个完整的 eBPF 程序,一般包括用户态和内核态两部分:用户态程序需求经过 BPF 体系调用跟内核进行交互,从而完结 eBPF 程序加载、事情挂载以及映射创立和更新等使命;而在内核态中,eBPF 程序也不能恣意调用内核函数,而是需求经过 BPF 辅佐函数完结所需的使命。尤其是在拜访内存地址的时候,必需求凭借 bpf_probe_read 系列函数读取内存数据,以确保内存的安全和高效拜访。在 eBPF 程序需求大块存储时,咱们还需求根据运用场景,引进特定类型的 BPF 映射,并凭借它向用户空间的程序供给运转状况的数据。

eBPF 程序分类和运用场景

bpftool feature probe | grep program_type

以上指令能够检查体系支撑的 eBPF 程序类型,一般有如下类型:

eBPF program_type socket_filter is available
eBPF program_type kprobe is available
eBPF program_type sched_cls is available
eBPF program_type sched_act is available
eBPF program_type tracepoint is available
eBPF program_type xdp is available
eBPF program_type perf_event is available
eBPF program_type cgroup_skb is available
eBPF program_type cgroup_sock is available
eBPF program_type lwt_in is available
eBPF program_type lwt_out is available
eBPF program_type lwt_xmit is available
eBPF program_type sock_ops is available
eBPF program_type sk_skb is available
eBPF program_type cgroup_device is available
eBPF program_type sk_msg is available
eBPF program_type raw_tracepoint is available
eBPF program_type cgroup_sock_addr is available
eBPF program_type lwt_seg6local is available
eBPF program_type lirc_mode2 is NOT available
eBPF program_type sk_reuseport is available
eBPF program_type flow_dissector is available
eBPF program_type cgroup_sysctl is available
eBPF program_type raw_tracepoint_writable is available
eBPF program_type cgroup_sockopt is available
eBPF program_type tracing is available
eBPF program_type struct_ops is available
eBPF program_type ext is available
eBPF program_type lsm is available

详细可参阅:

elixir.bootlin.com/linux/v5.13…

首要是分为 3 大运用场景:

  • 盯梢

tracepoint, kprobe, perf_event 等,首要用于从体系中提取盯梢信息,从而为监控、排错、功用优化等供给数据支撑。

  • 网络

xdp, sock_ops, cgroup_sock_addr , sk_msg 等,首要用于对网络数据包进行过滤和处理,从而完结网络的观测、过滤、流量操控以及功用优化等各种丰富的功用,这里能够丢包,重定向。

深入浅出 eBPF|你要了解的 7 个核心问题

cilium 根本用了一切的 hook 点。

  • 安全和其他

lsm,用于安全,其他还有 flow_dissector, lwt_in 都是一些不怎样常用的,不再赘述。

eBPF 的最佳实践是什么?

寻觅内核的插桩点

从前面能够看出来 eBPF 程序自身并不困难,困难的是为其寻觅合适的事情源来触发运转。关于监控和确诊领域来说,盯梢类 eBPF 程序的事情源包括 3 类:内核函数(kprobe)、内核盯梢点(tracepoint)或功用事情(perf_event)。此刻有 2 个问题需求答复:

1、内核中都有哪些内核函数、内核盯梢点或功用事情?

  • 运用调试信息获取内核函数、内核盯梢点
sudo ls /sys/kernel/debug/tracing/events
  • 运用 bpftrace 获取内核函数、内核盯梢点
# 查询一切内核插桩和盯梢点
sudo bpftrace -l
# 运用通配符查询一切的体系调用盯梢点
sudo bpftrace -l 'tracepoint:syscalls:*'
# 运用通配符查询一切姓名包括"open"的盯梢点
sudo bpftrace -l '*open*'
  • 运用 perf list 获取功用事情
sudo perf list tracepoint

2、关于内核函数和内核盯梢点,在需求盯梢它们的传入参数和返回值的时候,又该如何查询这些数据结构的界说格局呢?

  • 运用调试信息获取
sudo cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_openat/format

运用 bpftrace 获取

sudo bpftrace -lv tracepoint:syscalls:sys_enter_openat

详细如何运用以上信息,请参阅bcc。

寻觅运用的插桩点

1、如何查询用户进程的盯梢点?

  • 静态编译言语经过-g 编译选项保存调试信息,运用程序二进制会包括 DWARF(Debugging With Attributed Record Format),有了调试信息,能够经过 readelf、objdump、nm等东西,查询可用于盯梢的函数、变量等符号列表
# 查询符号表
readelf -Ws /usr/lib/x86_64-linux-gnu/libc.so.6
# 查询USDT信息
readelf -n /usr/lib/x86_64-linux-gnu/libc.so.6
  • 运用 bpftrace
# 查询uprobe
bpftrace -l 'uprobe:/usr/lib/x86_64-linux-gnu/libc.so.6:*'
# 查询USDT
bpftrace -l 'usdt:/usr/lib/x86_64-linux-gnu/libc.so.6:*'

uprobe 是根据文件的。当文件中的某个函数被盯梢时,除非对进程 PID 进行了过滤,默认一切运用到这个文件的进程都会被插桩。

上面说的是静态编译言语,他和内核的盯梢相似,运用程序的符号信息能够存放在 ELF 二进制文件中,也能够以单独文件的方式,放到调试文件中;而内核的符号信息除了能够存放到内核二进制文件中之外,还会以 /proc/kallsyms 和 /sys/kernel/debug 等方式暴露到用户空间。

关于非静态编译言语来说,首要是两种:

1、解说型言语

运用相似编译型言语运用程序的盯梢点查询办法,查询它们在解说器层面的 uprobe 和 USDT 盯梢点,如何将解说器层面的行为和运用行为相关需求相关言语的专家来剖析。

2、即时编译型言语

这类言语的运用源代码会先编译为字节码,再由即时编译器(JIT)编译为机器码履行,还会有大量的优化,盯梢难度很大,同解说型编程言语相似,uprobe 和 USDT 盯梢只能用在即时编译器上,从即时编译器的盯梢点参数里面获取最终运用程序的函数信息。找出即时编译器的盯梢点同运用程序运转之间的关系需求相关言语的专家来剖析。

能够参阅 BCC 的运用程序盯梢,用户进程的盯梢,本质上是经过断点去履行 uprobe 处理程序。尽管内核社区已经对 BPF 做了很多的功用调优,盯梢用户态函数(特别是锁争用、内存分配之类的高频函数)仍是有可能带来很大的功用开销。因而,咱们在运用 uprobe 时,应该尽量避免盯梢高频函数。

详细如何运用以上信息,请参阅:

github.com/iovisor/bcc…****

相关问题与插桩点

一个理想的状况是一切问题都清楚应当观察那些插桩点,可是这个要求技能人员对端到端的软件栈细节都了解十分透彻,一个愈加合理的办法是二八规律,将软件栈数据流的最中心的 80%头绪捉住,确保出现问题必定会在这个头绪被发现即可。此刻再运用内核栈和用户栈来检查详细的调用栈即可发现中心问题,比方说发现了网络在丢包,可是不知道为什么丢,此刻咱们知道网络丢包必定会调用 kfree_skb 内核函数,那么咱们能够经过:

sudo bpftrace -e 'kprobe:kfree_skb /comm=="<your comm>"/ {printf("kstack: %s\n", kstack);}'

发现该函数的调用栈:

kstack: kfree_skb+1 udpv6_destroy_sock+66 sk_common_release+34 udp_lib_close+9 inet_release+75 inet6_release+49 __sock_release+66 sock_close+21 __fput+159 ____fput+14 task_work_run+103 exit_to_user_mode_loop+411 exit_to_user_mode_prepare+187 syscall_exit_to_user_mode+23 do_syscall_64+110 entry_SYSCALL_64_after_hwframe+68

那么就能够回溯上面的函数,看看他们详细是哪一行在什么条件下调用的,就能够定位到问题。这个办法不只能够定位问题,也能够用于加深对内核调用的了解,比方:

bpftrace -e 'tracepoint:net:* { printf("%s(%d): %s %s\n", comm, pid, probe, kstack()); }'

能够检查一切网络相关的盯梢点及其调用栈。

eBPF 的完结原理是什么?

深入浅出 eBPF|你要了解的 7 个核心问题

5 个模块

eBPF 在内核首要由 5 个模块协作:

1、BPF Verifier(验证器)

确保 eBPF 程序的安全。验证器会将待履行的指令创立为一个有向无环图(DAG),确保程序中不包括不可达指令;接着再模仿指令的履行过程,确保不会履行无效指令,这里经过和单个同学了解到,这里的验证器并无法确保 100%的安全,所以关于一切 BPF 程序,都还需求严格的监控和评定。

2、BPF JIT

将 eBPF 字节码编译成本地机器指令,以便更高效地在内核中履行。

3、多个 64 位寄存器、一个程序计数器和一个 512 字节的栈组成的存储模块

用于操控 eBPF 程序的运转,保存栈数据,入参与出参。

4、BPF Helpers(辅佐函数)

供给了一系列用于 eBPF 程序与内核其他模块进行交互的函数。这些函数并不是恣意一个 eBPF 程序都能够调用的,详细可用的函数集由 BPF 程序类型决议。注意,eBPF 里面一切对入参,出参的修正都必须契合 BPF 标准,除了本地变量的变更,其他变化都应当运用 BPF Helpers 完结,假如 BPF Helpers 不支撑,则无法修正。

bpftool feature probe

经过以上指令能够看到不同类型的 eBPF 程序能够运转哪些 BPF Helpers。

5、BPF Map & context

用于供给大块的存储,这些存储可被用户空间程序用来进行拜访,从而操控 eBPF 程序的运转状况。

bpftool feature probe | grep map_type

经过以上指令能够看到体系支撑哪些类型的 map。

3 个动作

先说下重要的体系调用 bpf:

int bpf(int cmd, union bpf_attr *attr, unsigned int size);

这里 cmd 是关键,attr 是 cmd 的参数,size 是参数巨细,所以关键是看 cmd 有哪些:

// 5.11内核
enum bpf_cmd {
BPF_MAP_CREATE,  
BPF_MAP_LOOKUP_ELEM,  
BPF_MAP_UPDATE_ELEM,  
BPF_MAP_DELETE_ELEM, 
BPF_MAP_GET_NEXT_KEY, 
BPF_PROG_LOAD,
BPF_OBJ_PIN,
BPF_OBJ_GET,  
BPF_PROG_ATTACH, 
BPF_PROG_DETACH,  
BPF_PROG_TEST_RUN,
BPF_PROG_GET_NEXT_ID,  
BPF_MAP_GET_NEXT_ID, 
BPF_PROG_GET_FD_BY_ID, 
BPF_MAP_GET_FD_BY_ID,
BPF_OBJ_GET_INFO_BY_FD, 
BPF_PROG_QUERY, 
BPF_RAW_TRACEPOINT_OPEN, 
BPF_BTF_LOAD, 
BPF_BTF_GET_FD_BY_ID, 
BPF_TASK_FD_QUERY, 
BPF_MAP_LOOKUP_AND_DELETE_ELEM, 
BPF_MAP_FREEZE, 
BPF_BTF_GET_NEXT_ID, 
BPF_MAP_LOOKUP_BATCH, 
BPF_MAP_LOOKUP_AND_DELETE_BATCH, 
BPF_MAP_UPDATE_BATCH,  
BPF_MAP_DELETE_BATCH,  
BPF_LINK_CREATE,
BPF_LINK_UPDATE, 
BPF_LINK_GET_FD_BY_ID,
BPF_LINK_GET_NEXT_ID, 
BPF_ENABLE_STATS, 
BPF_ITER_CREATE,
BPF_LINK_DETACH,
BPF_PROG_BIND_MAP,
};

最中心的便是 PROG,MAP 相关的 cmd,便是程序加载和映射处理。

1、程序加载

调用 BPF_PROG_LOAD cmd,会将 BPF 程序加载到内核,但 eBPF 程序并不像惯例的线程那样,发动后就一向运转在那里,它需求事情触发后才会履行。这些事情包括体系调用、内核盯梢点、内核函数和用户态函数的调用退出、网络事情,等等,所以需求第 2 个动作。

2、绑定事情

b.attach_kprobe(event="xxx", fn_name="yyy")

以上便是将特定的事情绑定到特定的 BPF 函数,实际完结原理如下:

(1)凭借 bpf 体系调用,加载 BPF 程序之后,会记住返回的文件描述符;

(2)经过 attach 操作知道对应函数类型的事情编号;

(3)根据 attach 的返回值调用 perf_event_open 创立功用监控事情;

(4)经过 ioctl 的 PERF_EVENT_IOC_SET_BPF 指令,将 BPF 程序绑定到功用监控事情。

3、映射操作

经过 MAP 相关的 cmd,操控 MAP 增删,然后用户态根据该 MAP 与内核状况进行交互。

eBPF 的开展现状?

内核支撑

建议>=4.14

生态

eBPF 的生态自下而上的状况如下:

1、基础设施

支撑 eBPF 基础才能的开展。

  • Linux Kernal
  • LLVM\

2、开发东西集

首要是用于加载,编译,调试 eBPF 程序,不同言语有不同的开发东西集:

  • Go

  • github.com/cilium/ebpf

  • github.com/aquasecurit…

  • C/C++

  • github.com/libbpf/libb…

3、eBPF 运用

  • bcc

    github.com/iovisor/bcc

供给一套开发东西和脚本。

  • bpftrace

    github.com/iovisor/bpf…

根据 bcc,供给一个脚本言语。

  • cilium

    github.com/cilium/cili…

网络优化和安全

  • Falco

    github.com/falcosecuri…

网络安全

  • Katran github.com/facebookinc…

高功用 4 层负载均衡

  • Hubble

    github.com/cilium/hubb…

可观测

  • Kindling

    github.com/CloudDectec…

可观测

  • Pixie

    github.com/pixie-io/pi…

可观测

  • kubectl trace

    github.com/iovisor/kub…

调度 bpftrace 脚本

  • L3AF

    github.com/l3af-projec…

分布式环境下发动和办理 eBPF 程序的平台

  • ply

    github.com/iovisor/ply

动态 linux trace

  • Tracee

    github.com/aquasecurit…

Linux 运转时安全监测

4、盯梢生态的网站

  • ebpf.io/projects

  • github.com/zoidbergwil…

写在最后

用好 eBPF 的条件是对软件栈的了解

经过上面的介绍,相信咱们对 eBPF 已经有了满足的了解,eBPF 供给的只是一个框架和机制,中心仍是需求用 eBPF 的人对软件栈的了解,找到合适的插桩点,能够和运用问题进行相关。

eBPF 的杀手锏是全掩盖,无侵入,可编程

1、全掩盖

内核,运用程序插桩点全掩盖。

2、无侵入

不需求修正任何被 hook 的代码。

3、可编程

动态下发 eBPF 程序,边缘动态履行指令,动态聚合剖析。

团队信息

阿里云可观测团队,掩盖前端监控、运用监控、容器监控、Prometheus、链路追寻、智能告警、运维可视化等多个技能领域及产品,沉淀阿里云可观测在不同行业、不同技能场景的可观测解决计划与最佳实践。

阿里云 Kubernetes 监控是一套根据 eBPF 技能,针对 Kubernetes 集群开发的一站式无侵入式可观测性产品,根据 Kubernetes 集群下的目标、运用链路、日志和事情,旨在为 IT 开发运维人员供给全体的可观测性计划。

介绍:

help.aliyun.com/document_de…

接入:

help.aliyun.com/document_de…