在腾讯,曾经有很多产品已应用或者正在尝试应用istio来作为其微服务治理的根底平台。不过在应用istio时,也有一些对通信性能要求较高的业务会对istio的性能有一些担心。因为envoy sidecar的引入,使两个微服务之间的通信门路变长,导致服务延时受到了一些影响,istio社区始终以来也有这方面的声音。基于这类埋怨,咱们心愿可能对这一通信过程进行优化,以更好的满足更多客户的需要。
首先,咱们看一下istio数据面的通信模型,来剖析一下为什么会对延时有这么大的影响。能够看到,相比于服务之间间接通信,在引入istio 之后,通信门路会有明显增加,次要包含多出了两次本地过程之间的tcp连贯通信和用户态网络代理envoy对数据的解决。所以咱们的优化也分为了两局部进行。
内核态转发优化
那么对于本地过程之间的通信优化,咱们能做些什么呢?
其实在开源社区曾经有了这方面的摸索了。istio官网社区在2019年1月的时候曾经有了这方面探讨,在文档外面提到了应用ebpf的技术来做socket转发的数据代理,使数据在socket层进行转发,而不须要再往下通过更底层的TCP/IP协定栈的一个解决,从而缩小它在数据链路上的通信链路长度。
另外,网络开源我的项目cilium也在这方面有一个比拟深刻的实际,同样也是应用了ebpf的技术。不过在cilium中本地网络减速只是其中的一个模块,没有作为一个独立的服务进行开发实际,在腾讯云外部没法间接应用,这也促使了咱们开发一个无依赖的解决方案。
当然在初期的时候,咱们也对ebpf的技术进行了一个验证,从验证后果中能够看到,在应用了ebpf的技术之后,它的延时大略有20%到30%的晋升,阐明ebpf的技术利用在本地通信上还是有肯定优化能力的。
简略介绍一下ebpf,看一下它是怎么做到减速本地通信的。首先ebpf能够看作是一个运行在内核外面的虚拟机,用户编写的ebpf程序能够被加载到内核外面进行执行。内核的一个verify组件会保障用户的ebpf程序不会引发内核的crash,也就是能够保障用户的ebpf程序是平安的。目前ebpf程序次要用来追踪内核的函数调用以及平安等方面。下图能够看到,ebpf能够用在很多内核子系统当中做很多的调用追踪。
另外一个比拟重要的性能,就是咱们在性能优化的时候应用到的在网络上的一个能力,也就是上面提到的sockhash。sockhash自身是一个ebpf非凡的一个kv存储构造,次要被用作内核的一个socket层的代理。它的key是用户自定义的,而value是比拟非凡的,它存储的value是内核外面一个socket对象。存储在sockhash中的socket在发送数据的时候,如果可能通过咱们挂在sockhash当中的一个ebpf当中的程序找到接管方的socket,那么内核就能够帮忙咱们把发送端的数据间接拷贝到接收端socket的一个接管队列当中,从而能够跳过数据在更底层的解决,比方TCP/IP协定栈的解决。
在sidecar中,socket是怎么被辨认并存储在sockhash当中来实现一个数据拷贝的呢?咱们须要剖析一下数据链的本地通信的流量特色。
首先从ingress来讲,ingress端的通信会比较简单一点,都是一个本地地址的通信。ingress端的envoy过程和用户服务过程之间通信,它的原地址和目标地址刚好是一一对应的。所以咱们能够利用这个地址的四元组结构它的key,把它存储到sockhash当中。在发送数据的时候,依据这个地址信息反向结构这个key,从sockhash当中拿到接收端的socket返回给内核,这样内核就能够帮咱们将这个数据间接拷贝给接收端的socket。
egress会略微简单一点,在一个egress端服务程序对外收回的申请被iptables规定重定向到了envoy监听的一个15001的端口。在这里,发起方的源地址和接管方的目标地址是一一对应的,然而发起方的目标地址和接收端的源地址有了一个变动,次要是因为iptables对地址有一个重写。所以咱们在存储到sockhash中的时候,须要对这部分信息进行一个解决。因为istio的特殊性,间接能够把它改写成envoy所监听的一个本地服务地址。这样再存储到sockhash当中,它们的地址信息还是能够反向一一对应的。所以在查找的时候,还是能够依据一端的socket地址信息查找到另一端的socket,达到数据拷贝的目标。
通过对ebpf减速原理的剖析,咱们开发进去一个ebpf的插件,这个插件能够不依赖于集群自身的网络模式,应用daemonset形式部署到k8s集群的各个节点上。其中的通信成果如下图所示,本地过程的一个通信在socket层间接被ebpf拦挡当前,就不会再往下发送到TCPIP协定栈了,间接在socket层就进行了一个数据拷贝,缩小了数据链路上的一个解决流程
上面是对成果的一个测试,从整体来看,它的延时有大略5到8%的延时晋升,其实晋升的幅度不是很大,次要起因其实在整个通信的流程当中,内核态的一个解决占整个通信解决的工夫,延时其实是比拟少的一部分,所以它的晋升不是特地显著。
另外ebpf还有一些缺点,比方它对内核版本的要求是在4.18版本之后才有sockhash这个个性。另外sockhash自身还有一些bug,咱们在测试当中也发现了一些bug,并且把它提交到社区进行解决。
Envoy 性能钻研与优化
后面介绍了对istio数据面对流量在内核的解决所进行的一些优化。在istio数据面的性能问题上,社区留神到比拟多的是在内核态有一个显著的转发流程比拟长的问题,因而提出了应用eBPF进行优化的计划,然而在Envoy下面没有太多的声音。尽管Envoy自身是一个高性能的网络代理,但咱们还是无奈确认Envoy自身的损耗是否对性能造成了影响,所以咱们就兵分两路,同时在Envoy下面进行了一些钻研。
首先什么是Envoy?Envoy是为分布式环境而生的高性能网络代理,能够说基本上是作为服务网格的通用数据立体被设计进去的。Envoy提供不同层级的filter架构,如listenerFilter、networkFilter以及HTTPFilter,这使envoy具备十分好的可扩展性。Envoy还具备很好的可察看性,内置有stats、tracing、logging这些子系统,能够让咱们更容易地对系统进行监控。
进行istio数据面优化的时候,咱们面对的第一个问题是Envoy在istio数据面中给音讯转发减少了多少延时?Envoy自身提供的内置指标是很难反映Envoy自身的性能。因而,咱们通过批改Envoy源码,在Envoy解决音讯的开始与完结的地位进行打点,记录时间戳,能够取得Envoy解决音讯的延时数据。Envoy是多线程架构,在打点时咱们思考了性能和线程平安问题:如何高效而又精确地记录所有音讯的解决延时,以不便后续的进行剖析?这是通过如下办法做到的:
a. 给压测音讯调配惟一数字ID;
b. 在Envoy中预调配一块内存用于保留打点数据,其数据类型是一个构造体数组,每个元素都是同一条音讯的打点数据;
c. 提取音讯中的数字ID当作工夫戳记录的下标,将工夫戳记录到预调配的内存的固定地位。通过这种形式,咱们平安高效地实现了Envoy内的打点记录(毛病是须要批改Envoy以及压测工具)。
通过离线剖析,咱们发现了Envoy内音讯解决延时的一些散布特色。左图是对Envoy解决延时与音讯数量分布图,横轴是延时,纵轴是音讯数量。能够看到在高延时部份,音讯数量有异样减少的景象,这不是典型的幂率散布。进一步对高延时部份进行剖析后发现,这些高延时音讯平均地散布于整个测试期间(如上图右所示)。 依据咱们的教训,这通常是Envoy外部转发音讯的worker在周期性的执行一些耗费CPU的工作,导致有一部分音讯没方法及时转发而引起的。
深入研究Envoy在istio数据面的性能实现后,咱们发现mixer遥测可能是导致这个问题的根本原因。Mixer是istio老版本(1.4及以前)实现遥测和策略查看性能的一个组件。在数据面的Envoy中,Mixer会提取所有音讯的属性,而后批量压缩上报到mixer server,而属性的提取和压缩是一个高CPU的耗费的操作,这会引起延时数据分析中失去的后果:高延时音讯转发异样增多。
在确定了起因之后,咱们对Envoy的架构作了一些改良,给它减少了执行非关键工作的AsyncWorker线程,称为异步工作线程。通过把遥测的逻辑拆分进去放到了AsyncWorker线程中去执行,就解决了worker线程被阻塞的问题,进而能够升高envoy转发音讯的延时。
进行架构优化之后,咱们也做了比照测试,上图左测是延时与音讯数量图,能够看到它高延时局部失去显著的改善。上图右能够看出Envoy整体的延时升高了40%以上。
优化Envoy架构给咱们带来了第一手的教训,首先CPU是影响Istio数据面性能的要害资源,它的瓶颈次要呈现在CPU下面,而不是网络IO操作。第二,咱们对Envoy进行架构优化,能够升高延时,然而没有解决基本问题,因为CPU的应用没有升高,只是遥测逻辑转移到另外的线程中执行,升高Envoy转发音讯的延时。优化CPU使用率才是数据面优化的外围。第三点,Envoy的不同组件当中,mixer消化掉了30%左右的CPU,而遥测是mixer的外围性能,因而后续遥测优化就变成了优化的重要方向。
怎么进行遥测优化呢?其实mixer实现遥测是非常复杂的一套架构,应用Istio mixer遥测的人都深有体会,幸好istio新版本中,不止对istio的管制面作了大的调整,在数据面mixer也同样被移除了,意味着Envoy中高耗费的遥测就不会存在了,咱们是基于istio在做外部的service mesh,从社区失去这个音讯之后,咱们也疾速跟进,引入适配新的架构。
没有Mixer之后,遥测是如何实现的。Envoy提供了应用wasm对其进行扩大的形式,以放弃架构的灵活性。而istio社区基于Wasm的扩大开发了一个stats extension扩大,实现了一个新的遥测计划。与mixer遥测相比,这个stats extension不再上报全量数据到mixer server,只是在Envoy内的stats子系统中生成遥测指标。遥测零碎拉取Envoy的指标,就能够取得整个遥测数据,会大大降低遥测在数据面的性能耗费。
而后咱们对istio 1.5应用Wasm的遥测,做了一个性能的测试。发现整个Envoy代理在同样测试条件下,它的CPU升高10%,而应用mixer的遥测其实占用了30%的CPU,外面大部分逻辑是在执行遥测。按咱们的了解,Envoy至多应该有20%的CPU降落,然而实际效果只有10%左右,这是为什么呢?
新架构下咱们遇到了新的问题。咱们对新架构进行了一些实现原理和技术细节上的剖析,发现Envoy应用Wasm的扩大形式,尽管带来了灵活性和可扩展性,然而对性能有肯定的影响。
首先,Wasm扩大机制跟Envoy host环境是通过内存拷贝的形式进行通信,这是Wasm虚拟机的隔离性机制决定的。Envoy为了放弃架构灵活性的同时保障性能,使设计了一个非Wasm虚拟机运行扩大(如stats extension)的模式,即NullVM模式,它是一个假的Wasm虚拟机,实际上运行的扩大还是被编译在Envoy外部,但它也逃离不掉Wasm架构带来的内存拷贝影响。
其次,在实现extension与Envoy的通信时,一个属性的获取要通过屡次的内存拷贝,是一个非常复杂的过程。如上图所示,获取request.url这个属性须要在Envoy的内存和Wasm虚拟机内存之间进行一个简单的拷贝操作,这种形式的耗费远大于通过援用或指针提取属性。
第三,在实现遥测的时候,有大量的属性须要获取,通常有十几二十个属性,因而Wasm扩大带来的总体额定损耗十分可观。
另外,Wasm实现的遥测性能还须要另外一个叫做metadata_exchange扩大的反对。metadata_exchange用来取得调用对端的一些节点信息,而metadata_exchange扩大运行在另外一个虚拟机当中,通过Envoy的Filter state机制与stats 扩大进行通信,进一步减少了遥测的额定耗费。
那么如何去优化呢?简略对Wasm插件优化是没有太大帮忙,因为它的底层Wasm机制曾经决定了它有不少的性能损耗,所以咱们就开发了一个新的遥测插件tstats。
tstats应用Envoy原生的扩大形式开发。在tstats扩大外部,实现了遥测和metadata_exchange的联合,打消了Wasm带来的性能弊病。Tstats遥测与社区遥测兼容,生成雷同的指标,tstats基于istio管制面的EnvoyFilter CRD进行部署,用户能够平滑降级,当用户发现tstats的性能没有满足需要或者呈现一些问题时,也能够切换应用到社区提供的遥测扩大。
在tstats扩大还优化了遥测指标的计算过程。在计算指标的时候有许多维度信息须要填充,(目前大指标有二十几个维度的填充),这其实是一个比较复杂的操作,其实,有很多指标的维度都是节点信息,就是发动服务调用的客户端和服务端的一些信息,如服务名、版本等等。其实咱们能够将它进行一些缓存,减速这些指标的计算。
通过优化之后,比照tstats遥测和官网的基于Wasm的遥测的性能,咱们发现CPU升高了10到20%,绝对于老版本的mixer来说升高了20%以上,合乎了咱们对envoy性能调研的一个预期。上图右能够看到在延时上有一个显著的升高,即便在P99在不同的QPS下,也会有20%到40%的总体升高(这个延时是应用echo service做End-to-End压测失去的)。
应用火焰图从新察看一下Envoy外部的CPU应用散布,咱们发现tstats遥测插件占用CPU的比例显著更少,而应用wasm的遥测插件有一个显著的CPU占用来实现遥测,这也证实了tstats优化是有成果的。
总结
后面咱们分享了在优化istio数据面过程当中,在内核态和Envoy内摸索的一些教训。这是咱们优化的次要内容,当然还有一些其它的优化点,例如:
- 应用XDP进行减速,因为因为istio proxy的引入,Pod和Pod之间的拜访实际上是不须要通过主机上的iptables规定解决。基于这一点,咱们能够利用XDP的疾速转发能力间接将包发送到对应的网卡,实现疾速转发。
- 链路跟踪在Envoy中耗费的CPU也比拟可观,即便在1%的采样率下也耗费了8%的CPU,是能够进行优化的,所以咱们也在做这部分的工作。
- Envoy外部有十分多的内置统计,Istio的一些指标与Envoy内置的指标有一部分反复,能够思考进行一些裁剪优化,或者减少一些个性开关,当不须要应用的时候对它进行敞开。
总体来说,Istio数据面性能的损耗散布在各个环节,并不是独自的内核态音讯转发或者用户态Envoy就耗费特地多。在应用Mixer架构的Istio版本中,Envoy内一个显著的性能热点, mixer遥测,这也在版本迭代中逐渐解决了。
最初,在进行Istio数据面优化的时候须要综合思考各个环节,这也是咱们目前总体上对Istio数据面性能的一个意识,通过这次分享,心愿社区和大家都会在更重视Istio数据面的性能,帮忙ServiceMesh更好地落地。腾讯云基于Istio提供了云上ServiceMesh产品TCM,大家有趣味能够来体验。
问题
- 怎么判断我的项目须要应用服务网格?
服务网格解决的最间接的场景就是你的服务须要进行微服务治理,然而你们之前可能有多个技术栈,没有一个对立的技术框架,比方没有应用SpringCloud等。短少微服务治理能力,然而又想最低老本取得链路跟踪、监控、流量治理、熔断等这样的能力,这个时候能够应用服务网格实现。
- 这次优化有没有思考到回归到社区?
其实咱们也思考过这个问题,咱们在进行mixer优化的时候,过后思考到须要对Envoy做比拟多的改变,并且理解到社区规划从架构中去掉mixer,所以这个并没有回归到社区,异步工作线程架构的计划目前保留在外部。对于第二点开发的tstats扩大,它的性能和社区的遥测是一样的,如果提交到社区咱们感觉性能会有重叠,所以没有提交给社区。
- 服务网格数据调优给当初腾讯的业务带来了哪些扭转?
我想这里次要还是可观测性下面吧,之前很多的服务和开发,他们的监控和调用面的下面做得都是差强人意的,然而他们在业务的压力之下,其实是没有很欠缺的形式、没有很大的能源疾速实现这些服务治理的性能,应用istio之后,有不少团队都取得了这样的能力,他们给咱们的反馈都是比拟好的。
- 在缩小提早方面,腾讯做了哪些调整,服务网格当初是否曾经成熟,对开发者是否敌对?
在提早方面,咱们对性能的次要摸索就是明天分享的内容,咱们最开始就留神到延时比拟高,而后在内核做了相应的优化,并且钻研了在Envoy内为什么会有这些提早。所以咱们得出的论断,CPU是外围的资源,须要尽量升高数据面Proxy代理对CPU的应用,这是咱们做所有优化最外围的出发点,当CPU降下来,延时就会升高。服务网格当初是否曾经成熟。我感觉不同的人有不同答案,因为对一些团队,目前他们应用服务网格应用得很好的,因为他们有多个技术栈,没有对立的框架。他们用了之后,取得这些流量的治理和监控等等能力,其实曾经满足了他们的需要。然而对一些成熟的比拟大的服务,数据面性能下面可能会有一些影响,这个须要相应的团队进行认真的评估能力决定,并没方法说它肯定就是能在任何场景下能够间接替换现有的各个团队的服务治理的形式。
- 为什么不间接思考1.6?
因为咱们做产品化的时候,周期还是比拟长的。istio社区发版本的速度还是比拟快的,咱们还在做1.5产品化的时候,可能做着做着,istio1.6版本就收回来了,所以咱们也在不停更新跟迭代,始终在追随社区,目前次要还是在1.5版本上。其实咱们在1.4的时候就开始做当初的产品了。
- 公司在引入多种云原生架构,包含SpringCloud和Dubbo,作为运维,有必要用Istio做服务治理吗?另外SpringCloud和Dubbo这种架构迁徙到Istio,如何调整?
目前SpringCloud和Dubbo都有不错的服务治理性能,如果没有十分紧迫的需要,比方你们又须要引入新的服务并且用别的语言实现,我感觉持续应用这样的框架,可能引入istio没有太大的劣势。然而如果思考进行更大规模的服务治理,包含交融SpringCloud和Dubbo,则能够思考应用istio进行一个合并的,然而这个落地会比较复杂。那么SpringCloud和Dubbo迁徙到Istio如何调整?目前最简单的就是他们的服务注册机制不一样,服务注册模型不一样。咱们之前外部也有在预研如何提供一个对立的服务注册模型,以综合Istio和其它技术框架如SpringCloud的服务注册和服务发现,以及SpringCloud如何迁徙进来。这个比较复杂,须要对SDK做一些改变,我感觉能够下来再进行交换。
【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!