大家好,我是劉旭,来自同性云服务网络团队。我们团队主要负责开发同性云的服务网络产品TCM。今天很高兴给大家分享一下我们使用EPF优化服务网络性能的一些经验。这是E-SOUL的架构,也是目前服务网络普遍采用的一个架构。每个POD中会印行一个塞特卡容器,控制面负责给所有塞特卡容器下发配置。塞特卡容器中运行着NOE代理,所有进出POD的流量,会被IB Tables截持到NOE处理。NOE由此皆家开发,性能比较强,耽搁了塞特卡的资源占用不大。但是在大规模机器中,塞特卡的资源占用总量绝对是不能忽视的。从我们的经验来看,性能和资源开销一直是客户十分关心的一个问题,也是决定客户是否使用服务网络产品的一个关键因素。因此,我们团队一直在尝试优化塞特卡的性能,降低请求转发试验并减少性能开销。同时,我们也看到了社区中CLIM使用EPF代替IB Tables优化K8S Service性能的方案。我们大受启发,决定使用EPF代替IB Tables实现流量截持。本次分享主要范围以下几个部分。第一部分介绍目前社区使用的GIB Tables的流量截持方案。第二部分介绍我们实现的GIB Tables的流量截持方案。第三部分介绍使用SOCMAP优化NWI和应用进程间保罗通信性能的方案。最后是性能测试,对比下IB Tables和EPF的性能。首先,我们来看一下目前社区正在使用的GIB Tables的流量截持方案。这是一个POD从创建到运行的过程,创建POD时,POD内只有一个Application容器。API Solar会通过WebPOOC请求Sidecar Injector。Sidecar Injector会注入两个容器。分别是Ease-O-Init和Ease-O-Proxy。Ease-O-Init是一个Ease-O-Init container,负责创建流量截持相关的IB Tables规则,在创建完成后会退出。Ease-O-Proxy实际就是NWI,负责处理这个POD所有的进出流量。下面,我们来详细分析一下Ease-O-Init创建的IP Tables规则,左图展示了Ease-O-Init创建的IP Tables规则,右图展示了IP Tables的数据包处理流程,Ease-O-Init创建的IP Tables规则都在Net表上,Net表主要用于网络地址转换,例如流量重定项。这张图更直观地展示了IP Tables规则,以及NWI的处理流程。入流量首先到达PolyRoutine,然后到达Ease-O-Inbound,最后到达Ease-O-In redirect,被重定向到1506端口,1506端口由NWI的处理流程监听,出流量首先到达Output,然后到达Ease-O-Output,最后到达Ease-O-In redirect,被重定向到1501端口,1501端口由NWI的处理流程监听,我们再来分析下请求到达NWI后,NWI的处理流程,我们先来看一下WATCHOR INBUND LACENOR的配置,WATCHOR INBUND LACENOR会根据请求的原始目的地址,路由到相应的Class上,例如这里请求的原始的目的地址的端口是80,会被路由到INBUND 80这个Class上,INBUND 80这个Class上的Endpoint就是LogoHouse的80,请求由NWI转发到应用进程的80端口,我们再来看一下WATCHOR OUTBUND LACENOR的配置,和INBUND LACENOR,WATCHOR OUTBUND LACENOR也会根据请求的原始目的地址,转发到对应的Lacenor上,例如我们这里请求的原始目的地址的端口是80,会被路由到0.0.0.0帽号80这个Lacenor上,0.0.0.0帽号80这个Lacenor使用了80这个RDS配置,我们再继续看80这个RDS的配置,80这个RDS会根据请求的House Hider转发到对应的Class上,例如我们这里的House是NGX.default.rsvc.class.local,会被路由到0.0.0.0.0,NGX.default.rsvc.class.local上,这个Class的andpoint就是我们的NGX的PODIP,如果我们的NGX有多个副本,这里也会有多个IP,下面我们介绍一下如何使用EVPF在服务网格中完成流量接驰,EVPF是一种可以在Linux内核中运行用户编写的程序,而不需要修改内核代码,或加载内核模块的技术,EVPF目前被广泛用于网络,安全,监控等领域,在Couple Networks社区最早,应该也是最有影响力的项目,是Celium,Celium最早提出了使用EVPF替代IvTables优化K8S Service性能的发案,我们的发案也是受Celium启发,这是一个EVPF程序从开发,到编译,到运行的整个过程,首先我们使用C語言开发EVPF程序,然后使用Klan编译为字节码文件,再通过BPF系统调用加载,内核会对我们的EVPF程序进行教研,如果教研通过,会即时编译为机器码,并而太子到指定的互可点上,EVPF在内核中有很多互可点,例如系统调用,骚客的事件等,EVPF同时提供了Maple,用于和用户空间共享数据,也可用于EVPF程序间共享数据,在我们的发案中,也使用了很多个EVPFMaple,我们先来看一个最简单的EVPF程序,EVPF程序采用C語言编写,我们首先看最后一行的Lessence,Lessence定义了该EVPF程序的Lessence类型,因为Linux内核采用GPL许可证,所以它只能夹载GPL许可证的程序,如果将程序设置为其他许可证,内核将拒绝夹载。我们再看这个Hello寒数的具体内容,Hello调用了BPFTracePronK,将Message写入TraceFS的RimBuffer中,BPFTracePronK最多可以接受3个参数进行格式化说出。塞克KProb,随着可动声明Hello使用KProb互可到系统调用的可动寒数入口处。这样,我们将这个EVPF程序和太迟到KProb互可上后,所有的可动系统调用都会执行我们的EVPF程序。下面介绍下我们发案的具体实现,首先是对入向流量的处理。在服务网络中,应用程序接收到的流量都是由NY转发,所以理想情况应该是应用程序只监听Localhose的端口,NY监听PoldIP的端口,这样请求不需要经过IPTables接持,即可到达NY,再由NY转发给应用程序。我们通过互可办的系统调用实现这一点。我们在EVPF程序中修改办的地址,让应用程序只监听Localhose的,不过这里需要注意,有些程序可能会使用IPV6的地址,因此我们这里有两个EVPF程序,分别处理IPV4和IPV6的办的。不同的IPTables,我们可以在不同的Network Namespace里创建不同的IPTables规则,它们互不影响,EVPF程序Attach到指定的互可点上后,例如办的,系统中的所有经常调用办的都会触发我们的EVPF程序。所以这里我们需要区分,哪些是需要我们EVPF程序处理的,我们这里使用Network NamespaceCookie作为标识,在KBS中,每个POD都有独立的Network Namespace,每个Namespace都有唯一的Cookie,这个Cookie可以在EVPF程序中调用BPFGateNetNamesCookie获取。我们这里将需要通过EVPF完成流量截止的POD对应的Network NamespaceCookie存储在CookieMap中,在EVPF程序被触发时,我们首先判断当前Socket的Network NamespaceCookie是否在CookieMap中,如果是,再继续处理。我们这里还有一个Inbound PODMap,这个Map的数据有每个POD的Include Inbound POD和Exclude Inbound POD注决生长。我们可以通过这两个注决,配置控制拦截或者不拦截指定端口。下面我们来看一下CookieMap和Inbound PODMap的具体定义。EVPF支持多种不同类型的Map,我们这里的两个Map都是哈希类型的。定义EVPFMap时,需要指定KV的类型和Size,以及最佳容量等参数。CookieMap用于存储,需要使用EVPF完成流量截止的POD对应的Network NamespaceCookie,K就是这个Cookie,Value没有实际的意义。Inbound PODMap的Key是一个结构体,包含Cookie和端口信息,作用是存储每个POD的Inbound POD配置,Value为1时对应InClue的Inbound POD,Value为0时对应ExClue的Inbound POD。我们再来看一下Band4的具体实现。首先我们通过BPFGateNSCookie获取当前的Cookie信息,如果Cookie在CookieMap中再继续处理,否则直接返回,然后通过BPFGateCarrantUIDGID返回当前的UID和GID的信息,这里我们需要通过判断,UID和GID,是不是Ease of Proceed,也就是1337,如果是,我们也不需要处理,因为我们这里只修改应用程序的办的地址,接下来就是将办的地址修改为Localhost的并返回,接下来我们还需要修改控制面下发的XDS配置,需要下发POD IP和对应端口的recender到NVOL例如,我们这里的POD IP是172.16.1.100,应用程序监听80端口,就需要NVOL监听172.16.1.100的80端口,但是这里还有一个问题是,运行NVOL是Ease of Proceed用户,不是Root用户,默认情况下,非Root用户不能办的1024以下的特权端口,我们这里需要将IP and PrevisionPOD Start这个参数调整为0,这样NVOL就可以办的1024以下的端口,我们来看一下修改后的实际效果,上图是修改前,下图是修改后,第一个区别是我们删除了我说英镑的Listener,也就是NVOL不会再监听1500的端口,第二个区别是,之前英文程序监听的是任意地址的80端口,现在只监听Local Host的,第三个区别是NVOL会监听POD的IP的80端口,最终的实际效果就是这样,IP Tables的法案,每个报文都需要拦截处理,而我们基于EBPF的修改办的地址的法案,我们只在英文程序办的是执行一次EBPF程序,在后面的请求时不会再执行,相比IP Tables的法案减少了性能开销,接下来我们来看一下对出略量的处理,对出略量的处理比较复杂,这里我们不仅需要修改连接的目的地址,同时还需要让NVOL可以通过Gate of Sock Option这个系统调用,获取到连接的原始目的地址,因为NVOL在Whatchall Out of Bound Listener中,使用了Arm Genome Destination Filter,这里一共设计3个EBPF程序,Connect4在建立TCP连接前,会将目的地址修改为Local Host的15001,也就是NVOL Whatchall Out of Bound Listener的地址,同时将原始的目的地址存储在Socket的Local Storage中,因为此时还没有确定原IP和原端口,在TCP连接建立完成后,Socket Office会读取保存在Socket Local Storage中的原始目的地址,并使用Cookie加四元组作为key,将原始目的地址保存在Arm Genome Destination Map中,Gate of Sock Option会拦接Gate of Sock Option系统调用,返回保存在Map中的原始目的地址,这是Socket Storage Map的定义,Socket Storage Map是一种特殊类型的Map,这个类型的Map其实是把数据保存在Socket的Local Storage中,我们这里是将原始的目的地址保存在这个Map,Arm Genome Destination Map是哈希类型的Map,T是Cookie和四元组,Y6是原始的目的地址,Gate of Sock Option通过读取这个Map中的数据,就可以获得原始的目的地址信息,Out of Bound还有一种情况需要处理,Eso在1.8版本隐容了智能DNS代理,Satakar会截持应用程序的DNS请求并处理,这个截持也是通过Ibitables实现的,我们也需要用EBPF实现对DNS请求的截持,基于TCP协议的DNS请求拦截流程和上面类似,这里我们介绍一下对UDP的处理,这里供设计三个EBPF程序,Connect4,SandMessage4和ReceiveMessage4,Connect4和SandMessage4作用相同,都是用于修改连接的目的地址,这里有两个是因为Linux提供了两种发送UDP数据的方式,第一种是先调用Connect再调用Sand,第二种是直接调用Sand2,修改连接的目的地址后,我们还需要将原始的目的地址保存到Socket的Local Storage,在ReceiveMessage时,再将IP地址改回原始的目的地址,这样做的原因是因为有些程序,例如NSLOOKUP,会教验回包的IP地址和Connect4是否一致,如果不一致会包错,接下来介绍一下使用SocketMap加速NY和英文进程间网络重新的方案,SocketMap是一种特殊的EBPFMap,一般用于Socket的重新项,使用SocketMap优化俗网格性能的方案,在社区最早由Climb提出,这里直接用Clim的两张图片来说明一下,在注入Socket代理后,NY和英文进程间的通信都需要经过TCPIP协议站的处理,增加了请求实验,优化后,通过EBPF绕过TCPIP协议站,直接将数据发送给对端的Socket,需要说明的是,如果图中的两个POD在同一节点上,中间这里也是可以绕过的,下面我们介绍下具体原理,这里有两个EBPF程序,萨克奥布斯坚强萨克的事件,并以四元组为Key,将萨克的信息存储在萨克哈希这个Map中,对于两端都在本节点的萨克来说,萨克奥布斯会执行两次,客户端发送SYN时会产生一个事件,服务端发送SYN加ICK时会产生一个事件,因此会存储两条数据在Map中,SK Message拦结萨克哈希中的萨克的散的Message调用,然后去Map中查找对端萨克的,之后调用BPF函数,绕过TTPIP协议站,直接将数据发送到对端的萨克的,这是萨克哈希的顶应,K是四元组,Y6是萨克的,我们看一下萨克奥布斯的具体实现,在TTP主动或被动间连时都会触发,然后以四元组为K,将萨克的信息保存在萨克哈希中,这是SK Message的具体实现,在萨克的调用散的Message调发,我们通过交换原地址和目的地址,来查到对端萨克的,但是我们发现,用四元组作为K可能存在冲突的问题,例如同一级点上有两个炮的,应用程序都监听80用端口,NY通过同一用端口5万件应用程序,就会出现K冲突的问题,为了解决这个问题,我们在K中添加了network和namesviz cookie,但是对于同一级点上两个炮的同性的情况,在SK Message中,无法直到对端萨克的network和namesviz cookie信息,因此,对于非local host的连接,我们将Cookie设置为0,这样既做到了K不会冲突,又做到了同一级点上的两个炮的网络通信,仍然可以被SockMap加速,但是这个方案依赖SockOps和SK Message,这两种EBPF程序,需要支持获取network和namesviz cookie,之前的版本内核是不支持的,这两个EBPF程序在漏的时候会爆测,我们给社区提要了两个派是,分别支持了在SockOps和SK Message中调用BPFGateNetNetSCookie函数,目前已合入5.15版本,整个方案的部署方式如图,每个节点上会印行EstoEBPF这个demonsite,复则漏的EtouchEBPF程序,以及创建EBPFMap,同时会旺势节点上的泡的信息,删除Map中的无效数据,EstoElite会通过系统调用,获取network和namesviz cookie信息,更新到CookieMap中,另外还会根据流量结实相关注解,更新对应的EBPFMap,我们使用了WRK和nrix进行的设计测试,横做标识NyWalk数,IPTables是社区当前的方案,EBPF是只使用了EBPFT代IPTables完成流量结实,没有使用SockMap优化,EBPF加SockMap是使用了EBPFT代IPTables完成流量结实,同时也使用了SockMap优化,我们可以看到,使用EBPFT代IPTables完成流量结实,同时使用SockMap,我们可以提高大约20%的性能,在大规模进行中,能节约的CPU资源还是比较可观的,同时也能降低请求试验,以上就是本次分享的全部内容,欢迎大家使用体验,腾讯云服务网络产品,感谢大家观看!