深入浅出运维可观测工具(四):如何使用eBPF绘制网络拓扑图

本文主要是介绍深入浅出运维可观测工具(四):如何使用eBPF绘制网络拓扑图,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

哈喽~又到了我们技术分享环节了。eBPF这个系列自分享以来收到了很多朋友的喜欢,真是让博主又惊又喜,感谢大家的支持。话不多说,今天我们将对如何使用eBPF绘制网络拓扑图做一篇分享,文章较长,干货较多,大家可以一键三连先码后看~

新来的朋友点这里,一键回看前期精彩技术干货。

深入浅出运维可观测工具(三):eBPF如何兼容多架构模式性能管理icon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/132697151

擎创技术流 | 深入浅出运维可观测工具(二):eBPF应用中常见问题icon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/131919692

擎创技术流 | 深入浅出运维可观测工具(一):聊聊eBPF的前世今生icon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/131901910

一、eBPF出现的背景

1.传统架构下排查问题过于依赖技术人员的水平

在传统架构下,应用程序通过发布工具部署到虚拟机环境中。在这种模式下,对于网络性能的监控和观测主要依赖一些常规的技术,比如专门的网络监控工具如sniffer,还有一些网络抓包工具比如Wireshark、tcpdump(bpf技术)等,这些工具可以捕获和分析网络流量,检测网络延迟、丢包等性能问题。

比如对应用服务请求响应超时,无法确认是网络问题还是应用程序自身问题,需要指定一段时间并对应用监听的端口号进行抓包,然后把抓到的数据包逐个分析并找到对应的请求。但是整个过程却非常繁琐费时,十分依赖排障人员的技术能力。

2.云原生的应用导致应用间的调用关系更加复杂

随着云原生架构和容器技术的火热,应用程序上云并且作为微服务容器化部署,各种应用中间件实时动态扩缩容,各种复杂的调用关系,比如Pod之间相互调用、Pod通过service请求应用、pod访问kubernetes外的服务、云外通过ingress访问云内应用等等,都给网络排查带来了巨大的困难,完全无法通过原先传统架构下指定ip和port的抓包方式进行排查。

3.部署方式发生重大变化

由于部署方式发生了较大变化,由原先的单一进程(process),变为了容器(container)、容器组(pod)、工作负载(deployment)、服务(service)等等,网络监控需求也由简单的网络接口抓包,衍变为需要近实时看到容器、容器组、工作负载各个维度的tcp、udp流量、http调用访问、网络拓扑关系等。

4.eBPF灵活强大的优势

eBPF 技术的出现为网络性能管理带来了更强大、更灵活、更有效的工具和方法。它能够实时监测和分析网络流量,提供精细的流量控制和优化能力,并提高网络安全性和故障排查能力,对于云原生复杂的网络环境,ebpf允许用户在内核中编写和执行自定义的代码逻辑,以实时监测和分析网络流量。通过eBPF技术能够捕获每一个进程的网络连接信息以及对应的tcp延时、波动、连接数、重传数等指标,进而可以由进程关联到所属的容器,通过每一个容器可以关联到所在的容器组以及每一个工作负载的网络连接

二、如何使用eBPF绘制网络拓扑图

1.了解tcp发送过程

Linux是一个强大的操作系统,提供了丰富的系统调用接口。我们发送网络数据的时候通过调用系统调用发送网络数据。

要绘制网络拓扑图,我们首先要了解linux处理请求过程:

通过上面的流程我们发现一个RPC最基本的调用流程大致如下:

1)客户端建立连接(Establishing Connection)
  • 客户端向服务端调用connect函数建立连接。

  • 服务端使用accept接收连接。

2)数据传输(Data Transfer)

已建立连接的两端可以可以通过调用send、recv系列函数进行数据的传输。

3)断开连接(Terminating Connection)

当数据传输完毕或不再需要连接时,一方可以通过调用close发送一个FIN(结束)报文段。接收到FIN报文段的一方再次调用close发送一个ACK报文段作为确认。这个过程称为四次挥手,确保连接的安全关闭。

2.总结linux用于网络传输

1)使用的系统调用

我们通过分析<< unix网络编程第一卷 >> 找出网络传输用到的系统调用

基本套接字编程所用到的系统调用

这些函数统一需要传入fd参数,例如send函数,它的系统调用需要入参如下:

size_t send(int sockfd, const void *buf, size_t len, int flags);

我们需要使用eBPF,hook这些函数 找到对应的linux进程下sockfd 所对应的tcp五元组,然后进行汇总统计来绘制网络拓扑图。

3.对监控网络场景进行分析

1)擎创采集器开启后建立的TCP连接

擎创采集器启动后。服务程序通过调用 listen 和 accept 函数,接收客户端的连接请求,并与客户端建立通信。

而对于客户端而言,通过调用 connect 函数来建立和服务程序的连接。这些连接和之后的数据传输过程使用的系统调用,通过使用基于eBPF的技术,都可以eBPF嵌入字节码程序并进行分析。

2)擎创采集器开启前建立的TCP连接

由于调用方和服务器的tcp 连接过程早已创建完成。并不会再触发connect 和 accept的系统调用,只是在数据传输中调用send、recv或者write、read系列的函数。

如果应用程序之间数据传输使用的是write和read,并没有调用sockfd_lookup_light,所以暂时无法通过fd找到对应的sock 五元组。

但是如果应用程序之间的传输使用的是send和recv等系统调用,kernel 在内部会调sockfd_lookup_light,我们可以通过 pid 和fd的组合翻译出对应的sock五元组。

由于connect和accept在采集器启动之前已经建立连接,所以我们无法通过eBPF确认出流量的出入口方向。

例如go的一些应用程序:

https://pkg.go.dev/net@go1.21.0#IPConn.Read

IPConn 只提供了Read和write接口,如果TCP连接在采集器启动之前已经建立了连接,我们暂时无法把fd翻译成TCP五元组,也无法确认流量方向。

4.通过linux源码分析系统调用

1)找到hook点

linux 内核中定义recvmsg

SYSCALL_DEFINE3(recvmsg, int, fd, struct user_msghdr __user *, msg, unsigned int, flags)

内核整个调用过程

SYSCALL_DEFINE3(recvmsg) -> __sys_recvmsg->sockfd_lookup_light->___sys_recvmsg

通过解读recvmsg 我们发现 kernel 会在我们调用系统调用后通过sockfd_lookup_light把传进来的fd整数转化成为kernel的sock数据结构,即在给定的网络命名空间中查找并返回与指定文件描述符(socket 文件描述符)相对应的 struct socket 结构体指针,以便对套接字进行进一步的操作。

sockfd_lookup_light 使用的系统调用有:

read 和 write 并没有使用 sockfd_lookup_light,统一把 read write 处理操作挂到了,文件描述符操作结构体上

struct file_operations { struct module *owner; ......... ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); 
........ } __randomize_layout;

5.我们使用eBPF

1)hook sockfd_lookup_light

首先我们uprobe 住入口函数,记下进程号和 描述符的对应关系,因为每个进程的fd 都会对应一个sock结构体,所以我们把pid 进程号和 fd 组成一个key。

SEC("kprobe/sockfd_lookup_light")
int kprobe_hook_sockfd_lookup_light(struct pt_regs *ctx) { int sockfd = (int)PT_REGS_PARM1(ctx);u64 pid_tgid = bpf_get_current_pid_tgid(); pid_fd_map key = { .pid = pid_tgid >> 32, .fd = sockfd, }; struct sock **sock = bpf_map_lookup_elem(&sock_by_pid_fd_storage, &key); if (sock != NULL) { return 0; }bpf_map_update_elem(&sockfd_save_args, &pid_tgid, &sockfd, BPF_ANY); return 0; 
}

最后我们再 sockfd_lookup_light 把 pid和fd 的对应关系存储到map

SEC("kretprobe/sockfd_lookup_light")
int kretprobe_hook_sockfd_lookup_light(struct pt_regs *ctx) {u64 pid_tgid = bpf_get_current_pid_tgid(); int *sockfd = bpf_map_lookup_elem(&sockfd_save_args, &pid_tgid); if (sockfd == NULL) { return 0; }struct socket *socket = (struct socket *)PT_REGS_RC(ctx);enum sock_type sock_type = 0; bpf_probe_read_kernel(&sock_type, sizeof(short), &socket->type); ................. // 读出sock 结构体 struct sock *sock = NULL; bpf_core_read(&sock, sizeof(sock), (char *)socket);pid_fd_map pid_fd = { .pid = pid_tgid >> 32, .fd = (*sockfd), };bpf_map_update_elem(pid_fd_by_sock, &sock, &pid_fd, BPF_ANY); 
cleanup: bpf_map_delete_elem(&sockfd_save_args, &pid_tgid);
}

每当内核触发sockfd_lookup_light的时候我们就会把 pid,fd 以及sock 对应关系存储到sockfd_save_args中。

socket 五元组多存储在linux 内核的 struct sock 中

 

6.使用eBPF 绘制网络拓扑

通过hook accept 标记处的入口流量

SEC("kretprobe/inet_csk_accept")

通过hook connect 标记处的出口流量

SEC("kprobe/tcp_connect") SEC("kprobe/tcp_finish_connect")

通过hook sockfd_lookup_light 翻译出进程的fd对应的sock五元组

SEC("kretprobe/sockfd_lookup_light")

程序在调用系统调用的时候,我们就很容易使用 通过pid fd,在eBPF的 sock_by_pid_fd_storage map中 找到对应的sock,找到了kernel sock 结构体我们就可以通过sock 五元组,绘制出流量方向。

绘制网络拓扑图的过程中,我们使用eBPF可以抓取网络数据包并对其进行解析,获取各个节点之间的连接信息,包括源地址、目标地址、协议类型、端口等。通过收集和分析这些数据,我们可以逐步构建整个网络拓扑,并将其可视化为图形化的形式。

最后结合使用eBPF程序和图形数据库,我们完成了网络拓扑的绘制:

以上即为本次eBPF绘制网络拓扑图的全部内容。可观测之eBPF系列,正在不断创作中,欢迎您长期关注~

如果有用,请辛苦点个赞吧!


擎创科技,Gartner连续推荐的AIOps领域标杆供应商。公司专注于通过提升企业客户对运维数据的洞见能力,为运维降本增效,充分体现科技运维对业务运营的影响力。

行业龙头客户的共同选择

了解更多运维干货与行业前沿动态

可以右上角一键关注

我们是深耕智能运维领域近十年的

连续多年获Gartner推荐的AIOps标杆供应商

下期我们不见不散~

这篇关于深入浅出运维可观测工具(四):如何使用eBPF绘制网络拓扑图的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/786677

相关文章

Linux使用fdisk进行磁盘的相关操作

《Linux使用fdisk进行磁盘的相关操作》fdisk命令是Linux中用于管理磁盘分区的强大文本实用程序,这篇文章主要为大家详细介绍了如何使用fdisk进行磁盘的相关操作,需要的可以了解下... 目录简介基本语法示例用法列出所有分区查看指定磁盘的区分管理指定的磁盘进入交互式模式创建一个新的分区删除一个存

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

SpringBoot使用Apache Tika检测敏感信息

《SpringBoot使用ApacheTika检测敏感信息》ApacheTika是一个功能强大的内容分析工具,它能够从多种文件格式中提取文本、元数据以及其他结构化信息,下面我们来看看如何使用Ap... 目录Tika 主要特性1. 多格式支持2. 自动文件类型检测3. 文本和元数据提取4. 支持 OCR(光学

JAVA系统中Spring Boot应用程序的配置文件application.yml使用详解

《JAVA系统中SpringBoot应用程序的配置文件application.yml使用详解》:本文主要介绍JAVA系统中SpringBoot应用程序的配置文件application.yml的... 目录文件路径文件内容解释1. Server 配置2. Spring 配置3. Logging 配置4. Ma

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

基于Python开发电脑定时关机工具

《基于Python开发电脑定时关机工具》这篇文章主要为大家详细介绍了如何基于Python开发一个电脑定时关机工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 简介2. 运行效果3. 相关源码1. 简介这个程序就像一个“忠实的管家”,帮你按时关掉电脑,而且全程不需要你多做

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

使用SQL语言查询多个Excel表格的操作方法

《使用SQL语言查询多个Excel表格的操作方法》本文介绍了如何使用SQL语言查询多个Excel表格,通过将所有Excel表格放入一个.xlsx文件中,并使用pandas和pandasql库进行读取和... 目录如何用SQL语言查询多个Excel表格如何使用sql查询excel内容1. 简介2. 实现思路3

java脚本使用不同版本jdk的说明介绍

《java脚本使用不同版本jdk的说明介绍》本文介绍了在Java中执行JavaScript脚本的几种方式,包括使用ScriptEngine、Nashorn和GraalVM,ScriptEngine适用... 目录Java脚本使用不同版本jdk的说明1.使用ScriptEngine执行javascript2.

c# checked和unchecked关键字的使用

《c#checked和unchecked关键字的使用》C#中的checked关键字用于启用整数运算的溢出检查,可以捕获并抛出System.OverflowException异常,而unchecked... 目录在 C# 中,checked 关键字用于启用整数运算的溢出检查。默认情况下,C# 的整数运算不会自