@liuhui0803
2019-08-21T09:57:50.000000Z
字数 2611
阅读 1317
无服务器
Node.JS
AWS
下文介绍了去年我在AWS上基于Serverless Framework构建一个生产级项目所获得的经验。虽然并未事无巨细列出所有内容,但这个话题本身就有很多最佳实践值得深思熟虑并为此撰文。希望本文能对你有所帮助,帮你节约一些时间,减少一些麻烦。
要将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配置的详情,请参阅这里的介绍。
我们曾多次犯下这个简单的错误,随后才开始习惯于分配函数级别的权限。如果未能给函数添加正确的权限,Serverless Framework在部署或打包阶段并不会给出任何错误信息。因而随后只能翻查海量CloudWatch日志来判断到底哪里出错了。请养成良好习惯:部署任何新函数之前,务必要检查iamRoleStatements
。
这条原则含义显而易见,但在sls deploy
过程中却很不容易发现问题。对于函数和Stage的名称一定要妥善规划。
如果不使用Serverless Framework,解决方案也很简单,在CloudFormation中创建嵌套的栈即可。嵌套栈可以在函数层面或域实体层面组织成为一个函数。具体怎么做完全取决于你的偏好。
截止撰写本文时,还不能(很轻松地)直接在serverless.yml
文件中配置嵌套栈,而是需要借助插件。对于一些非常成熟的服务,如果包含多个相互穿插的依赖项,可能还无法直接由这样的插件来处理。如果预计到你的无服务器项目所用资源数量会大幅增加,那么请尽可能提前做好规划。
如果有多个应用程序客户端访问同一个API,那么这将是一种很棒的解决方案。客户端A会有自己的Lambda授权方(Authorizer),客户端B会使用另一个授权方。例如可以考虑这样的场景:我们可以允许“机器到机器”客户端(通过客户端凭据)访问面向客户端的应用程序所关联的路由(通过暗含的或明确指定的授权代码批准)。这种做法的另一个优势在于,可以用最少的工作量为不同客户端应用不同的API网关设置(如缓存、跟踪、错误代码、请求限制)。
目前尚不明确这是否是Serverless framework的Bug,但设置resultTtlInSeconds: 0
并不能正确生效。你需要手工访问AWS控制台并禁用对应的复选框。这一点很重要,因为你肯定不希望自己的授权方将基于之前的请求所创建的拒绝/允许策略缓存起来。
在构建任何规模的无服务器应用时,你也许能用最快速度学到这样一则经验:将所有配置值用硬编码的方式实现是一种难以为继的做法。该框架其实提供了几个选项,可供你动态地配置serverless.yml
。
${file()}
运算符 — 你可以导入自行选择的配置文件(如secrets.json)并将其设置为自定义变量,随后就可以在serverless.yml
中灵活使用。例如:${opt:stage, self:provider.stage}
— 可供我们指定一个Stage作为值,并且其默认值等同于serverless.yml
中Provider一节设置的默认Stage。我们目前就使用这种方法动态地指向相应帐户内特定Stage对应的资源。冷启动并不是笑话。我们曾经测试发现,对于VPC中的Lambda,很多时候热身工作大约需要20秒(20,000毫秒)以上的时间。虽然我的同事Yan Cui曾经撰文提到过不要在VPC中使用Lambda,不过在某些架构中,这样的做法不可避免,除非你打算将自己的数据库直接暴露至互联网。
重要的端点可能是完成页面加载或提供高性能服务所必需的任何端点。虽然每个应用程序的具体情况各不相同,但在我们的环境中,也包括了在着陆页(Landing page)上查询产品信息的端点。
我们使用了这个热身插件。
AWS在自己的文档中并未提及这一点,但需要提醒大家,不要将该SDK包含在自己已部署的依赖项中,这会导致整个部署的体积迅速激增。
我们sls deploy
的Zip文件非常庞大。对于包含约20个Lambda的一个服务,最终压缩之后的整个部署文件的体积约为15GB。因此我们很快就碰到了Lambda 75GB的软性限制而不得不申请服务扩容。
尽管扩容后的上限提高到150GB,但我依然开始调查这些软件包到出了什么问题。
此时可以通过两个操作让部署文件的体积实现数量级的“瘦身”:
首先最简单的方法是在Webpack配置中进行文件排除。这样可以避免所有测试文件进入到最终构建中。
随后我单独打包了所有函数。这一做法真正让软件包实现了数量级的瘦身,并且远远低于150GB的限制。
本文最初发布于Launch First Agency博客,作者Dan Jakaitis,经原作者授权由InfoQ中文站翻译并分享。点击阅读英文原文:10 Things I Wish I Had Known About Serverless。