团队规模的无服务器
构建无服务器应用程序的一大优势在于它非常容易上手。您只需在您选择的框架中编写五行 yaml 文件,即可部署一个完整的端点,其中包含 API 网关、路由、Lambda 函数和触发器、IAM 角色和策略,甚至日志记录功能。最棒的是,它具有极强的可扩展性。您无需部署负载均衡器、了解反向代理的工作原理,也无需学习如何设置系统日志。您只需推送代码即可!
但不知何故,我们的许多serverless.yml
文件看起来更像这样:
CloudFormation 太棒了!说真的,这篇文章并没有贬低 CloudFormation。它功能太多了!它可以管理您正在配置的服务状态,了解如何解决云组件之间的依赖关系,并提供可靠、确定性的基础设施配置方式。然而,随着无服务器使用量的增加,您通常会发现应用程序的复杂性、规模和后续部署时间都会迅速增长。
这主要是因为您的应用程序消耗或配置了大量的 AWS 资源。这本身并非坏事,无服务器的理念是优先选择托管服务而非定制解决方案!然而,您经常会发现您的模板包含大量资源。部署这些模板时,CloudFormation 需要构建一个有向无环的资源图,并在继续之前检查每个资源的配置。这需要很长时间,在某些情况下甚至需要 20 分钟以上。这些部署时间非常具有挑战性,因为绝大多数(> 90%)的部署只会涉及应用程序的实际业务配置(Lambda 函数代码)!
随着业务的增长,您也需要使用无服务器技术来解决新的业务问题。这会导致新的堆栈出现,并且经常带来一个问题:如何在无服务器堆栈之间共享基础设施?更具体地说,就是那些经常共享的基础设施,例如 cognito 池、VPC 或 API 网关。您甚至可能会遇到可怕的“每个 CloudFormation 堆栈 500 个资源”错误!
虽然使用插件将堆栈拆分成更小的堆栈很诱人,但这只会增加部署时间(更不用说整体复杂性了)。现在是时候拆分堆栈了,但正确的答案不是通过编程方式拆分一个巨大的堆栈。答案是有意地将基础架构与代码分离。
要成功扩展您的无服务器团队和部署,您需要将基础设施与代码分开,使用 CloudFormation 输出来共享资源,并利用 SSM 来保持非 CloudFormation 工具(如 Terraform)同步。
在云中划分并征服您的业务领域
具体来说,将您的长期和/或共享基础设施与您经常更改的资源分开。
经常变化的资源一般有:
- Lambda 函数代码和配置
- API 网关配置(除非您使用共享网关或单 lambda 模式)
- DynamoDB 表(除非它是共享的,但我通常不推荐这样做)
长寿命基础设施包括:
- Cognito 配置
- VPC
- API 网关(如果它们是共享的,或者您正在使用 mono-lambda 模式)
- EventBridge
- SNS 主题
- Kinesis 制作人
这里的关键点是将你共享的、长期的、缓慢变化的基础设施,从特定领域、经常更新的业务逻辑和配置中分离出来,形成一个独立的技术栈。这将减少逻辑变更带来的认知开销,限制 bug 的传播范围,或许对像我这样的开发者来说最重要——大幅缩短部署时间。如果你还没有读过我关于如何进一步缩短部署时间的文章,可以看看《云端开发》。
警告信号表明你的筹码量太大
如果你发现自己违反了迪米特的基础设施法则,那么你就需要将基础设施拆分成新的堆栈了。也就是说,运行你业务逻辑的 Lambda 函数与你在同一堆栈中配置的基础设施之间的距离要大于一跳。
除此之外,还有其他几种代码异味,当你闻到这些味道时,就会提醒你是时候将基础架构与代码分离了。它们分别是:
- 您的 serverless.yml 文件超过几百行
- 你的堆栈中不仅有 lambda 函数、dynamo 表,甚至还有基于 DDB 流的函数
- 您正在尝试决定是否应该将新的业务域添加到堆栈中,纯粹是因为现有堆栈中设置了数据库表、存储桶或 VPC
使用CloudFormation Outputs是一种简洁且可扩展的跨堆栈资源共享方式。Outputs 允许您在同一 AWS 账户内跨堆栈共享任意原语。您可以在代码块中声明这些原serverless.yml
语resources
:
resources:
# Okay, now we're writing raw CloudFormation
Resources:
Outputs:
PersonasTableArn:
Description: The ARN for the Persona's Table
Value:
'Fn::GetAtt': [personasTable, Arn] # This could be a string, but I'm using a GetAtt as that seems more realistic
Export:
Name: ${self:service}:${opt:stage}:PersonasTableArn # Export name must be *unique* across all outputs for a region. This name is what you'll import in other stacks
然后,您将使用Export
另一个堆栈中的名称来使用以下语法的输出${cf:output_name}
(此处有文档):
provider:
environment:
PERSONAS_TABLE_ARN: ${cf:OtherService:${opt:stage}:PersonasTableArn}
您可以在此处了解有关跨堆栈引用的更多信息。这里有一份针对无服务器框架项目的更详细参考。
关于 Terraform 和其他非 CloudFormation 系统的说明
将无服务器与现有的 IaC 工具(例如 Terraform)进行协调也很常见。如果您确实使用 Terraform,我建议您遵循上述相同的原则。也就是说,依靠 Terraform 来配置长期运行且很少更改的基础设施。然后使用 SAM、无服务器框架或任何您喜欢的工具来实现快速代码部署。
Terraform 不依赖于 CloudFormation。相反,它提供将状态存储在大量后端(通常是 S3 存储桶)中,这意味着您需要手动共享资源标识符,而不是使用 CloudFormation 输出。我发现最简单的选择是依赖 AWS Systems Manager (SSM)。
如果您使用 Terraform 来配置类似 Cognito 池或共享 SQS 队列之类的服务,则需要使用Terraform SSM 参数资源将 ARN 或名称发布到 SSM 中。然后,您就可以通过${ssm:...}
引用在无服务器应用中使用它们。
有时,您会发现需要将资源从CloudFormation 堆栈共享到Terraform 项目。在这种情况下,我建议您尝试使用serverless-ssm-publish 插件。这将允许您在部署基础架构堆栈后发布到 SSM,然后在 Terraform 中使用它们。
混合使用两者(通常使用 SSM 来维护云状态)的缺点是没有依赖关系解析。必须小心确保 Terraform 项目在无服务器应用中使用数据之前将数据推送到 SSM,反之亦然。
无论您选择将整个 IaC 配置保留在 Serverless、CloudFormation、Terraform 还是其他任何环境中,超过一定规模后,您都需要将基础架构与应用逻辑分离。这将降低每次部署的认知开销,限制部署影响范围,并缩短至关重要的部署时间。
有兴趣了解更多吗?你可以随时在 Twitter 上找到我。
鏂囩珷鏉ユ簮锛�https://dev.to/aws-builders/serverless-at-team-scale-a8