改善Istio传播延迟(译文来自Airbnb)
介绍
在本文中,我们将展示我们如何识别和解决Airbnb的服务网格性能问题,提供对服务网格问题故障排除过程的见解。背景
在Airbnb,我们使用微服务架构,这需要服务之间的高效通信。最初,我们正是为此目的开发了一个名为Smartstack的本土服务发现系统。然而,随着公司的发展,我们遇到了可扩展性问题。为了解决这个问题,我们在2019年投资了一个名为AirMesh的现代服务网格解决方案,该解决方案基于开源Istio软件构建。目前,我们超过90的生产流量已经迁移到AirMesh,并计划在2023年完成迁移。症状:传播延迟增加
在将Istio从1。11升级到1。12之后,我们注意到传播延迟出现了令人费解的增加从Istio控制平面收到更改事件通知到处理更改并将其推送到工作负载之间的时间。这种延迟对我们的服务所有者很重要,因为他们依靠它来做出关键的路由决策。例如,服务器需要有一个比传播延迟更长的正常关闭时间,否则客户端可以向已经关闭的服务器工作负载发送请求并得到503错误。数据收集:传播延迟指标
我们是这样发现这种情况的:当我们注意到从1。5秒(Istio1。11中的p90)增加到4。5秒(Istio1。12中的p90)时,我们一直在监控Istio指标pilotproxyconvergencetime的传播延迟。Pilotproxyconvergencetime是Istio记录的传播延迟的几个指标之一。完整的指标列表是:pilotproxyconvergencetime测量从推送请求添加到推送队列到处理并推送到工作负载代理的时间。(请注意,更改事件被转换为推送请求,并在被添加到队列之前通过称为debounce的过程进行批处理,我们将在后面详细介绍。)pilotproxyqueuetime测量推送请求入队和出队之间的时间。pilotxdspushtime测量构建和发送xDS资源的时间。Istio利用Envoy作为其数据平面。Istio的控制平面Istiod通过xDSAPI(其中x可以看作一个变量,DS代表发现服务)来配置Envoy。pilotxdssendtime测量实际发送xDS资源的时间。
下图显示了这些指标中的每一个如何映射到推送请求的生命周期。
有助于理解与传播延迟相关的指标的高级图表。
调查xDS锁争用
CPU分析显示1。11和1。12之间没有明显变化,但处理推送请求花费的时间更长,表明时间花在了一些等待事件上。这导致了对锁争用问题的怀疑。
Istio使用四种类型的xDS资源来配置Envoy:端点发现服务(EDS)描述如何发现上游集群的成员。集群发现服务(CDS)描述如何发现路由期间使用的上游集群。路由发现服务(RDS)描述如何在运行时发现HTTP连接管理器过滤器的路由配置。侦听器发现服务(LDS)描述如何在运行时发现侦听器。
对指标pilotxdspushtime的分析表明,升级到1。12后只有三种类型的推送(EDS、CDS、RDS)有所增加。Istio变更日志显示在1。12中添加了CDS和RDS缓存。
为了验证这些更改确实是罪魁祸首,我们尝试通过将PILOTENABLECDSCACHE和PILOTENABLERDSCACHE设置为False来关闭缓存。当我们这样做时,CDS的pilotxdspushtime恢复到1。11级别,但不是RDS或EDS。这改进了pilotproxyconvergencetime,但不足以将其恢复到之前的水平。我们认为还有其他因素影响了结果。
对xDS缓存的进一步调查显示,所有xDS计算共享一个缓存。棘手的是Istio在底层使用了LRU缓存。缓存不仅在write上被锁定,而且在read上也被锁定,因为当您从缓存中读取时,您需要将项目提升到最近使用。由于多个线程试图同时访问同一个锁,这导致了锁争用和缓慢的处理。
形成的假设是xDS缓存锁争用导致CDS和RDS速度变慢,因为为这两个资源打开了缓存,并且由于共享缓存而影响了EDS,但没有影响LDS,因为它没有实现缓存。
但是为什么关闭CDS和RDS缓存都不能解决问题呢?通过查看构建RDS时使用缓存的位置,我们发现未遵守标志PILOTENABLERDSCACHE。我们修复了该错误并在我们的测试网格中进行了性能测试,以使用以下设置验证我们的假设:控制平面:
1个Istiodpod(内存26G,cpu10核)数据平面:
50个服务和500个pod
我们通过每10秒随机重新启动部署并每5秒随机更改虚拟服务路由来模拟更改
结果如下:
性能测试的结果表。
因为我们的Istiodpod不是CPU密集型的,所以我们决定暂时禁用CDS和RDS缓存。结果,传播延迟恢复到之前的水平。这是针对此问题的Istio问题以及xDS缓存的潜在未来改进。去抖动
这是我们诊断中的一个转折点:在深入研究Istio代码库期间,我们意识到pilotproxyconvergencetime实际上并没有完全捕获传播延迟。我们在生产中观察到,即使我们将优雅关闭时间设置得比pilotproxyconvergencetime长,服务器部署期间也会发生503错误。该指标不能准确反映我们希望它反映的内容,我们需要重新定义它。让我们重新审视我们的网络图,缩小以包括去抖过程以捕获变化事件的完整生命周期。
更改事件生命周期的高级图表。
当更改通知Istiod控制器时,该过程开始。这会触发发送到推送通道的推送。然后,Istiod通过称为去抖动的过程将这些更改组合到一个组合的推送请求中。接下来,Istiod计算推送上下文,其中包含生成xDS所需的所有信息。然后将推送请求与上下文一起添加到推送队列。问题在于:pilotproxyconvergencetime仅测量从组合推送添加到推送队列到代理接收到计算出的xDS的时间。
从Istiod日志中,我们发现去抖动时间几乎为110秒,即使我们将PILOTDEBOUNCEMAX设置为30秒。通过阅读代码,我们意识到initPushContext步骤正在阻止下一次去抖动,以确保首先处理较旧的更改。
为了调试和测试更改,我们需要一个测试环境。但是,很难在我们的测试环境中生成相同的负载。幸运的是,debounce和initpush上下文不受Istio代理数量的影响。我们在生产中设置了一个没有连接代理的开发箱,并运行自定义图像来分类和测试修复。
我们执行了CPU分析并仔细研究了耗时较长的函数:
Istiod的CPU配置文件。
服务DeepCopy功能花费了大量时间。这是由于使用了copystructure库,使用goreflection做deepcopy,性能开销很大。删除库既简单又非常有效,可以将我们的去抖动时间从110秒减少到50秒。
DeepCopy改进后Istiod的CPU配置文件。
在DeepCopy改进之后,cpu配置文件的下一个大块是ConvertToSidecarScope函数。这个函数花了很长时间来确定每个Istio代理导入了哪些虚拟服务。对于每个代理出口主机,Istiod首先计算所有导出到代理命名空间的虚拟服务,然后通过将代理出口主机名与虚拟服务主机匹配来选择虚拟服务。
我们所有的虚拟服务都是公开的,因为我们没有指定exportTo参数,该参数是该虚拟服务导出到的命名空间列表。如果不配置该参数,则虚拟服务会自动导出到所有命名空间。因此,VirtualServicesForGateway函数每次都创建并复制所有虚拟服务。当我们有许多具有多个出口主机的代理时,这种切片元素的深层复制非常昂贵。
我们减少了不必要的虚拟服务副本:我们没有传递虚拟服务的副本,而是将virtualServiceIndex直接传递到select函数中,进一步将去抖时间从50秒减少到30秒左右。
我们目前推出的另一项改进是通过设置exportTo字段来限制虚拟服务的导出位置,基于哪些客户端可以访问服务。这应该减少大约10秒的去抖动时间。
Istio社区也在积极致力于改进推送上下文计算。一些想法包括添加多个worker来计算sidecar范围,仅处理更改的sidecar而不是重建整个sidecar范围。我们还添加了去抖动时间的指标,以便我们可以将其与代理收敛时间一起监控,以跟踪准确的传播延迟。结论
总结我们的诊断,我们了解到:我们应该同时使用pilotdebouncetime和pilotproxyconvergencetime来跟踪传播延迟。xDS缓存有助于降低CPU使用率,但会因锁争用而影响传播延迟,请调整PILOTENABLECDSCACHE和PILOTENABLERDSCACHE以查看最适合您系统的内容。通过设置exportTo字段来限制Istio清单的可见性。
如果您对此类工作感兴趣,请查看我们的一些相关角色!致谢
感谢Istio社区创建了一个伟大的开源项目,并与我们合作使其变得更好。还呼吁整个AirMesh团队在Airbnb构建、维护和改进服务网格层。感谢LaurenMackevich、MarkGiangreco和SurashreeKulkarni编辑这篇文章。
〔1〕:查看我们在Istio上的演示文稿Airbnb以了解详细信息。
〔2〕:请注意,最后两种情况发生了一些CPU节流,因此如果我们要分配更多CPU,我们预计传播延迟(尤其是P99)会进一步改善。
〔3〕:Istiod服务控制器监控来自不同来源的注册服务的变化,包括kubernetes服务、ServiceEntry创建的服务等,Istiod配置控制器监控用于管理这些服务的Istio资源的变化。
〔4〕:PR1,PR2
作者:YingZhu
出处:https:medium。comairbnbengineeringimprovingistiopropagationdelayd4da9b5b9f90