本文介绍了去年我在 AWS 上基于 Serverless Framework 构建一个生产级项目所获得的经验。虽然并未事无巨细列出所有内容,但这个话题本身就有很多最佳实践值得深思熟虑并为此撰文。希望本文能对你有所帮助,帮你节约一些时间,减少一些麻烦。
1. RDS 和 Lambda 很复杂
要将 Lambda 函数连接到 RDS 数据库,可行的方法有两种:
RDS 位于 VPC 中,但对所有 IP 开放:
这会造成安全弱点。其本质在于设置 VPC 安全组允许任何入站 IP 地址连接你的数据库(假设客户端有正确的用户名和密码)。这会导致 RDS 实例在面对互联网上肆虐的爬虫这种自动化攻击措施时门户大开。如果缺乏足够的技术和保护能力,最好别这样做。
RDS 位于 VPC 中,Lambda 位于同一个 VPC 中:
虽然更复杂,但这才是正确做法。对于新手,你的所有函数必须包含与 RDS 集群共享的恰当子网和安全组:
随后需要确保为 Lambda 提供了 ENI 访问。ENI 的分配存在一些硬性限制,在将任何 Lambda 放入 VPC 前必需妥善考虑。最终的权限应该类似这样:
对于在 VPC 中运行 Lambda,会遇到一些有关冷启动和延迟的问题。在某些安全设置下,这些问题不可避免,因此你需要确保仅将必要的 Lambda 放入 VPC 中,并尽可能使用缓存来避免不必要的 ENI 分配或 DB 连接。
注意:出于简化方面的考虑,这里避免了有关在配置中包含 NAT 网关这种情况的讨论。NAT 网关会产生巨大的成本,配置起来也更为复杂。有关 NAT 配置的详情,请参阅这里的介绍。
2. 确保你的 Lambda 附加了正确的角色
我们曾多次犯下这个简单的错误,随后才开始习惯于分配函数级别的权限。如果未能给函数添加正确的权限,Serverless Framework 在部署或打包阶段并不会给出任何错误信息。因而随后只能翻查海量 CloudWatch 日志来判断到底哪里出错了。请养成良好习惯:部署任何新函数之前,务必要检查 iamRoleStatements。
3. IAM 角色名称最多只能用 64 个字符
这条原则含义显而易见,但在 sls deploy 过程中却很不容易发现问题。对于函数和 Stage 的名称一定要妥善规划。
4. CloudFormation 的资源上限为 200
如果不使用 Serverless Framework,解决方案也很简单,在 CloudFormation 中创建嵌套的栈即可。嵌套栈可以在函数层面或域实体层面组织成为一个函数。具体怎么做完全取决于你的偏好。
截止撰写本文时,还不能(很轻松地)直接在 serverless.yml 文件中配置嵌套栈,而是需要借助插件。对于一些非常成熟的服务,如果包含多个相互穿插的依赖项,可能还无法直接由这样的插件来处理。如果预计到你的无服务器项目所用资源数量会大幅增加,那么请尽可能提前做好规划。
5. 可将多个事件附加给同一个 Lambda
如果有多个应用程序客户端访问同一个 API,那么这将是一种很棒的解决方案。客户端 A 会有自己的 Lambda 授权方(Authorizer),客户端 B 会使用另一个授权方。例如可以考虑这样的场景:我们可以允许“机器到机器”客户端(通过客户端凭据)访问面向客户端的应用程序所关联的路由(通过暗含的或明确指定的授权代码批准)。这种做法的另一个优势在于,可以用最少的工作量为不同客户端应用不同的 API 网关设置(如缓存、跟踪、错误代码、请求限制)。
6. 自定义授权方需要手工禁用缓存
目前尚不明确这是否是 Serverless framework 的 Bug,但设置 resultTtlInSeconds: 0 并不能正确生效。你需要手工访问 AWS 控制台并禁用对应的复选框。这一点很重要,因为你肯定不希望自己的授权方将基于之前的请求所创建的拒绝/允许策略缓存起来。
7. Serverless.yml 可以动态配置
在构建任何规模的无服务器应用时,你也许能用最快速度学到这样一则经验:将所有配置值用硬编码的方式实现是一种难以为继的做法。该框架其实提供了几个选项,可供你动态地配置 serverless.yml。
{opt:stage, self:provider.stage} — 可供我们指定一个 Stage 作为值,并且其默认值等同于 serverless.yml 中 Provider 一节设置的默认 Stage。我们目前就使用这种方法动态地指向相应帐户内特定 Stage 对应的资源。
8. 为重要端点使用“热身”插件
冷启动并不是笑话。我们曾经测试发现,对于 VPC 中的 Lambda,很多时候热身工作大约需要 20 秒(20,000 毫秒)以上的时间。虽然我的同事 Yan Cui 曾经撰文提到过不要在 VPC 中使用 Lambda,不过在某些架构中,这样的做法不可避免,除非你打算将自己的数据库直接暴露至互联网。
重要的端点可能是完成页面加载或提供高性能服务所必需的任何端点。虽然每个应用程序的具体情况各不相同,但在我们的环境中,也包括了在着陆页(Landing page)上查询产品信息的端点。
我们使用了这个热身插件。
9. AWS SDK 也是 Lambda 运行时的一部分
AWS 在自己的文档中并未提及这一点,但需要提醒大家,不要将该 SDK 包含在自己已部署的依赖项中,这会导致整个部署的体积迅速激增。
10. 单独打包,不要包含测试文件
我们 sls deploy 的 Zip 文件非常庞大。对于包含约 20 个 Lambda 的一个服务,最终压缩之后的整个部署文件的体积约为 15GB。因此我们很快就碰到了 Lambda 75GB 的软性限制而不得不申请服务扩容。
尽管扩容后的上限提高到 150GB,但我依然开始调查这些软件包到出了什么问题。
此时可以通过两个操作让部署文件的体积实现数量级的“瘦身”:
首先最简单的方法是在 Webpack 配置中进行文件排除。这样可以避免所有测试文件进入到最终构建中。
随后我单独打包了所有函数。这一做法真正让软件包实现了数量级的瘦身,并且远远低于 150GB 的限制。
图片: https://assets.smcdn.cn/docs/assets/paste_error_placeholder-b6c71c3140.png
原文链接:
https://blog.launchfirstagency.com/ten-things-about-serverless/
评论