为了更好地治理一直增长的服务和流量,Houzz基础架构工程团队最近将Web Server服务从Amazon Elastic Compute Cloud(Amazon EC2)迁徙到Kubernetes集群。这次迁徙使资源缩小了33%,首页提早有了30%的改善。

新的Kubernetes集群的整体架构蕴含多个应用程序,包含用NodeJS编写的前端(FE)应用程序和用HHVM编写的后端(BE)服务。 FE应用程序通过HTTP上的Apache Thrift协定与BE服务进行通信。每个应用程序都启用了程度Pod主动缩放(HPA)。集群内以及与内部服务的通信由Istio治理,都会通过Envoy Sidecar。

迁徙过程中有诸多挑战,本文次要是想和大家分享一下咱们迁徙过程中的最佳实际。

Pod 启动提早

在开始Kubernetes迁徙时,咱们留神到Pod启动提早有时会在新配置的节点上产生。 Envoy容器准备就绪大概花了六分钟,阻止了其余容器的启动。从Envoy日志中,咱们察看到pilot-agent 一直报告Envoy尚未准备就绪,并提出了查看Istiod是否仍在运行的倡议。

咱们实现了一个守护程序集,其惟一的工作就是解析Istiod服务的FQDN。从其指标来看,咱们察看到在新节点疏导后域名零碎(DNS)名称解析几分钟后会超时,并认为Envoy遇到了雷同的超时问题。

咱们确定了导致该问题的根因是dnsRefreshRate,其在Istio 1.5.2中的默认值为5分钟,并且与察看到的提早大抵匹配。因为在启动某些Pod之后新节点上的DNS客户端已准备就绪,因而较长的重试距离导致Envoy无奈及时检测到DNS客户端的就绪状态。通过强制Envoy进行更频繁的重试,咱们将附加的Pod启动提早从360秒缩小到60秒。

请留神,在Istio 1.6中,默认的dnsRefreshRate已更改为5秒。

HHVM Pod 预热和级联伸缩

咱们的BE服务内置于HHVM中,该服务在其代码缓存预热之前具备很高的CPU使用率和高提早。预热阶段通常须要几分钟,因而在默认的15秒HPA同步周期或HPA评估CPU使用率指标并调整所需Pod数量的工夫距离内,它不能很好地工作。当因为负载减少而创立新的Pod时,HPA从新Pod中检测到更高的CPU使用率,并扩大了更多Pod。这种踊跃的反馈循环始终继续到新的Pod被齐全预热或达到Pod的最大数量为止。新的Pod齐全预热后,HPA检测到CPU使用率显着降落,并缩减了大量Pod。级联伸缩导致了不稳固和提早峰值。

咱们进行了两项更改以解决级联伸缩问题。咱们依据他们的官网倡议改良了HHVM预热过程。预热期间的CPU使用率从失常应用的11倍缩小到1.5倍。 Pod开始提供流量后的CPU使用率从失常应用的4倍升高到1.5倍。

此外,咱们将HPA同步工夫从15秒减少到10分钟。只管HPA对负载减少的响应速度较慢,但它防止了级联扩大,因为大多数Pod能够在10分钟内实现预热并开始失常应用CPU。咱们发现这是一个值得衡量的抉择。

更改之前的HPA流动:

更改之后的流动:

负载平衡

负载不平衡是咱们迁徙到Kubernetes期间遇到的最值得注意的挑战,只管它仅在最大的虚构服务中产生。症状是某些Pod在重负载下无奈通过就绪查看,而后更多的申请将路由到这些Pod,从而导致Pod在就绪状态和未就绪状态之间摆动。在这种状况下增加更多的节点或Pod将导致更多的飘动Pod。产生这种状况时,提早和谬误计数会大大增加。缓解此问题的惟一办法是强行放大部署规模,以杀死一直稳定的Pod,而无需增加新Pod。然而,这不是一个可继续的解决方案,因为更多的Pod很快就开始飘动。因为此问题,咱们屡次回退了迁徙。

负载平衡:

为了排查不便,咱们增加了额定的日志记录,发现负载不平衡时触发,一个可用区(AZ)的申请显著多于其余两个。咱们狐疑这种不均衡是因为咱们过后应用的起码申请负载平衡策略中的正反馈回路造成的。咱们尝试了其余几种策略(Round Robin,Locality Aware和Random),都没有解决问题。 在排除了负载平衡策略之后,咱们在其余两个方面寻找了踊跃的反馈循环:重试失败的申请和异样检测。只管Istio的官网文档中指出默认状况下不会对失败的申请进行重试,但理论的默认重试次数设置为2。重试会导致级联失败,因为某些申请失败后会发送更多申请。此外,咱们察看到异样检测中的某些行为(也称为被动健康检查)无法解释,因而咱们决定禁用这两个性能。之后,不均衡问题隐没了,咱们可能将95%的申请迁徙到Kubernetes。咱们在旧平台上保留5%的资源用于性能比拟和调整。 最后,咱们不确定重试或异样检测这两个性能中的哪一个是造成负载不平衡的起因,只管咱们当初认为它与重试无关。在将Istio降级到1.6版,进行了一些性能改良并将100%的申请迁徙到Kubernetes之后,咱们尝试从新启用离群值检测-咱们违心承当这种危险,因为更改能够在几秒钟内复原。在撰写本文时,咱们还没有遇到负载不平衡的问题。就是说,咱们用以下事实证明了咱们的实践,即以后Istio版本的集群配置与产生不平衡时的配置不同。

公布后性能降落
咱们察看到,每次公布后,Kubernetes上的提早都会随工夫减少,因而咱们创立了一个仪表板来显示Envoy在Ingress网关,FE应用程序和BE服务Pod中报告的入站/出站提早。仪表板表明,总体减少是由Envoy在BE Pod中报告的入站提早的减少所驱动的,这包含服务提早和Envoy自身的提早。因为服务提早没有显着减少,因而代理提早被认为是提早减少的驱动力。 咱们发现,每个版本公布后,Envoy在BE Pod中的内存使用量也随着工夫减少,这使咱们狐疑提早减少是因为BE Pod中的Envoy的内存透露引起的。咱们exec到一个BE Pod,并列出了Envoy和主容器中的连贯,发现Envoy中有大概2800个连贯,主容器中有40个连贯。在2,800个连贯中,绝大多数是与FE Pod(BE Pod的客户)连贯的。

为了解决Envoy内存透露问题,咱们测试了一些更改,包含:

1.将FE Pod到BE Pod之间的连贯的idleTimeout从默认的1小时缩小到30秒。此更改缩小了谬误数量并进步了申请成功率,但同时也减少了FE和BE容器之间每秒的连贯申请数量。 2.将Envoy的并发或线程数从FE Pod中的16缩小到2。该更改勾销了自第一次更改以来每秒大多数连贯申请数量的减少。 3.在BE Pod中将Envoy内存限度设置为300MB。察看到预期的行为,并且Envoy的内存使用量超出限度时重新启动。容器持续运行,然而内存使用率较低。重新启动Envoy时,有些Pod有短暂的准备就绪工夫,这是对前两个更改的补充。尽管前两个更改缩小了Envoy的内存使用量,但第三个更改将在Envoy的内存使用量超出限度时重新启动。与重新启动主容器相比,重新启动Envoy所导致的停机工夫显著更少,因为后者会在HHVM中产生几分钟的预热工夫。
变更之前的提早:

变更之前内存变动:

变更之后的提早:

解决了公布后的性能降落问题之后,咱们将100%的申请迁徙到Kubernetes并敞开了旧主机。

集群范畴内的瓶颈

随着咱们将更多申请迁徙到Kubernetes中最大的虚构服务,咱们遇到了跨集群范畴的资源的问题,这些资源在虚构服务之间共享,包含API服务器,DNS服务器和Istio管制立体。 在事件期间,继续了一两分钟的所有虚构服务察看到谬误峰值,咱们发现这是因为未能解析FE Pod中BE虚构服务的DNS名称所致。谬误峰值还与DNS解析谬误和DNS申请降落无关。 Ingress服务调用不应依赖于DNS。相同,应该将FE Pod中的Envoy定向为将出站HTTP申请定向到BE服务中的端点的IP地址。然而,咱们发现NodeJS Thrift客户端库对无用的服务IP进行了DNS查找。为了打消DNS依赖性,咱们部署了Sidecar,将Virtual Service中BE服务的主机绑定到本地套接字地址。

Sidecar清单示例:

只管Istio从应用程序角度最大水平地进步了透明度,但除了在利用程序代码中用本地IP地址和端口号替换DNS名称之外,咱们还必须显式增加Host标头。 值得一提的是,sidecar的一个附带益处是能够优化内存应用。默认状况下,无论是否须要,Istio都会将跨Kubernetes集群的每个服务的上游集群增加到Envoy。保护那些不必要的配置的一项重大老本是Envoy容器的内存应用。 应用sidecar解决方案,咱们将DNS服务器故障与要害门路中的服务调用隔离开来,将DNS服务器上的QPS从30,000缩小到6,000,并将Envoy的均匀内存使用量从100MB缩小到70MB。

coreDNS QPS的变动:

变更后内存应用状况:

咱们遇到的另一个谬误顶峰与不统一的集群成员身份(节点终止时呈现这种状况)无关。只管Kubernetes应该可能优雅地解决节点终止,然而节点终止时有一种非凡状况会导致谬误尖峰:在终止的节点上运行Istiod pod。节点终止后,一些FE Pod破费了大概17分钟的工夫从新的Istiod Pod接管更新。在他们收到更新之前,他们对BE 集群成员身份认识不统一。鉴于此,这些有问题的FE Pod中的集群成员很可能已过期,导致它们向终止或未就绪的BE Pod发送申请。

集群成员身份不统一且是旧数据:

咱们发现tcpKeepalive选项在检测终止的Istiod Pod中起作用。在咱们的Istio设置中,将keepaliveTime,keepaliveProbes和keepaliveInterval别离设置为默认值300秒,9秒和75秒。从实践上讲,Envoy可能须要至多300秒加9,再乘以75秒(16.25分钟),能力检测到终止的Istiod Pod。咱们通过将tcpKeepalive选项自定义为更低的值来解决了这个问题。

建设大规模的Kubernetes集群具备挑战性,并且对大家来说十分有意义。咱们心愿你从咱们的教训中找到有用的信息。

PS: 本文属于翻译,原文