热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

基于eBPF的云原生可观测性开源项目Kindling之容器网络定界

背景随着kubernetes逐渐成为应用部署的事实标准,开发运维人员可以更便捷的部署、拓展应用程序,同时也带来了一系列的云上疑难杂症。其中最莫过于头疼

背景

随着kubernetes逐渐成为应用部署的事实标准,开发运维人员可以更便捷的部署、拓展应用程序,同时也带来了一系列的云上疑难杂症。其中最莫过于头疼的无非是网络问题。由于传统的虚拟机应用使用内核网络比较清晰,内核网络故障概率也比较少,问题往往出现在物理网络,即使问题出现在内核网络,也相对比较容易定界。而容器使用内核网络的方式出现了颠覆性的变化,比如使用了更多的二层、三层转发、不同namespace的通信、各种路由防火墙策略。在如此复杂的环境下,可能某一条网络规则的配置错误或者某个虚拟网络设备的状态不正常都会导致网络故障。

在如此复杂的网络场景下,我们的SRE或者研发工程师很难全局把控容器网络,可以说整个内核网络是一个黑盒。所以,面对这一系列的困境,kindling正在逐步尝试将内核网络白盒化以及帮助工程师确定是容器网络问题还是物理网络问题


Kindling的设想

基于以上问题,假如告诉用户数据包在哪个网络设备之后就“断链”是不是就能缩小问题的排查范围了呢?下图是flannel网络的vxlan模式通信图。

vxlan模式通信图

当podA发起一次调用podB的请求时,中间会经过veth设备、cni网桥、vxlan设备、物理网卡。


  • 对于该次请求而言,如果数据包已经从node1的ens192网卡上发送出去了,而node2并没有收到该次请求,则说明物理网络出现了问题。

  • 同理,对于该次请求而言,在cni0上和flannel.1之间出现了“断链”,则说明cni0和flannel.1之间的通信出现了问题。

接着,用户工程师可以在这个范围内去排查具体问题,比如:

物理网络问题,就看物理设备状态等。

cni0和flannel.1的通信问题则查看路由规则,防火墙策略等。

在这个基础上,用户工程师在排查类似网络问题时,不至于像无头苍蝇一样不知道如何下手,明确了在哪一阶段出现了问题


验证设想

为此,笔者动手使用eBPF去hook了一系列网络路径相关的函数,并且构造了一个cni0和flannel.1通信故障的场景。

使用以下命令开启阻断flannel.1设备接收数据包:

tc qdisc add dev flannel.1 root netem loss 100%

于是,笔者看到了大量网络数据包形成了“断链”,如图所示,该skb在经过cni0之后并没有如预想的那样到达flannel.1设备:



我们再来看看正常的网络数据包路径,使用以下命令关闭阻断:

tc qdisc del dev flannel.1 root netem loss 100%

很明显,如下图所示,数据包传输正常了:


如何实现


大致思路

由于数据包在内核中都会以sk_buff数据结构传递,所以我们只需要追踪容器网络中skb会触达的关键函数。由于不同的CNI会对容器发送的原始数据包进行不同的处理,比如flannel的vxlan模式会在容器以太网帧外增加vxlan头和一些主机的ip信息,所以skb的包头往往不能作为串联起这一串函数的key。我们选择skb的内存地址作为key,但是内存地址也会带来很多问题,比如skb的fclone机制和复用机制会影响key作为唯一值的判断。


追踪点位(函数)


  • 核心点位:net_dev_start_xmit(网卡发包)、netif_receive_skb(网卡收包)

  • 辅助判断点位:__kfree_skb、net_dev_alloc_skb

为什么选择这些点位,选择的标准是什么?


  1. 靠近网卡或者网络设备收发包,用于判断skb是否到达网络设备,net_dev_start_xmit和netif_receive_skb分别靠近网络设备收和发包,所以选择这两个点位作为主要数据来源

  2. 除了主要数据的来源,我们还需要一些点位来知道skb一次使命的完成(笼统点来说就是一个包处理完成)。而完成的标志往往是这个skb代表的内存地址被释放,重新申请或者复用,所以我们有了以下很多选择来组合使用:


  • 申请skb内存: alloc_skb、alloc_skb_fclone、dev_alloc_skb、netdev_alloc_skb
  • 释放skb内存: __kfree_skb

这些hook点被调用都非常频繁,为了尽量少的去hook,我们选择了kfree_skb+net_dev_alloc_skb,选择kfree_skb是因为释放的时候作为终点是最及时的,选择net_dev_alloc_skb是因为他不单单使用__alloc_skb来申请内存,而且有复用的逻辑,所以也必须解决这个场景,请看下列代码:

struct sk_buff *__netdev_alloc_skb(struct net_device *dev,unsigned int length, gfp_t gfp_mask)
{struct sk_buff *skb &#61; NULL;unsigned int fragsz &#61; SKB_DATA_ALIGN(length &#43; NET_SKB_PAD) &#43;SKB_DATA_ALIGN(sizeof(struct skb_shared_info));if (fragsz <&#61; PAGE_SIZE && !(gfp_mask & (__GFP_WAIT | GFP_DMA))) {void *data;if (sk_memalloc_socks())gfp_mask |&#61; __GFP_MEMALLOC;data &#61; __netdev_alloc_frag(fragsz, gfp_mask);if (likely(data)) {// 复用逻辑skb &#61; build_skb(data, fragsz);if (unlikely(!skb))put_page(virt_to_head_page(data));}} else {// else选择了 __alloc_skbskb &#61; __alloc_skb(length &#43; NET_SKB_PAD, gfp_mask,SKB_ALLOC_RX, NUMA_NO_NODE);}if (likely(skb)) {skb_reserve(skb, NET_SKB_PAD);skb->dev &#61; dev;}return skb;
}

另外&#xff0c;fclone是较新版 kernel 中添加的一个特性&#xff0c;以前版本的 kernel 中 skb 在分配的时候都是 从后备高速缓存(lookaside cache)skbuff_head_cache 中获取 sk_buff 的结构&#xff1b;而现在可以 在调用 alloc_skb()&#xff0c;通过 fclone 参数选择从 skbuff_head_cache 或者 skbuff_fclone_cache 中分配。两者的区别在于 skbuff_head_cache 在创建时指定的单位内 存区域的大小是 sizeof(structsk_buff)而 skbuff_fclone_cache 在创建时指定的单位内存区域大小是 2*sizeof(struct sk_buff)。所以&#xff0c;一个内存地址可能完成两次使命&#xff0c;如果单单为了解决这个问题而去hook __alloc_skb 那无疑是不值得的&#xff0c;所以我们选择从skb的数据结构中或者fclone的值&#xff0c;来判断是否进入了该逻辑。

于是&#xff0c;我们针对这些hook点做了如下的数据分析&#xff1a;


​额外的问题


  • 为了判断skb最终有没有从物理网卡出去&#xff0c;我们需要智能或者手动配置物理网卡的名称或者mac地址&#xff0c;这里智能识别选择了收发包最多的网卡作为物理网卡&#xff0c;这个逻辑在大部分场景下是没有问题的。为了防止意外情况&#xff0c;也提供了手动配置的方式

  • 对于同主机pod的调用&#xff0c;用路由来识别&#xff0c;发现路由指向本机&#xff0c;则过滤掉这些数据&#xff0c;因为这些数据会影响“断链”的判断

  • 为了性能考虑&#xff0c;只传递出起始点到终点时间比较长的数据&&最终目的地不是容器网卡或者物理网卡的数据


最终事件


字段

描述

类型

tuple

eth0网卡抓到的四元组

tuple

devs[]

网络设备序列

char[]

duration

起始点到终点的时间段&#xff0c;比如容器网卡到物理网卡的时间

u32


性能损耗

​可以看出&#xff0c;由于网络函数被触发频率比较高&#xff0c;所以在5000左右的tps下还是给用户程序的cpu造成了13%的额外损耗。

Kindling是一款基于eBPF的云原生可观测性开源工具&#xff0c;旨在帮助用户更好更快的定界云原生系统问题&#xff0c;并致力于打造云原生全故障域的定界能力。

Kindling项目地址&#xff1a;Kindling

在云可观测性方面有任何疑问欢迎与我们联系&#xff1a;Kindling官网


推荐阅读
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 本文介绍了游标的使用方法,并以一个水果供应商数据库为例进行了说明。首先创建了一个名为fruits的表,包含了水果的id、供应商id、名称和价格等字段。然后使用游标查询了水果的名称和价格,并将结果输出。最后对游标进行了关闭操作。通过本文可以了解到游标在数据库操作中的应用。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • RouterOS 5.16软路由安装图解教程
    本文介绍了如何安装RouterOS 5.16软路由系统,包括系统要求、安装步骤和登录方式。同时提供了详细的图解教程,方便读者进行操作。 ... [详细]
  • 解决Sharepoint 2013运行状况分析出现的“一个或多个服务器未响应”问题的方法
    本文介绍了解决Sharepoint 2013运行状况分析中出现的“一个或多个服务器未响应”问题的方法。对于有高要求的客户来说,系统检测问题的存在是不可接受的。文章详细描述了解决该问题的步骤,包括删除服务器、处理分布式缓存留下的记录以及使用代码等方法。同时还提供了相关关键词和错误提示信息,以帮助读者更好地理解和解决该问题。 ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
author-avatar
大约在冬季1122_867
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有