今天我决定领导整个公司走向无服务器架构。最初我花了几个月时间来尝试将Python Flask应用程序[1]迁移到Lambda,这些经历帮助我后来找到更好的方法。
在六个月之后,我们已在无服务化地部署我们的第四个主要项目。以下将讲述我们在此过程中学习到的经验以及对此的一些强烈建议。
#1-抛弃Python
Flask是一个挺不错的小框架,用于由服务器管理会话的站点,使用旧式的请求-响应方式。在交互式网络的新世界中,这就像是用橡皮筋和橡皮刮板来试图建造一个房子一样,非常古怪。
旧式的部署架构
当你开始将更多工作转移到客户端这边以支持交互时,你没有其他选择只能选择JavaScript。这通常会导致(很多奇怪的东西)内嵌到Python模板里,而技术债务则越积累越多。
Flask的解决方案逐渐成为不同语言的集合体。很快我就得出结论,这种方法将会造成一些可怕的混乱,导致我开始怀疑我为何要再使用Python了。
在切换到Node之后,很多东西都变得可维护且合理,并且也不再需要使用多种语言。通过Webpack上简单的Node/Express配置,你还可以使用ES6来消除Python开发者带来的糟糕的JavaScript的代码结构。
在Zapppa/Flask尝试做同样的事情简直比登记纳税更不友好。在5分钟内,你可以构建一个可以在Lambda上运行的完全成熟的Node/Express应用程序,就像1040EZ那样,这非常简单。所以我们放弃了Python并加入了JavaScript的阵营。
将Lambda函数作为整体
为此我们放弃了什么呢?Python支持者们会声情并茂地向你推荐所有酷炫的语言特性,但与JavaScript的实际异步魅力相比,这些仅仅只是玩具。而且我们现在也不需要再担心使用Python 2还是Python 3了(也不知我们到底有没有升级过……)。至少在我们的项目上,我们很容易就完成了转换。
当然,Ben Kehoe还抛出了一项引人注目但同时令人震惊的[2]观点:在无服务器架构中利用Python替代Node。
#2-推翻掉之前的架构
我们花费了大量的时间才意识到无服务器架构的明显好处,可能是因为我们一直是在构建Web应用程序(闭门造车),或者也有可能只是因为我老了。
我们最初的一些Web应用程序仍然有一个Node Express层来记住会话状态,(1)希望用户能总是请求到同一个Lambda容器,(2)悲剧的是在设计中也滥用了DynamoDB来保持会话ID。我们到底在做什么?!
在过渡时期的第一阶段,我们做了错误且可怕的就是我们的中间层跟Lambda上的Web服务器一样,导致我们最终得到了到处是JavaScript去调用REST API的html页面。这种做法非常原始,极度难以维护,并且很快就变得脆弱,但我们已经移除了中间层。在无服务器架构中,中间层必须去除。
应用状态移到客户端,业务逻辑迁去Lamdba
#3-尽情享受Vue
能够将所有东西都塞进前端感觉非常棒,但它很快就发展成一个令人震惊的混乱局面。你最终停止代码审查,因为你觉得分享你一直在开发的那些华而不实的机器语言黑魔法太尴尬了。并且“不审查代码”对开发人员来说不是一个很好的工作目标。
我在了解单页应用(SPA)领域时接触到了React,它是当前最流行的构建用户界面的方案。React很棒,但是它的学习曲线陡峭,有很多Webpack/Babel相关的设置,并且引入了JSX。虽然它可能是我们最终使用的东西,但它对于我们的当前需求来说太重了,所以需要调研其他替代方案。
幸运的是我很快就发现了Vue.js,我的无服务器生活开始走向极乐。事情是这样的:你甚至可以在一天内就学完Vue!
Vue的设计方法非常适合我们的设计模型,一切都是能自管理内容,设计和代码的组件。这使得管理我们的多个客户端项目和分散的团队变得非常容易,并且非常适合无服务器的思维模式。
这个开源的JavaScript框架为你提供强大的调试工具,出色的组织以及能为你节省数小时的开箱即用的Webpack构建。尤其是路由和商店管理插件,你可以像Facebook工程师那样制作实时有趣的应用。谁能想到制作单页应用可以这么简单?
从无服务器的角度来看,Vue将你的所有实现代码编译成index.html和bundle.js文件,并可上传到S3。新的编译命令仅需简单运行npm run build。
仔细想想,在以前,我们通过Elastic Beanstalk部署应用并监控利用率,在需要时进行自动扩展以及还需要管理合理的基础架构。
SPA真正的神奇之处在于,当你部署应用程序时,你只需将index.html,bundle.js和少量文件依赖项复制到由CloudFront分发前端的S3存储块中。这为你提供了稳定的分布式和加载行为,并且还支持多版本管理以及任何你想要的部署方法,可能只需管理文本文件。
理论上来说我们能将规模扩展到无限,同时只对我们已使用的服务付费,其中完全没有应用程序基础设施管理成本。
Vue本质上允许你在浏览器中构建桌面应用程序,这意味着你可以显著改善用户体验。所有状态都可以在这里进行管理而无需无休止的请求/响应,你可以使用标准UI技巧(如过渡效果)来隐藏延迟,并且整个应用程序仍可以正常运行。
#4-爱上DynamoDB
从很多方面来看,实现无服务器最难的部分是真正掌握DynamoDB。你肯定会在前几次迭代中犯一些错误,且会很轻易放弃所有并重新回到RDS,毕竟曾经在这个舒适圈里都很熟悉这些。
SQL在很长一段时间都是我所依赖的,我承认我将过多的业务逻辑放入数据库中。但RDMS系统只是另一个整体,它无法很好地扩展且不能支持有机发展敏捷系统的需求。
DynamoDB是一种完全不同的类型。当你找对正确的实施方案,NoSQL数据库能提供了极高的性能,又能大规模,并且几乎没有管理开销。但是你真正需要的是花时间探索它是如何工作的,还有在初始阶段将会有各种各样的问题。
Dynamo表字段不能包含空字符串。备份时间点不是自动的。如果分区和排序键错误,则必须从头开始使用表。如果你尝试过于密切地模拟SQL查询,你的表可能会越来越多。从RDS转换过来会让你感觉截然不同。
经过许多教程,尝试,失败并最终成功使用DynamoDB,我学到了以下:
#5-无服务器框架
我早期对Lambda进行的实验很笨拙,是直接编程写入到AWS控制台中,后来开始感到诅丧,因为我需要实施大量的工作和错误信息来处理一些微不足道的事情。仅仅是因为没有一条将IDE连接到生产环境的桥梁。
直到我发现无服务器框架,多年来发现的最令人兴奋的事情非它莫属了。
一个简单的sls deploy命令就能将你的代码直接打包上传到AWS上。如果你因代码错误需要检查日志,那么你仅需简单运行sls logs -f functionname -t,你可以像专业人士一样查看CloudWatch的日志而无需通过浏览器。
这改变了以往的一切。请大力赞美无服务器的维护者们吧,因为他们做了所有云服务商从第一天就应该提供的服务。这真的是太棒了!
#6-认证授权
在传统应用程序中,你只需对用户进行一次身份验证,然后通过跟踪会话ID来跟踪此用户行为。我们喜欢这样做是因为艰难的工作仅需完成一次。
通过sessionID控制访问权限
但这种方法存在一些问题,它只适用于在这中间存在服务器的情况。我们已将该中间的服务器去除掉了。它还可能让你暴露给一些令人讨厌的攻击行为,比如跨站点请求伪造(CSRF),并且不能让你轻易地将身份传递给其他服务。所以这种方法基本上仅支持单体应用。
我们讨厌单体服务以及CSRF攻击,但我们确实喜欢我们新引入的技术,即JWT令牌。当我了解到这是如何工作的时候,我有一种类似禅的兴奋感,我需要一张流程图来好好解释明白。
步骤1,获取JWT,第2步,使用它与你编写的任何服务进行通信:
授权过程获取JWT令牌
Lambda函数接受并验证令牌
基本的核心是每个请求都经过身份验证,客户端甚至可以调用多个无服务器进行通信。在JWT的世界里甚至不存在CSRF。无服务器代码所需要的就是使用自定义授权程序来检查标头中的JWT是否有效(可参考实例代码)。
与JWT对比起来,所有其他类型的用户验证看起来都过于复杂。我们毫不犹豫将所有用户验证都切换为Auth0(在某些情况下为Cognito)。无服务器身份验证既简单又非常有效。
迈向新世界
虽然我已经使用了AWS很长一段时间,即使是在EC2的场景下,因为我参与的时间相对较晚所以也会需要很多帮助。在结束无服务器会议之后,这感觉就像进入一个真正未开发的领域,需要我们在黑暗中去发现更多。
在我们的前几个实验中,我们尝试使用现有的工具和技术但发生了一些失误,结果并不是很好。经过几个月的正确积累,我们已经正式开始以100%无服务器方式交付项目。我相信我们迁移过程中的问题和早期的探索非常值得。