深入浅出运维可观测工具(四):如何使用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标杆供应商

下期我们不见不散~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/518548.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

react 分步表单中使用useEffect来更新表单值的问题

问题背景&#xff1a;我在完成一个分步表单的功能的时候&#xff0c;在进行点击下一步的时候&#xff0c;会通过useEffect 来监听下一步或者上一步的动作&#xff0c;进行表单赋值&#xff0c;我使用 useEffect(() > {setFieldsValue(formValues);}, [stepNum]) 直接赋值的…

iStoreOS系统-HomeAssistant服务下载安装HACS

iStoreOS系统-HomeAssistant服务下载安装HACS 1. HACS HACS&#xff08;Home Assistant Community Store&#xff09;是Home Assistant的一个插件商店&#xff0c;它允许用户轻松地浏览、安装和管理各种为Home Assistant开发的自定义插件和集成。通过HACS&#xff0c;用户可以…

Apache POI Excel的读写

1、 POI介绍 Apache POI是用Java编写的免费开源的跨平台的Java API&#xff0c;Apache POI提供API给Java程 序对Microsoft Office格式档案读和写的功能&#xff0c;其中使用最多的就是使用POI操作Excel文 件。 jxl&#xff1a;专门操作Excel maven坐标&#xff1a; POI结构:…

ServletContext

ServletContext 1.共享数据 ServletContext servletContext this.getServletContext(); String username "徐凤年"; servletContext.setAttribute("username",username);ServletContext servletContext this.getServletContext(); String username (…

真我诞生·2024消费者生活趋势报告(小红书平台)

跨过众声喧嚣的2023年&#xff0c;面对涌动的社会情绪、技术的创新以及流行风潮的更替&#xff0c;千瓜将2024年的隐形主轴定义为「真性情」&#xff0c;抛开宏观叙事法&#xff0c;透过“十大趋势”落到人文关怀上&#xff0c;有温度的营销叙事&#xff0c;才能吸引、影响人。…

最新的前端开发技术(2024年)

关于作者&#xff1a; 还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#xff0…

GraphQL

从表中查询10条数据 {user_info(_limit: 100) {idname} }根据id查询数据 {user_info(_where: {id: 1727515006802587648}_order_by: {create_time: _desc}_limit: 10) {idname} }外键联表查询(特别注意写法:update_by.id): {speaker_info(update_by.id: {_eq: 1729043650301…

Vue+SpringBoot打造校园电商物流云平台

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 商品数据模块2.3 快递公司模块2.4 物流订单模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 商品表3.2.2 快递公司表3.2.3 物流订单表 四、系统展示五、核心代码5.1 查询商品5.2 查询快递公司5.3 查…

KBPC5010-ASEMI逆变器整流桥KBPC5010

编辑&#xff1a;ll KBPC5010-ASEMI逆变器整流桥KBPC5010 型号&#xff1a;KBPC5010 品牌&#xff1a;ASEMI 封装&#xff1a;KBPC-4 最大重复峰值反向电压&#xff1a;1000V 最大正向平均整流电流(Vdss)&#xff1a;50A 功率(Pd)&#xff1a;大功率 芯片个数&#xff…

解决gradle构建java项目,在执行时打印到控制台出现乱码的问题,网上所能搜到的99%的解决文章都是错的

我一直喜欢用gradle构建项目&#xff0c;不怎么喜欢用maven&#xff0c;因为gradle可以执行脚本&#xff0c;功能强大&#xff0c;比maven的xml方式要简洁多了&#xff0c;但最近才发现一个奇怪的问题&#xff0c;就是在idea指定gradle编译和执行时&#xff0c;输出中文到控制台…

Facial Micro-Expressions:An Overview 阅读笔记

Proceedings of the IEEE上一篇微表情相关的综述&#xff0c;写的很详细。从心理学与计算机两个领域阐述了微表情生成的原因与相关算法&#xff0c;值得仔细研读。 摘要&#xff1a; Four main tasks in ME analysis arespecifically discussed,including ME spotting,ME recog…

【JAVA/Web】数组转对象

一. 需求 数组转对象 数组结构 List:[{id:1,code:phone,value:10101001},{id:2,code:name,value:admin},{id:3,code:address,value:XXXXXX} ]二. 数组转对象&#xff08;键值对映射关系&#xff09; 对象结构 object:{phone:10101001,name:admin,address:XXXXXX }2.1 Java…