云计算中单租户与多租户的比较


在过去的几年中,我一直在构建一个具有单租户架构的多消费者应用程序。每个消费者都有一个独特的、独立的资源堆栈,从而实现简单性和较小的爆炸半径(半径?)。然而,我们正在达到可扩展性限制,并将很快转向多租户架构。

在这篇文章中,我们将介绍单租户架构的优缺点,以及单租户与多租户为何不是二元选择。

背景
我们讨论的应用程序是事件代理电子商务(EBE)。这个内部 PostNL 平台负责接收来自许多应用程序的事件,并将正确的事件路由到订阅它们的应用程序。

当我们开始开发这个应用程序时,我们选择了单租户架构。实际上,这意味着系统是一个唯一的 CloudFormation 堆栈,其中包含接收、验证和转发事件所需的资源。显然,这意味着环境中有大量的堆栈和大量的重复。

单租户优点
我们选择单租户架构的主要原因是开发速度。单租户本质上更易于理解和维护:您可以查看队列、函数或存储桶,并 100% 确定地知道其职责和范围涵盖单个租户。这些资源不存在意外连接到另一个租户的端点、访问另一个租户的资源或使用另一个租户的配置的风险。

单租户还允许我们避免复杂的路由逻辑:如果资源接收到事件,则只有一个处理路径。我们不需要基于发送者/接收者及其权限来构建和维护任何条件逻辑。

事实上,这允许我们创建在部署时配置过一次的无状态资源。配置是不可变的,并且资源没有外部控制平面依赖性。这在 Lambda 环境变量中得到了很好的可视化:每个消费者都可以有不同的 BACKOFF_RATE,并且它被简单地烘焙到每个单租户堆栈中。

我们选择单租户架构的第二个原因是部署的影响范围较小:我们可以更新单个租户的应用程序代码并验证其行为,然后再进行下一个租户。 “大爆炸”部署同时导致所有租户瘫痪的风险因此大大降低。

此外,单个行为不端的租户不太可能影响其他租户——单一租户最大限度地减少了吵闹的邻居问题。


单一租户的缺点
单一租赁方法使我们能够快速进入我们的(内部)市场。从那时起,我们能够根据用户的需求快速发展和调整我们的路线图。然而,我们也遇到了一些需要缓解的限制。这些限制大致可以分为两类:

  • AWS 账户中的最大资源数量
  • AWS 服务的最大并发操作数

IAM 角色的最大数量是我们首先遇到的硬限制之一。 AWS 账户不能拥有超过 5000 个角色,标准实践规定每个资源有一个唯一的角色(例如状态机/lambda 函数)。在 560 个堆栈中,这限制了每个堆栈最多 8 个 IAM 角色。当我们几乎达到这个限制时,我们必须巩固一些角色,但代价是最小特权原则。现在,一些 Lambda 函数共享相同的角色和权限,这有时意味着函数可以访问它不需要的资源。

并发操作的最大数量是一个更棘手的问题。当我们有应用程序更新时,我们会在少量金丝雀堆栈上验证功能。当它们健康时,我们并行更新大部分其他堆栈。当我们达到大约 100 层时,这导致了许多意想不到的副作用。其中包括并发 CodeBuild 容器的最大数量、API Gateway 的最大部署率、CloudFormation 上的最大并发堆栈操作数以及 Lambda 上每秒的最大控制平面操作数。

Lambda 速率限制尤其令人讨厌。我们能够同时更新 100 个 CloudFormation 堆栈,并且每个堆栈都有许多 Lambda 函数。同时更新它们超过了每秒 15 个 Lambda 控制平面请求的最大值,导致 CloudFormation UPDATE_FAILED 状态。然后,CloudFormation 将进入回滚模式,但回滚还会更新 Lambda 函数,从而导致额外的速率限制,并最终出现 UPDATE_ROLLBACK_FAILED 状态。去过那里的人都知道那种痛苦。

最后,我们通过将部署并发数限制为 50 来缓解所有这些问题。为了实现这一点,我们使用外部和内部 Step Functions 状态机。内部状态机执行实际的 CDK 部署和所有相关操作。外层状态机由外部系统触发,会首先检查内层状态机的实际并发情况。如果它高于 50,它将后退并重试,直到有可用的插槽。

虽然并发限制器解决了所有速率限制问题,但它显然会减慢部署速度。实际上,大型更新可能需要部署 500 个堆栈。最大并发数为50,因此部署大约需要10波。每波最多需要五分钟,因此完全重新部署可能需要近一个小时。

多租户解决方案
我们知道我们已经达到了单租户解决方案所能支持的上限。 IAM 角色是我们无法跨越的硬限制的明显示例。其他是 CloudFormation 堆栈的最大数量,或者我们愿意支付的 CloudWatch 警报和指标的数量。

如果单租户不再足够,那么多租户是合乎逻辑的下一步。

在多租户设计中,我们的客户不再拥有专用的基础设施。相反,单个入口服务负责接收所有事件,单个出口服务负责转发所有事件。

多租户解决方案的主要优点是:

  • 减少资源量
  • 降低达到并发限制的可能性
  • 提高部署速度

然而,它也带来了新的挑战:
  • 无法再在部署时配置服务。相反,他们需要在运行时通过推或拉机制检索最新配置,并将其保持在本地状态。
  • 爆炸半径显着增加,因为:
    • 部署
    • 安全
    • 节流(吵闹的邻居)
  • 服务的复杂性以及因此出现错误的风险都会增加。

一切都是权衡
任何关于架构的文章如果不讨论权衡都是不完整的,这篇也不例外。正如我们上面了解到的,单租户和多租户解决方案各有利弊。从纯粹的技术角度来看,多租户系统是首选方法。它们具有可扩展性并且副作用有限。事实上,每一项 AWS 服务都是一个多租户解决方案——没有其他方法可以实现它们的运营规模。

但我们并不都是 AWS,也不是每个系统都需要为 100 万用户设计。就我们而言,单租户使我们能够以最小的努力实现良好的隔离。此外,它还显着缩短了我们的上市时间,使我们能够尽快找到适合我们的产品。它已经把我们带到了现在的位置,现在是时候发展到下一个阶段了。

选择不是二元的
在本文中,我们提出了单租户架构与多租户架构的对比。这是该范围的两个极端:每个租户在一端都有自己的基础设施,而所有基础设施在另一端都是共享的。正如我们所讨论的,两端都有优点和缺点。多租户解决方案最显着的缺点是事件的潜在广泛影响。

幸运的是,也有一些中间的解决方案。这些架构平衡了多租户系统的可扩展性和与单租户系统相关的隔离性。示例包括区域、区域、分层或基于单元的架构。所有这些解决方案都遵循相同的基本方法;他们采用一个大型的多租户系统并将其分成更小的块。这些块仍然可以非常大,例如两个完全独立的区域,也可以非常小,例如数据中心中的给定机架甚至物理服务器。无论大小如何,这些较小的块中的每一个仍然需要是一个多租户系统,具有良好实施的隔离和一致性措施。

对于 EBE,我们正在考虑分层架构:第 3 层是内部租户,纯粹用于测试和验证。第 2 层包含非任务关键型租户,第 1 层包含任务关键型生产工作负载。每个层都是多租户服务,涵盖多种不同的租户配置。任何部署都会首先覆盖较高层,只有当它们被证明稳定时,才会将部署提升到下一层。结果是更小的爆炸半径和更好的隔离,但代价是稍微多一点的资源和更慢的部署时间。

结论
在本文中,我们介绍了单租户和多租户解决方案的优缺点。我们已经看到,

  • 单一租户是一个快速入门的好选择,但具有明确且稳定的增长上限。如果您知道您只需要处理有限数量的客户,那么单租户可能是一个很好的长期解决方案。
  • 多租户架构是需要无限规模的服务的首选解决方案。多租户是在云提供商设置的资源限制内为大量客户提供服务的唯一方法。然而,多租户系统本质上更加复杂,这会影响开发速度和运营开销。它们还增加了事故的潜在爆炸半径。

通过对多租户系统应用区域或蜂窝设计,可以减小爆炸半径。这将限制事件的影响,并使您的开发人员能够在问题影响最关键的系统之前发现问题。然而,它也增加了额外的复杂性,并减少了多租户带来的一些好处。

最后,哪种解决方案最适合您的应用程序完全取决于您的业务环境、应用程序生命周期的阶段以及利益相关者设置的优先级。我希望这篇文章能帮助您做出明智的决定。