目录
Kubernetes Cookbook:构建云原生应用程序(作者:O'Reilly)
QCon 伦敦(2024 年 4 月 8 日至 10 日):采用正确的新兴趋势和实践。 立即注册并保存.
水平扩展的局限性和 DragonflyDB 垂直扩展模式的优点
要点
- 蜂窝架构可以为客户和企业带来显着的好处,例如提高可用性、弹性和工程速度.
- 自动化蜂窝基础设施需要解决关键问题:隔离、新单元创建、部署、权限和监控.
- 蜂窝架构依靠有效的请求路由和监控来实现高可用性目标.
- 自动化可以通过基础设施即代码(IaC)和构建管道来实现,并通过采用标准化应用程序组件来简化.
- 然而,没有一种放之四海而皆准的解决方案。 个人可以选择最适合自己需求的工具并确定自动化实施的水平.
什么是蜂窝架构?
蜂窝架构是一种有助于在多租户应用程序中实现高可用性的设计模式。 目标是设计您的应用程序,以便您可以将其所有组件部署到完全自给自足的隔离“单元”中。 然后,您创建这些“单元”的许多离散部署,它们之间没有依赖关系.
每个单元都是应用程序的一个完全可操作的自主实例,准备好为流量提供服务,而不依赖于任何其他单元或与任何其他单元交互.
来自用户的流量可以分布在这些单元中,如果一个单元发生中断,只会影响该单元中的用户,而其他单元仍保持完全运行。 这可以最大限度地减少您的服务可能遇到的任何中断的“影响范围”,并有助于确保中断不会影响大多数用户的 SLA.
相关赞助内容
-
Kubernetes Cookbook:构建云原生应用程序(作者:O'Reilly)
-
高性能数据架构模式白皮书
-
QCon 伦敦(2024 年 4 月 8 日至 10 日):采用正确的新兴趋势和实践。 立即注册并保存.
-
微服务监控简介——策略、工具和关键概念
-
水平扩展的局限性和 DragonflyDB 垂直扩展模式的优点
相关赞助商
您的云,得到简化. DoiT 提供技术和云专业知识,帮助您轻松购买、优化和管理 AWS、Google Cloud 和 Microsoft Azure. 了解更多.
有许多不同的策略可用于组织单元并决定将哪些流量路由到哪个单元。 有关蜂窝架构的优势以及这些不同策略的一些示例的更多信息,我们强烈推荐 Peter Voshall 在 re:Invent 2018 上的演讲:“AWS 如何最小化故障的爆炸半径".
管理具有许多独立单元的应用程序可能会令人畏惧。 因此,为创建和维护单元所需的常见基础设施任务构建尽可能多的自动化非常有价值。 在本文的其余部分中,我们将较少关注蜂窝架构的“原因”,而更多地关注创建这种自动化的“方式”。 有关“原因”的更多信息,请查看 Peter 的演讲以及文章末尾“其他资源”部分中的链接!
自动化您的蜂窝架构
在寻求蜂窝基础设施自动化的过程中,有五个关键问题需要解决:
- 隔离:我们如何确保细胞之间有明显的边界?
- 新细胞:我们如何使它们持续有效地上线?
- 部署:我们如何将最新的代码更改获取到每个单元中?
- 权限:我们如何确保单元安全并有效管理其入站和出站权限?
- 监控:操作员如何一目了然地确定所有小区的健康状况并轻松识别哪些小区受到中断的影响?
有许多工具和策略可用于解决这些问题。 本文将讨论 Momento 对我们行之有效的工具和解决方案.
不过,在解决这些具体问题之前,我们先谈谈标准化.
标准化
对应用程序中所有组件的构建/测试/部署生命周期的某些部分进行标准化,可以更轻松地围绕它们构建通用自动化。 通用化您的自动化将使在部署到每个单元所需的所有组件中重用基础设施代码变得更加容易.
重要的是要注意我们正在讨论 标准化, 不是 同质化. 大多数现代云应用程序都不是同质的。 您的应用程序可能包含在 Kubernetes、AWS Lambda 和 EC2 等平台组合上运行的五种不同的微服务。 要为所有这些不同类型的组件构建通用自动化,我们只需要标准化它们生命周期的几个特定部分.
标准化 - 部署模板
那么,我们需要标准化什么? 让我们考虑一下将代码更改推广到生产环境时通常涉及的步骤。 他们可能是这样的:
- 开发商 提交 版本控制存储库的代码更改.
- 我们 建造 具有最新更改的二进制工件; 这可能是 docker 映像、JAR 文件、ZIP 文件或其他一些工件.
- 神器是 已发表 或者 释放: docker镜像被推送到docker存储库,JAR文件被推送到maven存储库,ZIP文件被推送到云存储中的某个位置等.
- 神器是 已部署 到您的生产环境。 这通常涉及串行部署到蜂窝环境中的每个单元.
因此,对于我们应用程序的任何给定组件,这是我们希望部署过程的粗略模板:
图 1:最小部署模板
现在,蜂窝架构的目标之一是最小化中断的爆炸半径,而最有可能发生中断的时间之一是在部署后立即发生。 因此,在实践中,我们希望在部署过程中添加一些保护措施,以便在检测到问题时,可以停止部署更改,直到解决问题为止。 为此,添加一个我们可以首先部署的“临时”单元,以及在部署到后续单元之间的“烘焙”期是一个好主意。 在烘焙期间,我们可以监控指标和警报,并在出现任何问题时停止部署。 所以现在我们的任何给定组件的部署模板可能看起来更像这样:
图 2:具有“烘焙”阶段的部署模板
现在,我们的目标是推广我们的自动化,以便轻松实现这组部署步骤 对于任何应用程序组件,无论该组件基于何种技术构建.
许多工具可以自动执行上述步骤。 在本文的其余部分中,我们将使用一些基于我们在 Momento 选择的工具的示例,但您可以使用最适合您的特定环境的任何工具来实现这些步骤.
在 Momento,我们的大部分基础设施都部署在 AWS 中,因此我们倾向于使用 AWS 工具。 因此,对于在 EC2 上运行并通过 CloudFormation 部署的应用程序的给定组件,我们使用:
- 用于定义和执行阶段的 AWS CodePipeline
- 用于执行各个构建步骤的 AWS CodeBuild
- AWS Elastic Container Registry,用于发布组件的新 docker 映像
- AWS CloudFormation 用于将新版本部署到每个单元
- AWS Step Functions 可在“烘焙”步骤期间监控警报并决定部署到下一个单元是否安全
图 3:部署阶段实施 - CloudFormation 风格
对于基于 Kubernetes 的组件,我们只需稍加改动即可实现相同的步骤:我们使用 AWS Lambda 对 k8s 进行 API 调用,以将新映像部署到单元,而不是 CloudFormation.
图 4:部署阶段实施 - Kubernetes 风格
希望您能明白我们正在努力的方向; 尽管构成我们的应用程序的组件的技术堆栈存在差异,但我们可以为推出新更改所涉及的步骤定义一个通用模板。 然后,我们可以主要使用相同的工具链来实现这些步骤,并对具体步骤进行少量修改。 对所有组件的构建生命周期的一些事情进行标准化将使我们能够以通用方式构建这些步骤的自动化,这意味着我们可以重用大量基础设施代码,并且我们的部署将在所有组件中保持一致和可识别。我们的组件.
标准化 - 构建目标
那么,我们如何标准化各个组件的必要步骤呢? 一项有价值的策略是定义一些标准化的构建目标并在所有组件中重用它们。 在 Momento,我们为此使用了一种经过验证的技术:Makefile.
Makefile 非常简单,而且它们一直存在。 他们为这个目的工作得很好.
图 5:使用 Makefile 的标准化构建目标
在左侧,您可以看到我们的一个 Kotlin 微服务的 Makefile 的片段。 右侧是 Rust 服务的 Makefile 的片段。 构建命令非常不同,但重要的是,我们在这两个命令上都有相同的目标列表.
例如,我们有一个 管道构建 目标,它控制发生的事情 建造 该特定服务的部署过程的步骤。 然后,我们有一些“单元引导程序”和“GCP 单元引导程序”的目标,因为我们可以为 Momento 部署到 AWS 单元或 GCP 单元。 Makefile 目标名称相同; 这意味着在这些单独服务之外运行的基础设施的其他部分现在具有这个共同的生命周期,它们知道它们可以依赖于我们在执行操作时需要与之交互的每个组件内部的存在诸如部署之类的事情.
标准化 - 细胞登记
帮助我们标准化自动化的另一个构建块是“单元注册表”。 这只是一种机制,为我们提供我们创建的所有单元格的列表以及有关它们的基本元数据位.
图 6:单元注册表的 TypeScript 模型
在 Momento,我们用 TypeScript 构建了单元注册表。 我们有大约 100 行 TypeScript,它们定义了一些简单的接口,我们可以使用它们来表示有关单元格的所有数据。 我们有一个 CellConfiguration
界面,这可能是最重要的一个; 它捕获有关给定细胞的所有重要信息。 这是生产细胞还是发育细胞? 它位于哪个地区? 此单元中端点的 DNS 名称是什么? 它是 AWS 单元还是 GCP 单元?
我们还有一个 MomentoOrg
接口,其中包含一个数组 CellConfiguration
s。 所以,组织只是跟踪所有现有单元的一种方式.
使用这些接口提供的模型,我们可以编写更多的打字稿代码来实例化它们并创建有关单元格的所有实际数据。 这是该代码的一个片段:
图 7:“alpha”细胞的细胞注册数据
这就是我们的“alpha”单元的数据的样子。 我们有一个单元名称、一个帐户 ID、区域、DNS 配置等。现在,每当我们想要添加一个新单元时,我们只需输入此单元注册表代码并向此数组添加一个新条目.
现在我们已经拥有了有关细胞的所有数据,我们需要将其发布到某个地方,以便我们可以从基础设施的其余部分访问它。 根据您的需求,您可能会做一些复杂的事情,例如将数据放入可以查询的数据库中。 我们不需要任何复杂的东西,所以我们只需将数据以 JSON 形式存储在 S 上3.
单元注册表的最后一个组件是一个小型 TypeScript 库,它知道如何从 S3 检索此数据并将其转换回 TypeScript 对象。 我们将该库发布到私有 npm 存储库,并且可以从基础设施代码中的其他任何位置使用它。 这使我们能够开始在所有基础设施自动化中构建一些泛化模式; 我们可以循环所有单元并为每个单元配置相同的自动化.
标准化 - Cell Bootstrap 脚本
我们用来概括自动化的最后一个标准化是“单元引导脚本”。 将应用程序的所有组件部署到新单元可能非常具有挑战性、耗时且容易出错。 然而,单元引导脚本可以简化流程并确保从一个单元到下一个单元的一致性.
假设每个应用程序组件的源代码都位于 git 存储库中,那么,考虑到上面的构建块,引导新单元的逻辑就这么简单:
- 使用单元注册表查找该单元所需的所有单元元数据(例如,AWS 账户 ID、DNS 配置等)
- 对于每个应用程序组件:
- 克隆该组件的 git 存储库
- 运行标准化
cell-bootstrap
Makefile 中的目标
图 8:单元引导脚本
该脚本只需五行代码,即可为部署应用程序的新单元提供通用且可扩展的解决方案。 如果您向应用程序引入新组件,脚本仍然具有适应性,并确保简单且一致的部署过程.
把它们放在一起
现在我们已经定义了一些标准化的构建块来帮助我们组织有关单元的信息并概括应用程序组件的各种生命周期任务,现在是时候重新审视我们需要解决的五个问题的列表了,以自动化我们的基础设施我们所有的细胞.
隔离
确保 AWS 环境中单元之间隔离的最简单方法是为每个单元创建单独的 AWS 账户。 起初,如果您不习惯管理多个帐户,这可能看起来令人畏惧。 然而,如今AWS的工具已经非常成熟,比你想象的要容易.
作为默认状态,在专用 AWS 账户内部署单元可确保与其他单元隔离。 您必须为一个单元设置复杂的跨账户 IAM 策略才能与另一个单元交互。 相反,如果您将多个单元部署到单个 AWS 账户中,则需要设置复杂的 IAM 策略来 防止 细胞之间无法相互作用。 IAM 策略管理是使用 AWS 时最具挑战性的部分之一,因此任何时候您可以避免这样做,这将节省您的时间和麻烦.
使用多个账户的另一个好处是,您可以使用 AWS Organizations 将账户链接在一起,然后使用 AWS Cost Explorer 按单元可视化和分析您的成本。 如果您选择将多个单元部署到单个 AWS 账户中,则必须仔细标记与每个单元关联的资源以查看每个单元的成本。 使用多个帐户可以让您免费获得此内容.
图 9:每个单元账户的 AWS Cost Explorer 成本视图
与蜂窝架构密切相关的一项挑战是路由。 当您有多个独立的单元,每个单元都运行应用程序的副本时,您必须选择一种策略,将流量从用户路由到所需的单元.
如果您的用户通过 SDK 或您提供的其他客户端软件与您的应用程序交互,则将流量路由到各个单元的一种简单方法是为每个单元使用唯一的 DNS 名称。 这是我们在 Momento 使用的方法; 当我们的用户创建身份验证令牌时,我们会将该用户的相应单元的 DNS 名称作为声明包含在令牌内,然后我们的客户端库可以根据该信息路由流量.
但是,这种方法仅适用于某些用例。 如果您的用户通过 Web 浏览器与您的服务交互,您可能希望为他们提供一个可以在浏览器中访问的 DNS 名称,这样他们就不需要知道您的单元格。 在这种情况下,有必要创建一个薄路由层来引导流量.
图 10:用于单元隔离的路由层
布线层应尽可能小。 它应该包含最低限度的逻辑来识别用户(基于请求中的某些信息),确定他们应该路由到哪个单元,然后相应地代理或重定向请求.
此路由层提供了更简单的用户体验(用户不需要了解您的单元),但它的代价是您必须在架构中维护和监视新的全局组件。 它还会成为单点故障,而您可以通过使用蜂窝架构在很大程度上避免这种情况。 这就是为什么您应该努力使其尽可能小和简单.
不过,拥有这样一个路由层的一个好处是,它可以透明地将客户从一个单元迁移到另一个单元。 假设一个用户的增长超出了一个单元格的容量,并且您希望将他们移动到一个更大或不太拥挤的单元格。 在这种情况下,您可以准备新的单元供其使用,然后部署对路由逻辑/配置的更改,以重定向其流量,而无需他们知道发生了什么.
新细胞
如果您按照上面的标准化部分进行操作,您可能已经直觉到我们已经完成了大部分工作来解决如何创建新单元的问题。 我们需要做的就是:
- 在我们的组织中创建一个新的 AWS 账户
- 将该帐户添加到我们的单元注册表中
- 运行单元引导脚本来构建和部署所有组件
就是这样! 我们有一个新牢房。 因为我们标准化了每个组件的 Makefile 中的构建生命周期步骤,所以部署逻辑非常通用,几乎不需要付出什么努力就可以启动新的单元.
部署
对于任何应用程序架构来说,部署可能是最具挑战性的问题,对于蜂窝架构尤其如此。 值得庆幸的是,近年来,基础设施即代码工具的重大进步使得其中一些挑战变得更加容易应对.
在过去的几年里,大多数 IaC 工具使用声明性配置语法(例如 YAML 或 JSON)来定义您希望创建的资源。 然而,最近的趋势为开发人员提供了一种使用真实编程语言来表达基础设施定义的方法。 开发人员现在有机会利用熟悉的编程语言的表达能力和强大功能来定义基础架构组件,而不必费力处理复杂而冗长的配置文件。 此类新工具的示例包括:
- AWS CDK(云开发套件)- 用于部署 CloudFormation 基础设施
- AWS cdk8s - 用于部署 Kubernetes 基础设施
- CDKTF(CDK for Terraform)-用于通过 HashiCorp Terraform 部署基础设施
这些工具让我们可以使用类似的结构 for
循环消除数百行样板 YAML/JSON.
图 11:CloudFormation JSON 与 CDK TypeScript
用 TypeScript 这样的语言表达我们的基础设施的另一个非常强大的功能是我们可以使用 npm 库作为依赖项。 这意味着我们的 IaC 项目可以添加对单元注册表库的依赖项,从而访问包含所有单元元数据的数组。 然后,我们可以循环该数组来定义每个单元所需的基础设施步骤。 当添加新的单元并更新单元注册表时,基础设施也将自动更新!
尤其是AWS CDK和AWS CodePipeline的结合,功能极其强大。 我们可以使用通用模式为每个应用程序组件定义管道,并为每个组件设置必要的构建和部署步骤,同时共享大部分代码.
在 Momento,我们为可能需要添加到 AWS CodePipeline 的每种类型的阶段提供了一些 TypeScript CDK 代码(例如,构建项目、推送 docker 镜像、部署 CloudFormation 堆栈、将新镜像部署到 Kubernetes)集群等)我们可以将这些阶段一起推入一个数组中,然后循环它以将阶段添加到每个管道:
图 12:向 CodePipeline 添加阶段的 CDK 代码
我们创建了一个特殊的管道,称为“管道的管道”。 它是一个“元”管道,负责为每个应用程序组件创建单独的管道.
图 13:管道中的管道
该存储库充当我们所有部署逻辑的单一事实来源。 任何时候开发人员需要更改我们的部署基础架构的任何内容时,都可以在这一点完成。 我们对部署步骤列表所做的任何更改(例如更改单元的顺序或使“烘焙”步骤更加复杂)将自动反映在我们的所有组件管道中。 添加新单元时,管道中的管道将运行并更新所有组件管道,以将新单元添加到部署步骤列表中.
为了帮助改善我们的可用性故事,我们仔细考虑了向生产单元部署的顺序。 单元根据大小、重要性和流量级别组织成波。 在第一波中,我们部署到预生产单元,这些单元在升级到生产单元之前充当变更的测试场。 如果这些部署进展顺利,我们将逐渐部署到越来越大的生产单元。 这种分阶段部署方法可以控制变更的进展,并增加我们在问题影响许多客户之前发现问题的可能性.
权限
为了管理进出单元的权限,我们严重依赖 AWS 的 SSO(现在称为 IAM Identity Center)。 这项服务为我们提供了一个单点登录启动页面,我们所有的开发人员都可以使用他们的 Google 身份登录,然后访问他们有权访问的任何帐户的 AWS 控制台。 它还提供通过命令行和 AWS SDK 访问目标账户的权限,从而轻松实现操作任务的自动化.
管理界面提供对每个帐户内的用户访问的精细控制。 例如,在单元帐户中定义“只读”和“单元操作员”等角色,授予不同级别的权限.
图 14:AWS SSO 账户权限
将 AWS SSO 的角色映射功能与 CDK 和我们的单元注册表相结合意味着我们可以完全自动化每个单元账户的入站和出站权限.
对于入站权限,我们可以循环访问注册表中的所有开发人员和单元帐户,并使用 CDK 授予适当的角色。 当新帐户添加到单元注册表时,自动化会自动设置正确的权限。 我们循环访问注册表中的每个单元格以获得出站权限,并根据需要授予对 ECR 映像或私有 VPC 等资源的访问权限.
监控
监测大量细胞可能很困难。 拥有一个监控故事至关重要,以确保您的操作员可以在单一视图中评估所有单元的服务运行状况; 期望您的操作员查看每个单元帐户中的指标并不是一个可扩展的解决方案.
要解决此问题,您只需选择一个集中式指标解决方案,即可将所有单元帐户中的指标导出到该解决方案中。 该解决方案还必须支持按维度对指标进行分组,在本例中为您的单元格名称.
许多指标解决方案都提供此功能; 可以将多个帐户的指标聚合到中央监控帐户中的 CloudWatch 指标。 存在大量第三方选项,例如 Datadog、New Relic、LightStep 和 Chronosphere.
这是 LightStep 仪表板的屏幕截图,其中 Momento 的指标按单元尺寸分组:
图 15:指标仪表板,其中指标按单元名称分组
额外的好处
现在我们已经介绍了蜂窝架构如何帮助实现高可用性以及现代 IaC 和基础设施工具如何帮助您实现蜂窝基础设施自动化,让我们注意一下您可以从这种自动化中获得的一些额外好处.
一个关键的好处是能够非常快速地启动新细胞。 通过本文中描述的单元引导脚本,我们可以在数小时内从头开始部署一个新单元。 如果没有基础的标准化和自动化,此过程的大部分步骤都必须手动完成,并且很容易需要数周时间。 对于初创公司和小公司来说,能够灵活地快速添加新单元以响应新客户的询问可能是一个巨大的价值主张。 这可能会导致达成一笔大交易或错失良机.
另一个巨大的价值是开发人员能够在自己的开发帐户中创建个人单元。 有时,在没有真实环境的情况下,没有真正的方法来测试和调试依赖于多个服务或组件之间的交互的复杂功能.
一些工程组织会尝试使用共享开发环境来解决这个问题,但这需要开发人员之间的仔细协作,并且容易出现冲突和停机。 相反,通过我们的单元引导脚本,开发人员可以在一天内启动和拆除应用程序的完全可操作的开发部署。 这种敏捷方法可以最大限度地减少干扰并最大限度地提高生产力,使开发人员能够专注于他们的任务,而不会无意中影响其他人.
没有一种尺寸适合所有情况
在本文中,我们讨论了我们选择用于自动化蜂窝基础设施的几种工具和技术。 但需要指出的是,对于本文提到的任何给定技术,都有大量替代选择。 例如,虽然 Momento 使用多种 AWS 工具,但其他主要云提供商(例如 GCP 和 Azure)为每个相关目标提供类似的产品.
此外,您可以选择仅自动化我们选择自动化的一部分内容,或者您可以选择自动化超出我们此处概述的内容! 这个故事的寓意是,您应该选择对您的业务有意义的自动化级别以及最无缝地适合您的环境的工具.
概括
蜂窝架构可以让您的客户在可用性方面受益,并确保您达到 SLA。 它对于您的企业的敏捷性和工程速度也很有价值。 自动化这些流程只需要解决本文中提出的一些关键问题,并进行一些工作来标准化应用程序组件中的某些内容.
由于基础设施作为代码空间的变化,今天的自动化变得更加简单,只要您利用这些机会标准化有关如何定义组件的一些事情.