- 前言 -
数十年来,IT 业界一直在努力掌握分布式系统。然而,随着系统日益复杂,给开发数字产品的组织带来巨大挑战。可以说,分布式系统最棘手的方面之一是面对故障时的可靠性,特别是现代分布式系统使用大量物理与虚拟资源,包括网络、计算及存储时。
从低级网络协议和 Web 早期阶段起,分布式系统历经重大转变,演化为面向服务的架构(SOA)以及近来的微服务。随着云计算加速分布式系统成长,其分布层级显著提升。功能即服务(FaaS)与边缘计算将计算和处理代理数量拓展至数百万。
正如 L.Peter Deutsch 等人在《分布式计算的八大谬误》中指出,假定网络永远可靠是谬误。即便最基础的云服务也高度依赖网络。这凸显了 Deutsch 的见解在 90 年代首次提出后至今的持续相关性,在云和边缘计算时代,这些观察结果更为关键。
基于单元的架构成为应对分布式系统众多挑战的良策。其一,它运用隔板模式将故障隔离至受影响基础设施的一小部分空间内,防止产生广泛影响。但这并非全部价值。单元还能将大型架构组织成域绑定的部署与交付单元,提供社会技术优势。
在 “基于单元的架构:如何构建可扩展和弹性系统” 系列文章中,旨在呈现这种模式为现代分布式架构带来的益处,引领读者踏上探索之旅,对基于单元的架构的关键方面进行全面概述与深入剖析,并给出应用于现有和新架构的实用建议。
- 现代分布式系统新法宝:基于单元的架构 -
对于软件开发人员来说,适应增长是横亘在每一位开发者面前的巍峨山峰,无论在小型初创公司还是大型企业,当评估如何交付新产品或功能之际,系统应如何稳妥地应对持续攀升的负载,这一问题无可避免地浮现出来。
与此同时,构建和运营现代分布式系统的挑战会随着规模和复杂性的增加而不断加剧。云中或本地的基础设施资源可能出现意外且难以排除的故障,架构组件需要处理这些故障才能提供所需的可用性。
▏整体式架构、微服务与弹性挑战
几年前,微服务及其相关架构开始流行,因为它们有助于解决整体式应用程序 (monorepo) 面临的一些扩展挑战。这些应用程序可能不支持足够的并发或分区,因此会达到可伸缩性限制,从而导致性能和稳定性问题。随着这些整体式应用程序的增长,在本地环境中使用它们变得更具挑战性,应用程序的部署也日趋复杂,使得开发团队的前行脚步极为缓慢。
微服务使团队能够单独处理、部署和扩展服务,从而帮助缓解这些问题。但是,与大多数事情一样,没有什么是没有缺陷的,微服务也有其自身的挑战。
一个是微服务架构非常精细,直到单个服务级别。因此,开发团队将缺乏了解他们拥有的各种微服务在更广泛的系统上下文中的使用位置。了解其他感兴趣的团队的所有权下存在哪些微服务也将更具挑战性。
随着微服务架构变得越来越复杂,这些挑战只会随着时间的推移而变得更加突出。此外,随着云基础设施的广泛采用,许多公司现在管理着大量的云资源,从计算到存储再到网络和支持服务。这些资源中的任何一个都可能遇到故障,从而导致服务轻微或显著降级,并且尽管使用了冗余和故障转移机制,但如果不采取特殊措施,则无法完全包含某些故障模式。
▏基于单元的架构的重新出现
与故障隔离相关的挑战并不新鲜,也不特定于微服务或云。一旦软件系统变得分布式以适应不断增长的负载要求,由于其分布式特性,必须考虑许多新的故障模式。
基于单元的体系结构最早出现在面向服务的体系结构 (SOA) 时代,旨在管理大型分布式系统中的故障并防止它们影响整个系统的可用性。Tumblr、Flickr、Salesforce 或 Facebook 等公司的初始实施旨在将故障的冲击半径限制在客户或用户群体(分片)的一小部分,使用自包含的单元作为并行化单元来管理基础设施和应用程序资源并隔离故障。
首先,基于单元的架构是 Bulkhead 模式的实现,这是软件工程从造船业采用的一种想法。舱壁是船舶结构中的水密垂直隔板,可防止在船体破裂时水淹没整艘船。
多年来,隔板模式一直被宣传为现代架构(尤其是微服务)的关键弹性模式之一。然而,采用率一直很低,主要是由于额外的复杂性,因此大多数公司选择将工作重点放在其他地方。
一些知名公司最近选择重新审视基于单元的架构方法,以满足其基于微服务的云托管平台的高可用性要求。Slack 在因 AWS 可用区网络故障而经历部分中断后,已将其大部分面向用户的关键服务迁移到使用基于单元的方法。Doordash 通过其基于 Envoy 的服务网格实施了区域感知路由,转向基于可用区的单元架构并降低了跨可用区数据传输成本。反过来,Roblox 正在将其基础设施重新安排为单元,以便在继续扩展时提高效率和弹性。
这些公司的共同点是,它们在云或私有数据中心的大型基础设施资产上运行微服务架构,并且由于基础设施或应用程序故障的无限冲击半径,它们经历了严重的中断。作为回应,他们采用了基于单元的架构,以防止故障导致大范围中断。
AWS 长期以来一直是基于单元的架构的采用者和传播者,并在 2018 和 2022 的年度 re:Invent 大会上介绍了该架构,还在 2023 年 9 月发布了一份关于基于单元的架构的白皮书。
▏基于单元的架构的构建块
概括地说,基于 cell 的体系结构由以下元素组成:
-
单元 - 提供故障边界的独立基础设施/应用程序堆栈;负责处理应用程序工作负载
-
控制平面 - 负责预置资源、部署应用程序服务、确定路由映射、提供平台可观测性、移动/迁移数据等。
-
数据平面 - 负责根据数据放置和单元运行状况(由控制平面确定)适当地路由流量
为了提供容错优势,基于 Cell 的架构旨在支持控制平面和数据平面之间的单元级隔离和低耦合。请务必确保数据平面可以在没有控制平面的情况下运行,并且不应直接依赖于控制平面的运行状况。
▏Cell 作为一流的架构结构
采用基于 cell 的架构提供了有趣的优势组合。首先,Cells 在基础设施级别提供故障边界,其中 Cell 实例被指定为服务于特定流量段,将故障隔离到用户或客户群的子集。但是,它们也提供了一个机会,可以将相关的应用程序服务分组到特定于领域的集群中,从而有助于架构和组织结构,促进高内聚和低耦合,并减轻工程团队的认知负担。
对于小型系统或开始基于单元的架构采用工作时,完全有可能拥有一个包含所有应用程序服务的单个单元。对于具有许多应用程序服务的大型系统,可以使用多个单元来沿域边界组织体系结构。这种方法可以帮助大型组织采用产品思维方式,并使系统架构与产品域和子域保持一致。这对于由数百个微服务组成的大型微服务系统尤其重要。
从容错角度来看,单元(或单元实例)是一个完整、独立的基础设施堆栈,包括它运行和为指定流量段(由单元分区策略确定)提供工作负载所需的所有资源和应用程序服务实例。尽可能多地分离细胞以控制故障至关重要。理想情况下,Cell 应该独立于其他 Cell,不共享任何状态或像数据库一样具有共享依赖项。任何单元间通信都应保持在最低限度;理想情况下,应避免同步 API 调用。相反,应使用异步、消息驱动的数据同步。如果无法避免 API 交互,则必须通过 cell router,这样基于 cell 的架构的故障隔离特性就不会受到影响。
关于单元部署选项的许多考虑因素包括选择单个或多个 DC(数据中心)部署并确定最佳单元大小。一些组织采用基于单元的架构和单个 DC 部署,其中单元实例的所有基础设施和应用程序资源都位于单个数据中心或可用区中。这种方法可以最大限度地减少多 DC 部署的灰色故障的影响,并简化运行状况监控(信元运行状况良好)。另一方面,如果使用得当,多 DC 部署可以在 DC 级故障时提供弹性,但运行状况监控变得更具挑战性。
单元大小调整还可以在管理故障影响和管理基础设施成本方面发挥重要作用。使用较小的cell 可以减少影响范围 (更少的用户/客户),提高资源利用率 (由于较高的 cell 占用水平而减少空闲资源),并限制将流量段重新路由到其他 cell 所需的工作。但是,如果 cell 大小太小,则可能会给为特别大的 client/客户提供服务带来挑战,因此 cells 应该足够大,以满足基于分区键的最大流量段。
另一方面,单元越大,资源方面的规模经济就越大,这意味着更好的产能利用率。运营团队可能更容易管理更少的单元数量。此外,对于较大的单元大小,需要注意基础设施限制,例如云提供商平台的区域和账户级别限制。
▏用于管理基于 Cell 的架构的控制平面
采用基于单元的架构需要付出大量努力来开发超过支持常规微服务架构所需的管理能力。除了基础设施和应用程序服务的供应和部署之外,基于 Cell 的架构还需要额外的功能,专门用于管理和监控 Cell、在可用 Cell 之间划分和放置流量,以及在 Cell 之间迁移数据。
基于 cell 的架构的主要考虑因素是如何在 cells 之间对流量进行分区,这应该使用面向 cell 的方法为每个域单独确定。制定最佳分区方案的第一步是选择分区键。在大多数情况下,这最终可能是用户或客户标识符,但应针对每种情况单独做出选择,同时考虑流量分段的粒度,以避免分段大于所选的单元格容量。
有许多方法可以实现 cell 分区的映射,它们各自的优点和缺点。这些方法包括完全映射(其中存储所有映射记录)和使用一致的哈希算法,这些算法为存储桶提供相当稳定的项目分配,并在添加和删除存储桶时最大限度地减少客户流失。无论选择哪种映射方法,提供 override 功能以启用对某些分区键的特殊处理并帮助测试活动都是很有帮助的。
次要考虑因素是新用户/客户加入或预置新单元格时的单元格放置策略。该策略应考虑每个单元的大小和可用性容量,以及可能发挥作用的任何云提供商配额/限制。当达到 cell-capacity 阈值,并且需要一个新信元来容纳进入平台的流量时,控制平面负责配置新信元并更新信元映射配置,以确定数据平面的应用程序流量路由。
与上述内容相关的是数据迁移功能,这对于 cell 放置(如果需要分区重新洗牌)或事件期间(如果 cell 变得不健康并需要排空)非常重要。就其本质而言,从技术角度来看,数据迁移非常具有挑战性,因此此功能是提供基于 Cell 的架构最困难的方面之一。相反,在不同单元中的数据存储之间迁移或同步底层数据为数据冗余和故障转移开辟了新的可能性,进一步提高了采用基于单元的架构提供的弹性。
▏用于路由应用程序流量的数据平面
尽管控制平面负责管理架构,但数据平面可靠地移动流量数据。在基于 cell 的架构上下文中,这意味着将流量路由到适当的 cells,这由分区映射记录确定。需要强调的是,路由层需要尽可能简单且可水平扩展,并且应避免复杂的业务逻辑,因为数据平面是单点故障。
路由层实施可以采用从 DNS 和 API 网关到部署在通用计算或基于容器的执行平台上的定制应用程序服务的解决方案。无论哪种情况,分区映射数据都必须可用于从可靠的数据存储(可能是高度可用的分布式数据库或 blob 存储服务)中读取。路由层可以支持同步 API 调用(HTTP 或 GRPC)和异步消息,尽管后者的实现可能更具挑战性。
考虑到数据平面在单元之间的流量流中的关键作用,它可以实施安全策略,以确保单元内的服务仅提供授权的 API 请求。因此,可以实施一系列安全机制来防止未经授权的访问,包括 OAuth 或 JWT、用于身份验证的双向 TLS 以及用于授权的 RBAC 或 ABAC。
▏使用基于单元的架构的好处
采用基于单元的架构的主要好处是通过故障隔离提高了弹性。单元提供故障隔离边界,并减少问题的影响,例如部署失败、客户端滥用产品/平台、操作员错误或数据损坏。
使用单元还有助于提高系统的可扩展性。理想情况下,应限制单元的大小以减少故障的冲击半径,这也使单元成为扩展平台的良好单元。随着工作负载随着时间的推移而增加,可以预置更多单元来满足新流量(新客户/用户)。限制单元格大小可降低任何非线性缩放因子或意外争用点(性能瓶颈)导致意外的风险。
同样,单元可用于部署范围。组织可以在将更改推广到更广泛的用户/客户群体之前,使用范围限定为单元(因此是用户/客户子集)的金丝雀部署,而不是在任何地方推出新版本的服务。
尺寸上限单元非常适合量化系统的性能,因为与扩展单元内的组件相比,更容易测试单个单元的性能并建立系统的可扩展性特征。
Cells 提供了对属于同一子域或界定上下文的服务进行分组的额外好处,这可以帮助组织将团队和部门边界与产品域边界保持一致。这对于大型组织尤其相关,因为其中有数十或数百个团队构建和运营大型产品组合。
最后一个潜在的好处可能是通过减少跨可用区流量来节省成本,但这应该与与在数据层面内运行路由层相关的任何其他运营成本进行权衡。
▏采用基于单元的体系结构的注意事项
虽然基于单元的架构在分布式系统的上下文中提供了许多优势,但实现这种方法需要额外的努力并带来挑战,因此它可能并不适合每个组织,例如仍在迭代产品市场适合度的投资的初创公司。与微服务架构一样,基于单元的架构需要在底层平台上进行大量投资,以便让这种架构加快而不是阻碍团队的速度。
考虑到大多数具有重要基础设施足迹的公司都可能面临过去促使其他公司采用基于单元的架构的挑战,因此可能仍然值得评估基于单元的方法是否值得追求。
首先,任何由于声誉、财务或合同要求而根本无法承受大范围中断的公司都应该强烈考虑采用基于单元的架构,如果不是全部,至少对于面向用户的关键服务。
此外,任何需要或希望实现低恢复点目标 (RPO) 或恢复时间目标 (RTO) 的系统也应考虑基于单元的方法。最后,需要在租户级别进行严格基础设施级隔离的多租户产品可以从基于单元的架构中受益,以提供完全专用的租户功能。
无论如何,都应该考虑采用基于单元的架构的总成本,并与预期收益进行平衡,以确定预期的投资回报。
- end -