可用性:理论、挑战、工具与最佳实践

本文深入探讨分布式系统的核心特性——可用性,涵盖理论定义、测量方法、常见问题、关键工具(如复制和自动故障转移)以及最佳实践,帮助构建高可用系统。

可用性:理论、挑战、工具与最佳实践

可用性是衡量系统在部分组件故障时保持运行能力的关键指标。今天,我将探讨分布式系统的这一核心特性,涵盖理论、挑战、工具和最佳实践,确保系统在任何情况下都能保持运行。

什么是可用性?

可用性描述了系统如何处理故障,并决定了系统的正常运行时间。通常,我们用“九”的表示法来描述系统可用性。99%的可用性保证每天最多14.40分钟的停机时间,而99.999%(即五个九)将这一时间减少到846毫秒。

大多数云服务为最终用户提供三九(99.9%)到五九(99.999%)的可用性保证SLA。

可用性 (%) 每天停机时间 (~) 每月停机时间 (~) 每年停机时间 (~)
90 144分钟 (2.4小时) 73小时 36.53天
99 14分钟 7小时 3.65天
99.9 1.5分钟 44分钟 8.77小时
99.99 9秒 4.4分钟 52.6分钟
99.999 846毫秒 26秒 5.3分钟
99.9999 86.40毫秒 2.6秒 31.5秒

此外,术语“高可用性”或HA用于描述至少具有三九可用性保证的服务。

可用性和一致性之间存在著名的权衡。通常认为,在故障情况下,我们只能选择其中之一。尽管在大多数情况下这是正确的,但整个主题更加 nuanced 和复杂。例如,CRDTs 对这一说法提出了质疑;Google内部的Spanner也是如此。

此外,我们可以使用各种技术来平衡这两个特性。系统可能在某些地方偏爱一个特性,而在其他地方则不然。只需记住:这种权衡存在,并且是分布式系统研究中最重要的案例之一。

如何测量可用性

可用性可能是最简单的测量特性,至少对于单个服务而言。您可能已经在某个仪表板中有了正常运行时间或停机时间指标。只需将该值除以:24(小时)、1440(分钟)、5184000(秒)。瞧,您就有了服务的每日正常运行时间百分比,并且可以轻松看到达到了多少个九。

当我们的服务有多个依赖项,或者我们想要测量整个系统的可用性时,情况变得更加复杂。

例如,考虑服务A有两个依赖项:DB和Email Service。

  • 服务A的正常运行时间为99.99%。
  • DB的正常运行时间为99.9%。
  • Email Service的正常运行时间为99%。

因此,服务A的可用性不是99.99%,而是98.89%。0.9999 × 0.999 × 0.99 = 0.9889 => 98.89%。

以更易读的格式:

组件 SLA (九) 可用性 (小数)
前端API 99.99% 0.9999
数据库 99.9% 0.9990
邮件服务 99% 0.9900
复合A 0.9999 × 0.9990 × 0.9900 = 0.9889 → 98.89% 0.9889

虽然最终差异不大,但它清楚地说明了这一点。服务的可用性不是独立的,而是所有依赖项的乘积。

同样的原则适用于系统。整个系统的可用性是其所有服务和工具的乘积。即使一个可用性较差的组件也可能导致整个系统崩溃。

最弱环节 能达到的最佳产品
99% (两个九) < 99%
99.9% (三个九) < 99.8%
99.99% (四个九) < 99.96%

以下是关于如何构建可用性相关指标的快速说明:

层级 在可用性上下文中的示例
SLI(指标) http_request_success_ratio = 成功请求数 ÷ 总请求数
SLO(目标) http_request_success_ratio ≥ 99.95% over 30 days
SLA(协议) “我们保证99.9%的月可用性;否则,您将获得服务积分。”

系统可用性差的迹象

我们可以注意到一些行为,表明我们的服务存在可用性问题。此外,其中一些与可扩展性差的迹象相似。

  • 低正常运行时间百分比——最明显的迹象,直接显示服务已关闭,用户无法访问。
  • 服务“抖动”——服务在启动和关闭之间振荡,自动重启或故障转移反复切换服务。
  • 健康检查失败——在正常负载下持续探针超时,意味着服务已关闭或即将关闭。
  • 高平均恢复时间——中断持续数小时,团队才能解决并恢复系统。
  • 流量突然降至零——服务已关闭或用户放弃连接尝试。
  • 直接反馈——重要客户致电CTO/CIO(或其他任何人)抱怨一切已关闭,警报开始旋转,以及其他有趣事件。

可用性的游戏改变者

在我看来,可用性的游戏改变者是自动且优雅的故障转移。虽然听起来简单,但实际上更复杂。为了实现它,我们需要结合多个不同的概念并使它们协同工作。尽管如此,它对于提供零停机体验至关重要。

最先进的零停机故障转移的解剖:

阶段 发生了什么 典型目标时间
1. 检测 健康探针检测到异常(5×超时/60秒)。 ≤ 5秒
2. 决定 编排器将节点标记为不健康,停止调度。 ≤ 1秒
3. 重定向 负载均衡器从池中移除端点;粘性会话迁移。 ≤ 2秒
4. 恢复 替换pod/VM启动并通过就绪检查。 ≤ 40秒 (热备: ≈ 0秒)

当然,自动故障转移不是银弹,也有缺点。两个最显著的是设计复杂性和成本增加。冗余导致成本增加,而故障转移本身增加了复杂性。

这听起来可能不好,但不幸的是,没有这样的机制,我们将无法提供高可用性。

可用性工具

我已经将自动故障转移作为构建可用系统的关键工具。然而,这些并不是唯一的概念。还有更多,您可以在下面找到它们。

复制

复制是实现冗余的一种方法。关键区别在于冗余影响我们系统的所有层,从软件到硬件。而复制主要与数据层相关。

我们提供同一数据集的多个最新副本,通常分布在多个节点上。因此,如果其中一个节点发生故障,数据仍然对用户可用。

有两种主要类型的复制:

  • 单主/单领导者——只有一个副本节点处理传入写入——领导者。其余节点提供读取访问,可用于卸载部分传入流量。领导者将更改传播到其他节点,通常使用某种类型的预写日志(WAL)。如果领导者节点因某种原因发生故障或变得不可用,则进行领导者选举过程,并从运行中的节点中选择新的领导者。
  • 多主/多领导者——所有节点同时接受读取和写入。然后将写入传播到其他节点。这种情况下最大的问题是相同的写入操作可能同时出现在两个不同的节点上。因此,需要单独的冲突解决机制。

复制的概念非常广泛。对这两种方法的良好遍历和比较超出了本文的范围。然而,我承诺在单独的文章中深入探讨复制。

现在,请记住下表:

单主 多主
只有一个节点接受写入 多个节点接受写入
通过WAL传播 冲突解决和传播

自动故障转移

自动且优雅(用户不可察觉)的故障转移机制是可用性的关键。

良好的自动故障转移,我们需要至少结合三个概念:

  • 冗余——我们需要多个节点才能开始考虑构建任何故障转移。
  • 健康检查——我们需要正确定义健康检查,以检测节点是否关闭或不应处理用户请求。
  • 负载均衡器/实际故障转移——我们需要一种方法来更换故障组件并将流量重定向到运行中的组件。

每个部分单独都不足够;所有部分必须协同工作。

隔离故障

提高系统可用性的另一种方法是隔离故障。通过这样做,我们可以确保一个组件的故障不会导致同一处理流程中其他组件的级联故障。

与本段中的大多数概念一样,没有单一的工具或方法来实现这一点。相反,我们可以遵循以下模式之一。我们也可以混合不同的模式。

让我们深入了解它们:

  • 断路器——现存最常见的微服务模式之一。它以类似于电路断路器的方式实现快速失败概念。如果在特定时间段内对其他服务的多次连续调用失败,断路器将切换。然后,在超时期间,所有调用该服务的尝试将立即失败。从而减少可能故障服务的负载,并给它时间恢复。还避免了在流程的其他阶段引入潜在超时。
  • 隔舱——根据这种模式,我们系统中的组件和资源应该被分隔。分区应以组件不共享任何资源的方式进行。例如,每个分区应有自己的线程池、连接池以及CPU或内存限制。这种拆分将减少一个组件过度使用(高资源利用率)并影响系统中其他组件的可能性。
  • 错误内核——我们将系统分为两种类型的组件,核心和辅助组件。核心组件绝不能因任何原因失败。辅助组件可能会失败,我们应该能够轻松重启它们。然后我们可以将辅助组件移动到系统的“外围”。因此,我们最终得到可靠的核心和易于重启的叶组件。

多区域或多云部署

多可用区或多区域部署将保护我们免受最意想不到的故障类型。那些会 wipe out 整个数据中心或位于特定区域的多个数据中心的故障。如法国OVH数据中心的燃烧或爱荷华州GCP的电气问题。

我们可以更进一步,构建多云故障转移。如果您的核心云提供商关闭,您可以切换到备份。虽然这给系统增加了大量额外的复杂性,但它进一步 drastically 降低了系统范围故障的概率。区域范围的故障本身就很罕见。提供商范围的故障更罕见。然而,两者都可能发生。能够处理它们可能不会决定99.99%和更低活力层级之间的差异。

然而,能够处理此类事件有一些优势:

  • 除了在其他人关闭时保持存活。
  • 表明您的架构有多好。

混沌工程/故障注入

混沌工程本身实际上不会帮助您构建可用系统。相反,它帮助您确保您的系统实际上是可用的。通过引入 deliberate 和可跟踪的故障,您可以识别在其他情况下不会出现的弱点和问题。我也在这里提到了这个概念。

只需记住它并不完全安全,并双重检查您的系统是否能够处理它。

为什么我们未能实现高可用性

在了解了什么、如何和为什么之后,是时候了解为什么我们失败了。在我看来和经验中,有几个因素导致我们在构建可用系统时失败。

一些原因将与我的可扩展性文章中的相同。

  • 忽略权衡——我们做出的每个决定都有短期和长期的后果,我们必须意识到。当然,我们可以忽略它们;但仍然,我们必须首先了解它们,并有意识地知道为什么我们忽略一些潜在的缺点。
  • 不正确的健康检查——它们反应太慢或太快。过早或过晚重启服务增加了用户经历故障的可能性。
  • 缺乏冗余——关键组件没有正确配置冗余。
  • 设计不良的故障转移——我们无法足够快地将流量重定向到运行中的节点。

以下是如何增加不失败于可用性的机会的简单清单:

今天做什么 影响
为每个组件添加健康检查。 30分钟的工作减少了部署/故障转移期间的502错误。
跟踪可用性产品 使隐藏的单点 painfully 明显。
设置书面SLO 使团队对齐“足够好”的含义。
运行故障转移演练。 在实践中检查您的设计。

总结

我分享了许多构建高可用系统的概念和方法。

让我们快速回顾一下关键要点:

  • 制作高可用系统需要混合不同的概念,如:冗余、健康检查和故障转移。
  • 适当的健康检查将帮助您跟上组件的状态。
  • 隔离故障并防止其传播将保持系统运行,即使某些组件发生故障。
  • 多区域部署将在最意想不到的时刻拯救您。

这里讨论的一些概念无法使用单一工具实现。它们需要架构思维和跨堆栈层的协调。

概念 工具
复制 通常是您使用的数据库产品的一部分
自动故障转移 K8s探针、云自动缩放产品
故障隔离 Resilience4j、K8s命名空间
多可用区 云提供商可用区

高可用性不仅仅是一个指标——它是一种心态。

为失败而构建。监控一切。并将可用性视为一等特性。

祝您在可用性斗争中好运。感谢您的时间。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计