关于docker:使用Kubernetes和Istio构建大型集群的挑战和解决方案

11次阅读

共计 4148 个字符,预计需要花费 11 分钟才能阅读完成。

为了更好地治理一直增长的服务和流量,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: 本文属于翻译,原文

正文完
 0