Istio在HelloFresh生产环境中的应用实践(二)


第一篇文章里,我们介绍了HelloFresh生产环境中为顺利上线Istio采取的一些步骤方法。而在本篇中,我们将更深入地来探讨一些技术细节以及我们在使用Istio时遇到的一些问题。

扩大支持基础架构

我们在增加部署规模的同时,也不得不扩大Istio的控制平面以及增加监控的力度。因为每增加一个新的Istio sidecar都会给Istio控制平面带来更多的负载。当Pilot连接到istio-proxy时,istio-proxy会针对每一个请求(去掉一些缓存)使用Mixer进行检查并输出报告,所以这些控制平面组件变得非常的关键。我们把namespace迁移到Istio之前,都已经部署了监控还有调整了控制平面。这么做是为了在继续增加负载之前,能够给我们提供一个环境健康检查。另外,我们还得定期去调整每个控制平面组件的副本数量,CPU以及内存的使用限制。Istio 1.5版本中重建了控制平面,将原有的多个组件整合为一个单体结构:Istiod,降低复杂度和维护难度的同时,也让易用性得到提升。但是原有的多组件并不是被完全移除,而是在重构后以模块的形式整合在一起组成Istiod,所以控制平面在概念上依然适用的。

除了控制平面,我们还得关注下每个istio-proxy的内存消耗量。虽然说istio-proxy属于比较轻量型的组件,但是如果运行数千个istio-proxy必然会造成较高的内存占用。尽管我们的Kubernetes集群能够自动扩缩容,但是在计算运行服务网格的成本和性能的开销上面,还需要考虑到istio-proxy内存消耗情况。

同样地,所有生成的日志、指标以及跟踪信息都会给Graylog,Jaeger和Prometheus这些监控组件带来额外的负载。因此,在将更多的namespace加入Istio之前,我们要对用于保存日志的组件、Jaeger、Cassandra、Prometheus Pod做一次扩容。

启动顺序(2nd Class Sidecars)

我们在启动Istio之后,发现有些应用程序在启动时会立即崩溃,然后在重启之后又恢复正常。一开始让我们一头雾水,但是很明显这现象只出现在启动时进行出站请求的服务上。后来我们发现这是因为在istio-proxy容器准备就绪之前,应用程序容器就已经启动了,所以导致了出站请求失败。这跟在Kubernetes中不能保证sidecars容器的执行顺序有关。为了缓和这个问题,我们先让应用程序启动失败,然后在istio-proxy就绪时依靠Kunbernetes重新启动它。虽然这不是理想的解决方案,但是我们迫切等待这个问题解决的一天。

定制镜像

一旦弄清楚应用程序启动的问题,我们就会在关闭节点时遇到相反的问题。当Pod收到进程终止信号时,envoy能够立即关闭,但是sidercar应用程序依然保持着连接。我们通过自制proxy-v2镜像来缓解这个问题,该镜像会先等待打开的连接耗尽之后再关闭istio-proxy。

自动扩容

我们目前使用Kubernetes HPA(Horizontal Pod Autoscaling,HPA)Pod水平自动伸缩,用于在负载高的场景下(比如,正在进行新的营销活动期间)自动缩放某些Pod。在部署并启动Istio之后,我们发现HPA无法按照预期的情况进行扩展,甚至在某些情况下根本无法扩展。

HPA基于CPU利用率来自动伸缩时,会使用到一个Pod的所有CPU请求之和。将istio-proxy sidecar添加到Pod,我们可以更改CPU和内存的请求总量,从而来有效的横向扩展。举个例子,比如已经将HPA的target设定为 70%【即HPA将通过增加或者减少Pod副本的数量(通过Deployment)以保持所有Pod的平均CPU利用率在70%以内】,Pod的request定义为100M,所以缩放目标值为70M(1000.7=70M)。假设Istio默认request也为100M。因此,现在注入istio-proxy,扩容的目标值为140M((100M+100M) 70%),这个值可能永远也无法达到。因为我们发现,istio-proxy在我们环境中CPU消耗约10M,即使额外多消耗10M且再加上前面应用容器的70M,这个结果(70M+10M)依然远低于140M的目标值。

我们通过计算得到正确的缩放指标值并将HorizontalPodAutoscaler设置为targetAverageValue来解决这个问题。

指向多网关的外部DNS

以前我们使用nginx-ingress来处理Kubernetes的入口流量。移至Istio之后,为了能够使用Gateway对象,还必须得删除Ingress。我们还运行多个不同的Istio Gateway控制器,并使用external-dns来管理53条路由。启用了Istio之后,将立即创建网关对象的DNS记录,但会以随机顺序指向错误的Istio网关控制器。好在,现在这个问题已经解决了。

Istio网关日志

我们希望Istio网关能够像使用nginx-ingress一样生成访问日志。虽然这样可以使访问日志的网格范围更广,但是这样会让日志基础结构处于超负荷的状态。我们提出了以下解决方案,使用程序、实例和规则来实现相同的结果。此规则将来自istio-ingressgateway的流量配置发送到我们的入口访问记录器处理程序,在那里它被kube-gelf接收并存储在Graylog中。
图片_2.png

蝴蝶效应

运行服务网格的好处之一是它允许以统一的方式控制分布式体系结构,一次可以控制数百个sidecars,缺点是一旦配置错误将会使整个服务网格都受到影响。在使用Istio的几个月中,我们发现即使是很小的Istio全局配置也会在上游其他地方产生重大的连锁反应。在一个实例中,一次小的全局配置更改导致Istio Pilot将新配置推到1000个连接的istio-proxy。当代理加载此配置时,每个代理消耗的内存比平时多大约75mb。虽然75mb听起来并不多,但是当在同一工作节点上有数十个已连接的istio-proxy时,这种突发的临时高峰导致了我们的某些Kubernetes工作节点缺少可用内存,从而影响了节点上运行的Pod。我们在部署到生产环境之前就抓住了这一点。
图片_3.png

这里的经验是通过观察任何配置的更改可能对istio-proxy产生的影响,例如CPU或内存,以及更改后对下游的影响,例如Kubernetes工作节点上的可用内存和CPU。

Sidecar资源限制

如果你拥有大型的Kubernetes基础架构的使用机会,那么还将会使用到external-dns,cluster-autoscaler,Prometheus等辅助服务。在默认情况下,Istio将为Kubernetes集群中的所有服务配置路由,集群和端点。很多时候,面向消费者的服务不需要与这些辅助服务直接通信,因此不需要它们具有任何Envoy/Istio的配置。因此我们使用Sidecar资源来限制推送到每个Envoy的配置数量,并且它具有减少每个Envoy的内存占用量的额外好处。
图片_4.png

观察外部服务

虽然Istio非常适合观察网格内部的情况,但是如何连接到网格外部的服务呢?其实完全不用担心,Istio提供了Service Entry资源来帮助我们解决此问题。我们为网络之外的流量以及外部的供应商设置了服务条目(Service Entry)。这样可以在一个中央仓库中管理服务条目以及其他Istio资源,所有团队都可以看到该仓库。
图片_5.png

添加额外的功能

第1篇里提到过我们在选择功能上首先关注可观察性和可靠性。但这并不意味着我们会忘记Istio提供的所有其他功能。一旦掌握了核心基础知识,我们便可以将注意力转移到内部团队要求更多的功能上。由于我们增加了实现核心功能的曝光度和舒适度,因此在短时间内我们就可以启动一些更令人兴奋的功能,例如推出全局自动mTLS,速率限制和基于路径的重试。
图片_6.png

完善监控

启用Istio后使我们能够完善在监控中缺失的支柱。我们一直都有日志记录和指标,但是在应用程序中很少使用跟踪。注入istio-proxy sidecar使我们能够填补缺失的示踪缝隙并完成可观察性图片。生成所有跟踪和指标后,我们的SRE团队便可以为所有Istio服务设置服务水平目标和错误预算。
图片_7.png

结论

Istio的旅程并非没有挑战,但与任何新兴技术一样,Istio也在不断改进。我们与Istio的合作还远远没有结束,我们仍在学习它,并探索Istio可以如何帮助改善我们的客户体验。我们希望你可以借鉴我们的一些经验,并为你的客户做同样的事情。

原文链接:Everything We Learned Running Istio In Production — Part 2(翻译:胡建鑫)

0 个评论

要回复文章请先登录注册