您想接收有关新内容的通知吗?
作者:Becky Weiss 和 Mike Furr
在 Amazon,我们构建的服务必须满足极高的可用性目标。这意味着我们需要仔细考虑我们的系统所具有的依赖关系。我们对系统进行设计以保持弹性,即使这些依赖关系受损,也不会受到干扰。在本文中,我们将定义一种名为静态稳定性的模式,我们将使用这种模式实现此等水平的弹性。我们将向您展示如何将此概念应用于可用区,这是 AWS 中的关键基础设施构建块,因此是构建我们所有服务的基础依赖关系。
在静态稳定的设计中,即使依赖关系受损,整个系统仍能继续工作。系统可能看不到其依赖关系本应提供的任何更新信息(如新内容、删除的内容或修改的内容)。然而,虽然依赖关系已受损,但在依赖关系受损之前所执行的一切工作都将继续发挥作用。我们将介绍如何构建 Amazon Elastic Compute Cloud (EC2) 以保持静态稳定。然后,我们将提供两个静态稳定的示例架构,我们发现这些架构对于在可用区之上构建高度可用的区域系统非常有用。
最后,我们将深入探讨 Amazon EC2 背后的一些设计理念,包括如何在软件级别提供可用区独立性。此外,我们将讨论使用这种架构构建服务时要进行的一些权衡。
静态稳定性
• 它从 VPC 子网中分配网络接口。
• 它准备 Amazon Elastic Block Store (EBS) 卷。
• 它生成 AWS Identity and Access Management (IAM) 角色凭证。
• 它安装安全组规则。
• 它将结果存储在各种下游服务的数据存储中。
• 它根据需要将所需的配置传播到 VPC 中的服务器和网络边缘。
• 它从 Amazon EBS 卷读取并向其中写入内容。
• 如此等等。
• 数据层面的运作容量通常要高于其控制层面(通常按数量级来计算)。因此,最好将它们分开,以便可以根据各自的相关扩展维度进行扩展。
• 多年来,我们发现,系统的控制层面往往比其数据层面具有更多的活动部件,因此仅从统计角度来说,它就更有可能因此原因而受损。
静态稳定性模式
在本节中,我们将介绍 AWS 中使用的两种高级模式,通过利用静态稳定来设计系统以实现高可用性。每种模式都适用于一组特定的情况,但都利用了可用区抽象。
如果出现可用区受损问题,上图中显示的架构不需要采取任何措施。受损可用区中的 EC2 实例将无法进行运行状况检查,Application Load Balancer 将从这些实例转移流量。实际上,Elastic Load Balancing 服务是根据这一原则设计的。它预置了足够的负载均衡容量,可以承受可用区受损,而无需进行扩展。
即使没有负载均衡器或 HTTPS 服务,我们也会使用此模式。例如,处理来自 Amazon Simple Queue Service (SQS) 队列消息的 EC2 实例队列也可遵循此模式。这些实例部署在多个可用区的 Auto Scaling 组中,并适当地进行超额预置。如果可用区受损,服务不会执行任何操作。受损的实例停止工作,而其他实例则会收拾残局。
与主动-主动无状态示例一样,当包含主节点的可用区受损时,有状态服务对基础设施将不起任何作用。对于使用 Amazon RDS 的服务,RDS 将管理故障转移并将 DNS 名称重指向到工作可用区中的新主节点。此模式也适用于其他主动-备用设置,即使它们不使用关系数据库。特别是,我们将此应用于具有集群架构(具有领导节点)的系统。我们在多个可用区中部署这些集群,并从备用候选节点中选择新的领导节点,而不是“及时”启动替换节点。
这两种模式的共同之处在于,在可用区受损时,它们都在产生任何实际影响之前预置了所需的容量。在这两种情况下,服务都不会特意采取任何控制层面依赖关系,例如,为应对可用区受损而预置新的基础设施或进行修改。
幕后原理:Amazon EC2 内部的静态稳定性
本文的最后一节将更深入地介绍弹性可用区架构,涵盖我们遵循 Amazon EC2 中的可用区独立性原则的一些方式。当我们构建的服务不仅需要自身具有高可用性,而且还需要提供其上的其他服务可以高度可用的基础设施时,了解其中一些概念很有帮助。作为低级别 AWS 基础设施的提供商,Amazon EC2 是应用程序可用于实现高可用性的基础设施。有时其他系统可能也希望采用这一策略。
我们在部署实践中遵循 Amazon EC2 中的可用区独立性原则。在 Amazon EC2 中,软件被部署到托管 EC2 实例的物理服务器、边缘设备、DNS 解析器、EC2 实例启动路径中的控制层面组件以及 EC2 实例所依赖的许多其他组件。这些部署遵循分区部署日历。这意味着同一区域中的两个可用区将在不同的日期收到给定的部署。在 AWS 中,我们采用分阶段部署方法。例如,我们遵循最佳实践(无论我们部署的服务类型如何),即首先部署整体,然后部署服务器的 1/N 等。但是,在特定服务情况下(例如 Amazon EC2 中的服务),我们的部署将更进一步,并特意与可用区边界保持一致。这样,部署问题将影响一个可用区,并会回滚和修复。它不会影响其他可用区,这些可用区将继续正常工作。
在 Amazon EC2 中构建时,我们使用独立可用区原则的另一种方法是将所有数据包流设计为处于可用区内,而不是跨越边界。第二点是网络流量保持在可用区本地,这点值得详细了解。这是一个有趣的插图,说明了我们在构建高可用性区域系统(该系统为独立可用区的使用者)时如何进行不同的思考(即,它使用可用区独立性作为构建高可用性服务的基础),这与我们向其他人提供独立于可用区的基础设施(允许他们针对高可用性进行构建)的情况相反。
下图介绍了一种高度可用的外部服务(以橙色显示),该服务依赖于另一种内部服务(以绿色显示)。简单的设计将这两种服务都视为独立 EC2 可用区的使用者。每种橙色和绿色服务都由 Application Load Balancer 进行领导而且每种服务都有一组妥善预置的后端主机,分布在三个可用区中。一种高度可用的区域服务可调用另一种高度可用的区域服务。这是一种简单的设计,对于我们构建的许多服务来说,这也是一种很好的设计。
但是,假设绿色服务是一项基础服务,亦即假设它不仅要具有高可用性,而且本身要作为提供可用区独立性的构建块。在这种情况下,我们可能会将其设计为区域本地服务的三个实例,并在此基础上遵循可用区感知部署实践。下图说明了高可用性区域服务调用高可用性分区服务的设计。
我们将构建块服务设计为独立于可用区的原因要归结为简单的算法。假设可用区已受损。对于黑白故障,Application Load Balancer 将自动从受影响的节点上进行故障转移。然而,并非所有的故障都如此明显。可能存在灰色故障(例如软件中的错误),负载均衡器无法在其运行状况检查中看到该错误,也无法进行彻底处理。
在前面一个高可用性区域服务调用另一个高可用性区域服务的示例中,如果通过系统发送请求,然后作出一些简单的假设,请求避开受损可用区的可能性为 2/3 * 2/3 = 4/9。也就是说,这一请求比避开事件的可能性还要低。相比之下,如果我们将绿色服务构建为分区服务(如当前示例中所示),然后,橙色服务中的主机可以调用同一可用区中的绿色终端节点。使用此架构,避开受损可用区的可能性为 2/3。如果此调用路径中包含 N 个服务,然后这些数字对于 N 个区域服务一般化为 (2/3)^N,而对于 N 个分区服务则保持不变,仍为常数 2/3。
因此,我们将 Amazon EC2 NAT 网关构建为分区服务。NAT 网关是一种 Amazon EC2 功能,它允许来自私有子网的出站 Internet 流量,它不是区域 VPC 范围的网关,而是作为分区资源,客户可按可用区单独实例化,如下图所示。NAT 网关位于 VPC 的 Internet 连接路径中,因此它是该 VPC 中所有 EC2 实例的数据层面的组成部分。如果一个可用区存在连接障碍,我们希望将该障碍控制在该可用区内,而不是将其传播到其他区域。最后,我们希望已构建类似本文上述架构的客户(即通过在三个可用区中预置一个在任意两个可用区具有足够容量的队列来承载全部负载)知悉这样一个事实,即其他可用区将完全不受受损可用区中发生的任何情况的影响。实现此目的的唯一方法是确保所有基础组件(如 NAT 网关)都确实处于可用区内。
这种选择会造成额外的复杂性成本。对于我们来说,在 Amazon EC2,额外的复杂性表现为管理分区而非区域服务环境。对于 NAT 网关的客户来说,额外的复杂性表现为拥有多个 NAT 网关和路由表,以便在 VPC 的不同可用区中使用。这种额外的复杂性是适当的,因为 NAT 网关本身是一项基础服务,是 Amazon EC2 数据层面的一部分,应该提供分区可用性保证。
在构建独立于可用区的服务时,我们还需要考虑一个因素,即数据的耐用性。虽然前面介绍的每个分区架构都显示了包含在单个可用区中的整个堆栈,但我们会在多个可用区中复制任何硬状态以实现灾难恢复。例如,我们通常在 Amazon S3 中存储定期数据库备份,并跨可用区边界维护数据存储的只读副本。主可用区不需要这些副本即可正常工作。它们可确保我们将客户或业务关键型数据存储在多个位置。
在设计将在 AWS 中运行的面向服务的架构时,我们已学会使用以下两种模式之一或两者的组合:
• 更简单的模式:区域-调用-区域。这通常是面向外部的服务的最佳选择,也适用于大多数内部服务。例如,当在 AWS 中构建更高级别的应用程序服务(如 Amazon API Gateway 和 AWS 无服务器技术)时,我们会使用此模式来提供高可用性,即使在可用区受损的情况下也是如此。
• 更复杂的模式:区域-调用-分区或分区-调用-分区。在 Amazon EC2 内设计内部(在某些情况下是外部)数据层面组件时(例如,直接位于关键数据路径中的网络设备或其他基础设施),我们遵循可用区独立性模式,并使用在可用区中孤立的实例,这样网络流量就会保持在同一可用区中。此模式不仅有助于将损害隔离到可用区,而且在 AWS 中具备有利的网络流量成本特性。
结论
在本文中,我们讨论了在 AWS 上使用的一些简单策略,以便成功地获取可用区的依赖关系。我们已经了解到,静态稳定性的关键要素是在产生损害之前进行预测。无论系统是否在主动-主动水平可扩展队列上运行,或者它是否为有状态的主动-备用模式,我们都可以使用可用区来实现高可用性级别。我们部署我们的系统,以便在产生损害时所需的所有容量都已完全预置并准备就绪。最后,我们深入了解了 Amazon EC2 本身如何使用静态稳定性概念来保持可用区相互独立。
关于作者
Becky Weiss 是 Amazon Web Services 的高级首席工程师。她目前在 AWS 主要从事 Identity and Access Management 方面的工作,并且通常专注于为客户在云中提供灵活、全面和权威的安全控制。过去,她曾从事 Amazon Virtual Private Cloud(即联网)和 AWS Lambda 方面的工作,还曾与 AWS 专业服务合作,帮助企业客户在 AWS 中成功保护其环境。Becky 恰好也是 AWS 的超级粉丝,在业余时间,她在 AWS 上构建了各种有用和无用的有趣事物。在 AWS 就职之前,Becky 曾为 Microsoft 工作,主要从事 Windows 和 Windows Phone 相关的工作。
Mike Furr 是 Amazon Web Services 的首席工程师。他于 2009 年加入 Amazon,当时他已在马里兰大学帕克分校完成计算机科学博士学位。在 Amazon 任职期间,他曾从事 Virtual Private Cloud、Direct Connect 以及 AWS 计量和计费堆栈方面的工作。他现在专门从事 EC2 方面的工作,帮助团队扩展云。