延长 MTBF - 可用性及其他:了解和提高 AWS 上的分布式系统的韧性

延长 MTBF

提高可用性的最后一个要素是延长 MTBF。这一点既适用于软件,也适用于用于运行软件的 AWS 服务。

延长分布式系统 MTBF

延长 MTBF 的一种方法是减少软件中的缺陷。我们可以通过多种方式来实现这一目的。客户可以使用 Amazon CodeGuru Reviewer 等工具来查找和修复常见错误。在将软件部署到生产环境之前,您还应该对软件进行全面的同行代码审查、单元测试、集成测试、回归测试和负载测试。增加测试中的代码覆盖率有助于确保即使是不常见的代码执行路径也能得到测试。

部署较小规模的更改也可以降低更改的复杂性,从而帮助防止意外结果。每项活动都提供了在缺陷被调用之前识别和修复缺陷的机会。

防止故障的另一种方法是定期测试。实施混沌工程可以帮助测试工作负载如何发生故障、验证恢复程序,并有助于在生产环境中出现故障之前发现和修复故障。客户可以将 AWS Fault Injection Simulator 用作混沌工程实验工具。

建立容错能力是防止分布式系统出现故障的另一种方法。快速失效模块、采用指数回退和抖动的重试、事务和幂等性都是可以帮助工作负载获得容错能力的技术。

事务是具备 ACID 特性的一组操作。这些特性如下所示:

  • 原子性 — 所有操作要么全部发生,要么全部不发生。

  • 一致性 — 每个事务都让工作负载处于有效状态。

  • 隔离性 — 并发执行的事务会让工作负载处于相同状态,如同它们是按顺序执行的一样。

  • 持久性 — 事务提交之后,即使工作负载出现故障,其所有影响也会保持不变。

采用指数回退和抖动的重试可以克服由海森堡错误、过载或其他条件引起的暂时故障。当事务具有幂等性时,它们可以多次重试而不会产生副作用。

对于海森堡错误对容错硬件配置的影响,我们可以将其完全忽略,因为海森堡错误同时出现在主子系统和冗余子系统上的可能性微乎其微。(参见 Jim Gray,“Why Do Computers Stop and What Can Be Done About It?”,1985 年 6 月,Tandem 技术报告 85.7。) 在分布式系统中,我们希望通过软件实现同样的结果。

出现海森堡错误时,软件必须快速检测到错误的操作并快速失效,以便可以重试。这通过防御性编程以及验证输入、中间结果和输出来实现。此外,进程是隔离的,不与其他进程共享任何状态。

这种模块化方法可以确保故障的影响范围受到限制。进程会独立失效。当某个进程失效时,软件应该使用“进程对”来重试该工作,这意味着新进程可以承担失效进程的工作。为了保持工作负载的可靠性和完整性,每个操作均应被视为 ACID 事务。

这可以让进程的失效不会因为中止事务并回滚所做的任何更改而破坏工作负载的状态。这可以让恢复过程在已知良好状态下重试事务并正常重启。这就是软件容许海森堡错误的方式。

但是,您的目标不应该是让软件能够容许海森堡错误。您必须在工作负载进入生产环境之前就发现并消除缺陷,因为任何程度的冗余都无法实现正确的结果。(参见 Jim Gray,“Why Do Computers Stop and What Can Be Done About It?”,1985 年 6 月,Tandem 技术报告 85.7。)

延长 MTBF 的最后一种方法是缩小故障的影响范围。正如前面的容错能力和故障隔离部分所述,实现这一目标的主要方式是通过模块化来创建故障容器,从而实现故障隔离。降低故障率可以提高可用性。AWS 通过将服务划分为控制平面和数据平面、可用区独立性 (AZI)、区域隔离基于 cell 的架构随机分片等技术来提供故障隔离。AWS 客户也可以使用这些技术。

例如,假设工作负载将不同客户置于其基础架构的不同故障容器中,每个容器最多为 5% 的客户提供服务。其中一个故障容器中发生了一个事件,该事件让 10% 的请求延迟超过了客户端超时时间。在这次事件中,对于 95% 的客户来说,该服务具有 100% 的可用性。对于剩余 5% 的客户来说,该服务具有 90% 的可用性。这时的可用性为 1 − (5% 客户×10% 请求) = 99.5%,而不是 100% 的客户的 10% 的请求无效(可用性为 90%)。

规则 11

故障隔离可以降低总体故障率,从而缩小影响范围并延长工作负载的 MTBF。

延长依赖项 MTBF

延长 AWS 依赖项 MTBF 的第一种方法是使用故障隔离。许多 AWS 服务都可以在可用区内提供一定程度的隔离,这意味着一个可用区的故障不会影响另一个可用区的服务。

在多个可用区中使用冗余 EC2 实例可以提高子系统的可用性。AZI 可以在单个区域内提供备用功能,从而提高 AZI 服务的可用性。

但是,并非所有 AWS 服务都在可用区层面运行。许多其他服务可以提供区域性隔离。在这种情况下,如果区域性服务的设计可用性无法实现您的工作负载所需的总体可用性,则您可以考虑采用多区域方法。每个区域都对服务进行隔离的实例化,相当于创建备件。

有多种服务可以帮助简化多区域服务的构建。例如:

本文并未深入探讨构建多区域工作负载的策略,但您应该权衡多区域架构的可用性优势和实现所需可用性目标所需的额外成本、复杂性和运营实践。

延长依赖项 MTBF 的下一个方法是将工作负载设计为静态稳定。例如,您有一个提供产品信息的工作负载。当客户针对产品发出请求时,您的服务会向外部元数据服务发出请求以检索产品详细信息。然后,您的工作负载会将所有这些信息返回给用户。

但是,如果元数据服务不可用,您的客户发出的请求就会失败。因此,您可以将元数据异步拉取或推送到本地服务,用于回复请求。这样您就无需从关键路径同步调用元数据服务。

此外,因为您的服务在元数据服务不可用时仍然保持可用,所以您可以在计算可用性时删除这个依赖项。本示例假设元数据不会经常更改,并且提供过时的元数据要好于请求失败。另一个类似的例子是 DNS 的提供过时数据功能,它可以让数据在 TTL 到期后仍然保存在缓存中,并在不容易提供刷新后的应答时用于响应。

延长依赖项 MTBF 的最后一种方法是缩小故障的影响范围。如上文所述,故障不是一个二元事件,存在着不同的程度。利用模块化设计,我们可以将故障限制在故障容器所服务的请求或用户范围内。

这样可以减少事件期间的故障,从而通过限制影响范围来最终提高整个工作负载的可用性。

减少常见的影响源

1985年,Jim Gray 在 Tandem Computers 的一项研究中发现,故障主要源自两种事物:软件和操作。(参见 Jim Gray,“Why Do Computers Stop and What Can Be Done About It?”,1985 年 6 月,Tandem 技术报告 85.7。) 即使在 36 年之后,情况仍然如此。尽管技术取得了进步,但这些问题并没有简单的解决方案,而且故障的主要原因也没有改变。本节开头探讨了如何解决软件故障,因此这里的重点将放在操作和降低故障频率上。

稳定性与功能的比较

如果再看一次 分布式系统可用性 部分的软件与硬件故障率图表,我们可以发现软件的每个版本都会引入缺陷。这意味着工作负载的任何变化都会提高故障风险。这些变化通常是新功能之类的东西,会带来必然的结果。可用性更高的工作负载会更重视稳定性而不是新功能。因此,提高可用性的一种最简单的方法,就是降低部署频率或减少提供的功能。部署频率更高的工作负载的可用性天然低于比不频繁部署的工作负载。但是,不添加新功能的工作负载无法满足客户需求,并且作用会随着时间的推移而降低。

那么,我们如何继续创新并安全地发布新功能呢? 答案是标准化。正确的部署方式是什么? 如何确定部署顺序? 测试标准是什么? 不同阶段之间要等待多长时间? 单元测试是否涵盖了足够的软件代码? 标准化可以回答这些问题,并防止由于未进行负载测试、跳过部署阶段或过快地部署到太多主机等而引发问题。

实现标准化的方法是推行自动化。自动化可以减少人为错误,让计算机处理其擅长的任务,即以同样的方式反复执行同样的操作。将标准化与自动化结合的方法是设定目标。目标可以是不进行手动更改、仅通过临时授权系统访问主机、为每个 API 编写负载测试等。卓越运营是一种文化规范,可能需要进行重大变革。根据目标来确定和跟踪效果有助于推动文化变革,这将对工作负载的可用性产生广泛影响。AWS Well-Architected 卓越运营支柱针对卓越运营供了全面的最佳实践。

操作人员安全

导致故障的运营事件的另一个主要引发因素是人。人会犯错误。他们可能会使用错误的凭证、输入错误的命令、过早按回车键,或者错过关键步骤。始终采取手动操作会导致错误,从而引发故障。

造成操作人员错误的一项主要原因是用户界面混乱、不直观或不一致。Jim Gray 在 1985 年的研究中还指出,“要求操作人员提供信息或执行某些功能的界面必须简单、一致并能容许操作人员错误。” (参见 Jim Gray,“Why Do Computers Stop and What Can Be Done About It?”,1985 年 6 月,Tandem 技术报告 85.7。) 这一见解在今天仍然是正确的。在过去的三十年中,整个行业中有许多例子表明,混乱或复杂的用户界面、缺乏确认或说明,甚至只是不友好的人类语言就导致操作人员犯下了错误。

规则 12

让操作员可以轻松采取正确操作。

防止过载

最后一个常见影响来源是您的客户,即工作负载的实际用户。成功的工作负载往往会被大量使用,但有时这种使用量会超出工作负载的扩展能力。可能出现的情况有很多,比如磁盘已满、线程池耗尽、网络带宽饱和,或者达到数据库连接限制。

没有万无一失的方法可以消除这些故障,但是通过运行状况指标对容量和使用情况进行主动监控,我们就能在可能发生这些故障时提前收到警告。卸除负载断路器以及采用指数回退和抖动的重试等技术可以帮助我们尽可能减少影响并提高成功率,但这些都是发生故障之后采取的措施。基于运行状况指标的自动扩展可以帮助降低过载导致的故障的发生频率,但可能无法足够快地响应使用率的变化。

如果您需要确保客户具有持续可用的容量,则必须在可用性和成本之间做出权衡。要确保容量不足不会导致不可用,您可以为每位客户提供配额并确保扩展工作负载的容量,以便提供 100% 的分配配额。当客户超过配额时,他们会受到限制,这不是故障,也不会影响可用性的计算。您还需要密切跟踪您的客户群并预测未来的使用情况,以便预置足够的容量。这样可以确保您的工作负载不会因为客户过度使用而产生故障。

我们来看一个提供存储服务的工作负载。工作负载中的每台服务器可以支持每秒 100 次下载,为客户提供每秒 200 次下载的配额,共有 500 个客户。要支持这一数量的客户,该服务需要提供每秒 10 万次的下载容量,这需要 1000 台服务器。如果任何客户超出其配额,他们就会受到限制,这可以确保其他所有客户都有足够的容量。这是一个简单的示例,说明了一种在不拒绝工作单元的情况下避免过载的方法。