分布式系统会不可避免地发生些故障,我们需要计划好如何解决,其中有种方法是运行多个服务实例,这样即便有一个故障了,其他的可以继续接管。在本文中,我们将探讨一些在 Kubernetes 上实现此目标的不同方法。

None

冗余(Redundancy)是有代价的,我们在考虑弹性时就应想到这一点。当然,如果客户可以忍受少量中断,并且对他们的体验没有太大影响,那么这点就无所谓了。

在讨论服务运行时间(uptime)时,通常以几个 9 来评价,例如运行时间为 99.9%,这意味着每 1000 个请求中,只有一个失败。根据以往经验,我们每增加九个服务,需要花费约十倍的成本

只要应用程序不是经常崩溃,我们就可以运行一个 Pod 并依靠 K8s 重新运行,不过这需要一个 Pod 来处理服务接收的负载。

N

随着服务开始需要更多的 Pod 来处理负载,我们可以对其进行扩展。如果流量是随时间变化的(例如中午高峰),那我们要有足够的 Pod 在高峰时间处理负载。这种策略在 Pod 接收的流量会明显减少时,才能提供较好的弹性。

如果某个 Pod 在高峰时段发生故障,那么请求(request)将分布到其余 Pod 上,这样可能会超过它们的容量。不过在流量较小的话,其余 Pod 可能会有足够的容量来承受负载。

在讨论伸缩时,大家可能听过一些术语,例如 in、out、up 和 down。通常,up 和 down 意味着保持相同数量的实例,但增加或减少 CPU 或 Server 的内存。In 和 out 是增加或删除服务实例,但保持资源不变。明白这些后,我们可以进一步实现伸缩,但要注意会受到可使用的最大 CPU 或内存限制。

N+1

与 N 一样,我们要了解需要多少 Pod 来处理高峰流量,这次添加了一个额外 Pod 来为我们提供保护,以防止高峰期间出现 Pod 故障的情况。这种策略的弹性成本就是一个 Pod,这是额外的成本,并只在故障情况下才需要。这就是为什么即便有一个 Pod 可以处理所有流量,但我们仍要有一个额外 Pod。

伸缩

与其手动计算高峰时段所需的 Pod 数量,不如让 K8s 代为完成。有了伸缩指标(scaling metric),K8s 可以根据当前需求伸缩服务,在需求较低时缩小规模,在需要时扩大规模,这样就降低了成本。虽然伸缩本身并不能使 Pod 从故障中恢复过来,但是可以应对激增的需求。控制好 Pod 的内存和 CPU 可以使我们更精确地扩展规模并降低成本。

扩展服务需要留出一定的空间,最好不要将 Pod 用到极限。Pod 需要一些时间才能启动,它会自动计算自动伸缩指标。应用程序需要能够处理请求扩展与实际扩展之间的请求。另外,如果有很多 Pod,那么总的空闲空间会缩小,因为它分布在所有 Pod 中。

75% 伸缩

知道自动伸缩以及何时伸缩服务时,我们就可以控制所需的弹性。将服务容量伸缩到 75% 时,我们会损失 25% 的 Pod,拥有的 Pod 越多,损失的也就越多,同时我们还要为未利用的 Pod 支付大量费用。即便是这样,当我们运行着大量的 Pod 时,依旧要考虑降低弹性百分比,因为可能会出现大量 Pod 发生故障的情况。

如果服务的流量特别麻烦,这项策略就会特别有用。尖峰流量(spike)会对自动伸缩产生很大影响,因为它们出现的时间很短,以至于自动伸缩器没有时间做出反应。如果我们大致知道峰值是多少,那么就可以将其规划到服务的伸缩中。

伸缩 + N

如果想精确地控制冗余而不是某个百分比,那么可以选择扩展容量,再增加 N 中的额外 Pod。这样即便 N 的 Pod 故障,我们仍然有足够的容量,来精确控制冗余 Pod。

K8s Service 使用标签(labels)来决定路由哪些 Pod 的请求。这样我们可以使用不同配置部署同一 Service 的两个副本集,一个副本集使用水平 Pod 自动伸缩器,而另一副本集配置为具有 N 的 Pod。两个副本集使用相同的标签标记容器,并且 Service 将路由到该标签。该 Service 在所有 Pod 中平均分配流量,从而允许在扩展服务的同时维护 N 的冗余 Pod。

多集群

我们还可以将应用程序部署到两个 K8s 集群中,这样即便整个集群出现故障,都还能继续维护服务。负载均衡器在集群前面,并在集群之间路由流量。

运行多个集群时,自动伸缩也更具挑战性,因为伸缩指标通常不会在集群之间共享。如果集群发生了故障,仍在工作的集群将在几乎没有通知的情况下,接收来自故障集群的所有流量。有多种方法可以应对这种突然增加的负载,自动伸缩功能能迅速地做出反应并处理流量。根据所需的冗余程度,集群可以以“主动-被动”模式(active-passive mode)运行,这意味着集群可以接收所有请求,但除非在集群之间共享伸缩指标,否则在此设置中,服务必须从零开始扩展。

数量与冗余

拥有的 Pod 越多,单个 Pod 的故障对其他 Pod 的影响就越小。假设我们有十个可以服务 100rps 的 Pod,如果以 90rps 的比例伸缩并且有一个 Pod 故障,那么其余 Pod 要接收 100rps,并达到容量极限;如果我们有 20 个 Pod,其中有一个 Pod 故障,其余的 Pod 仅需处理 95rps。这两种情况都是假定服务能准确接收请求。实际上,服务通常会收到比这少的流量,如果扩大规模,收到的流量会稍多一些。

总结

自动伸缩是一个复杂的问题,有很多选择需要我们考虑。通过本文,希望大家对此有更好的了解,并且可以使用它来减少云账单成本,同时保持服务的正常运行。

原文链接:https://mp.weixin.qq.com/s/iiGFTGQ1ZKOBxrkLBQ1HQw