Peter Wayner 范范
在AWS Lambda、谷歌云函数(Cloud Functions)和微软Azure Functions的帮助下,一点点业务逻辑就能够完成许多工作
如果你曾有过因为服务器故障在凌晨3点钟被叫醒过的经历,就会明白“无服务器”这个热门术词的吸引力之所在。机器需要花上数小时、数天有时甚至是数周时间进行配置,同时它们还需要经常更新以解决BUG和安全漏洞。这些更新通常会带来麻烦,因为新的更新会与其他的更新不兼容,这种情况似乎永无休止。
由于运行服务器而产生的这种令人头疼的死循环也是许多大型云公司选择“无服务器”架构的原因之一。他们知道,老板长期以来一直听到的借口都是“服务器这样啦,服务器那样啦”。老板肯定在考虑我们是否能够摆脱服务器。
这是一个很好的营销口号,唯一的问题是它在严格意义上并不正确。这些应用没有了服务器就如同饭店没有了厨房。如果你想点东西都在菜单上,并且你喜欢厨师准备它们的方式,那么坐在餐厅里是很棒的。但是如果你想吃一道与众不同的菜,或是吃到与众不同的口味,那么你最好有自己的厨房。
亚马逊、谷歌和微软这三家巨头目前正在激烈地争夺主机应用的未来,希望将它们写入自己的无服务器API并通过自己的自动化层进行管理。如果平台达到了我们的要求,同时新的模式普及起来,那么对于创建数十亿美元的独角兽级网络应用来说,它们无疑是最简单快捷的方式。我们只需要写少量的关键逻辑,平台会处理所有的細节。
无服务器函数正在成为能够将所有云功能连接起来的胶合或脚本语言。曾经相对独立的映射或AI工具如今也通过由事件驱动的无服务器函数连接到了一起。如今我们的许多工作都可以通过云端各个部分的响应和事件触发来完成。如果我们希望尝试机器学习并通过它们分析数据,那么最快的方式是创建一个无服务器应用,然后将事件发送至云端的机器学习部分。
这其中的关键是将所有东西进一步细化,让它们更容易共享云端上的资源。过去,所有的人都在疯狂地利用运行在自己的虚拟机上的Ubuntu服务器创建新实例。所有的人都在使用相同的操作系统,而这个操作系统会在被划分为多个虚拟Ubuntu服务器的真实服务器上大量复制。无服务器操作避免了这种复制,从而大幅降低了云计算的成本,尤其是对于那些只是零星运行,甚至从未让那些位于机房中的老服务器发生拥堵的工作。
当然所有的便利背后都有隐藏的成本。如果你想将代码迁移到另一个站点,你可能会为需要重写堆栈中的绝大部分而感到怵头。API则不同,虽然像JavaScript等流行的语言都进行了标准化,但是它们几乎已经变为了专利的。这使得用户极有可能遇到厂商锁定的情况。
为了介绍无服务器选项的吸引力,我花费了一些时间创建了一些函数并将它们放在堆栈上。我没有编写太多的代码,不过重点就在于此。我将更多的时间花在了点击按键和填写web表单以配置所有的东西。你是否还记得我们使用XML和JSON配置所有东西的时候?现在我只需要填写一个web表单,云会为我们完成剩下的工作。尽管如此,我们仍然要像程序员那样考虑问题,搞清楚云端上发生了什么以及哪些不在自己的控制之中。
AWS Lambda
AWS Lambda正在成长为亚马逊整个云的shell脚本层。作为一个基础系统,其可让嵌入的函数对由亚马逊云基础设施的任意部分生成的事件做出响应。如果新的文件上传至S3,我们可以让其触发一个函数,让函数利用新文件做有趣的事情。如果某个视频正在使用亚马逊Elastic Transcoder(弹性转码器)进行转码,那么我们可以让Lambda函数等待至转码完成后再依次被触发。这些函数能够触发其他的Lambda操作,或是仅向某人发送更新。
你可以用JavaScript (Node.js)、Python、Java、C#和Go等编写Lambda函数。鉴于上述这些语言可以嵌入许多其他语言,这使得运行Haskell、Lisp甚至是 C++等其他代码成为了可能。
由于亚马逊为配置和优化提供了许多选项,因此我们会觉得编写Lambda函数会比预期的要复杂。尽管在技术上我们确实可以只编写几行代码就能够完成许多事情,但是我认为自己必须要分配更多的时间去配置代码的运行方式。许多工作是通过浏览器填写表单完成的,而不再是通过键入文本文件完成。有时我会觉得我们只是将文本编辑器换成了一个基于浏览器的表单。不过,这也是为了保留所有的灵活性所付出的代价,亚马逊希望将这些灵活性也提供给Lambda用户。
在额外的步骤中,有一些是由于亚马逊向用户提供了更多选择并期待更多的人初次尝试函数编写而导致的。一旦我在谷歌或微软上编写完成了一个函数,那么我能够将自己的浏览器指出正确的URL并立即测试它们。亚马逊会让我点击配置API网关并正确设置防火墙。
最后,所有的这些点击都添加了一个辅助层,这比使用文本文件要更容易上手。在我创建了一个函数后,浏览器会发出 “该函数包含了一个外部库”的警告。若是回到纯Node的时代,这正是我希望知道的东西,我会通过谷歌搜索这一错误信息提示来找寻答案,期间我会双手交叉,期待着答案能够出现在搜索结果页面上。现在云会为我们提供帮助。
如果无服务器意味着可以将我们从服务器管理中解放出来,那么亚马逊还有许多与AWS Lambda一样的 “无服务器”选项。与此同时,亚马逊还有EC2 Auto Scaling、AWS Fargate、AWS Elastic Beanstalk等弹性工具,其中EC2 Auto Scaling和AWS Fargate可启动和关闭服务器,AWS Elastic Beanstalk可处理上传的代码,将其部署至web服务器上并进行负载平衡和扩展。当然,通过这些自动化工具,我们仍然可以创建服务器镜像。
AWS Step Functions是一个非常有帮助的解决方案。作为无代码流程图工具,它们可为软件架构师称之为工作流的模型创建状态机。部分问题在于所有的无服务器函数都是无状态的,在执行一些最基础的业务逻辑时它们才工作,然而当通过检查单或流程图浏览客户端时这就会带来麻烦。你需要不断地去数据库重新加载关于客户端的信息。Step Functions则将Lambda函数和状态连接在了一起。
谷歌云函数与Firebase
如果你的目标是摆脱配置服务器的工作,那么谷歌云有许多服务可以将你从提供根密码、甚至是使用命令行的工作中解放出来。
从2008年的谷歌App Engine开始,谷歌一直在慢慢地添加不同的“无服务器”选项,并且提供各种消息传递和数据透明组合。谷歌云Pub/Sub可以为我们屏蔽消息队列,因此我们只需为数据生产者和消费者编写代码即可。谷歌云函数为许多重要的产品提供了由事件驱动的计算,包括选框工具和API。与此同时,谷歌的Firebase可让我们将JavaScript代码混合到向客户端传递数据的数据存储层。
在这些当中,Firebase是最让我感兴趣的。有人认为数据库是原始的无服务器应用,其抽象掉了数据结构和磁盘存储,然后通过TCP/IP端口传递所有的信息。通过添加JavaScript代码和通知完成所有工作(你可能会希望通过服务器架构完成,如认证),Firebase将抽象化运用到了极致。在技术上,它们只是一个数据库,但是它们可以处理许多业务逻辑和堆栈通知。我们真的可以摆脱一些客户端的HTML、CSS、JavaScript和Firebase。
你可能倾向于将Firebase的JavaScript层称之为“存储过程”,就像甲骨文的做法一样,但是这将导致错失一个良机。Firebase代码是由JavaScript编写的,因此它们可以在本地版本的Node.js上运行。你可以在这个层嵌入许多业务逻辑,因为Node已经带有处理这一工作流的库。此外,你还可以享受到运行在客户端、服务器和数据库上的同构代码的便利。
吸引我的另一个原因是Firebase中的同步层,它们会在网络中从数据库同步项目的副本。通过这个功能,我们可以将自己的客户端应用设置成另一个数据库节点并订阅针对相关数据的所有变化(或仅仅是相关的数据)。如果某个地方的数据发生了变化,它们会修改所有的地方。我们不再需要发布通知,只需要为Firebase编写一个信息,因为Firebase会在需要的地方复制它们。
我们不需要将关注点仅仅放在Firebase上。更为基础的谷歌云函数则是一个更为简单的解决方案,其可将定制的代码嵌入至整个谷歌云中。此时,对于编写将在预先配置的Node环境中运行的Node.js代码来说,云函数一个很好选择。尽管谷歌云平台支持Java、C#、Go、Python和PHP等多种语言,但云函数仅限于支持JavaScript 和Node。目前已经有迹象显示它们将支持其他的语言,如果这些真的变成了现实,那么我对此一点也不感到意外。
谷歌云函數在谷歌云中融合程度还没有达到AWS Lambda在AWS中的融合程度,至少目前是这样。当我尝试着创建一个函数与谷歌Docs交互,我发现自己可能需要REST API并用Apps Script编写代码。换句话说,谷歌Docs有着自己的REST API,在“无服务器”这个词被造出来之前的很长一段时间里它一直在尝试这一概念。
值得关注的是谷歌App Engine一直保持着良好的发展势头。在一开始,它们仅提供了启用Python应用以满足网站访问者的需求。经过几年的发展,如今它们已经可以处理许多不同的语言运行环境。一旦将代码捆绑到可执行文件中,在用户发送请求时,App Engine将进入启动足够节点的程序以处理流量,并进行相应的扩展和收缩。
尽管如此,目前仍然有一些障碍需要克服。与云函数一样,我们的代码必须要以一种相对无状态的方式编写,同时还必须要在限定的时间内完成每个请求。App Engine不会扔掉所有的辅助工具,或是忘掉这些请求之间的东西。虽然App Engine是无服务器革命中的一个重要组成部分,但是对于那些依旧使用老方法通过Python、PHP、Java、C#或Go创建堆栈的人来说,App Engine仍然是最好用的。
微软Azure Functions
当然,微软也正在和他的竞争对手一样努力的让用户也能够通过Azure云享受无服务器的便利。他们创建了自己的基础函数Azure Functions以及设计精巧的工具,这些工具甚至可以抵得上半个程序员。
微软最大的优势可能在于其Office应用。Office应用以前为桌面执行文件,如今它们正逐步迁移至云端。实际上,微软云营收超过亚马逊在很大程度上得益于其将Office的营收也计入到云营收当中。
Azure Functions文档中列出的一个最佳范例为我们展示了当用户将一个电子表格存储在OneDrive后云函数是如何被触发的。云端上的小精灵们会突然活跃起来帮助处理该电子表格。对于钟爱Excel 电子表格(或其他Office文档)的IT商店支持团队来说,这无疑是天赐的利器。他们可以通过编写Azure Functions做几乎所有的事情。虽然我们经常会认为HTML和web是云唯一的界面,然而它们没有理由不能通过像微软Word或Excel这样的文档格式完成。
Azure的Logic Apps吸引我的原因是其中的一个工具可以让我们只填写表格而无疑担心语义和语法的问题。虽然我们仍然需要像程序员那样思考问题并对抽象和数据做出明智的决策,但是我们可以确信自己编写的“代码”没有填写的表格那么多。
与亚马逊的Step Functions一样,Logic Apps有意编码“工作流”。得益于一些状态的可获得性,这里的“工作流”要比普通意义上的“函数”稍微复杂些。我们仍然要编写逻辑,以类似流程图的方式将不同的函数和连接器连接来,但是我们不再需要使用正式的计算机语言拼写它们。
Logic Apps最大的优势是预建了“连接器”,这使得它们能够深入到更多的微软和第三方应用中。我们可以高效地向Logic Apps推送或从Logic Apps中拉出数据,就像Salesforce、Twitter和Office 365那样。这些连接对于公司的IT员工来说极具价值,他们如今可以通过编写Logic Apps将外部工具连接起来,就像他们过去创建shell脚本一样。
Azure另一个有趣之处是Azure Cosmos DB。该数据库同时既是NoSQL数据库也是SQL数据库。微软复制了针对Cassandra和MongoDB的API,因此我们可以在不重写Cassandra或MongoDB代码的情况下推入和推出信息。如果我们想写SQL,那么我们也可以做到。Cosmos DB更为直接,它们为所有东西都创建了索引以更快的运行。如果我们有许多SQL和NoSQL代码,同时我们希望它们能够共同工作,那么Azure Cosmos DB会创建一个中央结点。或许我们还希望在未来为其他不同的解决方案留下一扇敞开的门。
三个无服务器云之间的比较
哪种无服务器平台最适合我们呢?虽然编写基础函数的工作量在这三个独立的平台上都相差无几,但是还是存在着区别。最明显的区别可能是可以使用的语言,因为每个平台在完成了对Node.js和JavaScript的支持之后都有自己偏爱的语言。我们对在微软Azure上能够使用C#语言一点也不意外,意外的是它们是唯一支持F#和TypeScript语言的平台。亚马逊支持的语言是Java、C#和Python。尽管谷歌的App Engine支持许多语言,但是目前谷歌的基础函数仅限于JavaScript。
在比较这些无服务器云时,最困难的工作是对价格和速度进行比较,因为它们更多地被隐藏在后台。当我启用VM实例后,我会有种自己在挥金如土的感觉,因为它们的定价是按小时收费的。如今,这些提供商正在将香肠切的越来越薄,我们可以以不到一美元的价格获得几十万次的函数调用。我们将会像《王牌大贱谍》中的邪恶博士那样将“百万”这个词挂在嘴边。
当然,这些表面上的低价很快就会让我们的头脑丧失理智和预算意识,这就如同我们去一个使用不同货币的陌生国家度假一样。我们不久就会进行另一个数百万次的数据库调用。这很像我们在墨西哥的疗养观光胜地坎库恩的酒吧喝酒一样,我们无法迅速地计算出它们的真正价格。
当云向我们销售原始的虚拟机时,我们可能会根据RAM 和CPU进行评估,但是在无服务器平台,我们根本没有什么能够用于评估的依据。
值得关注的是,无服务器模式将会迫使我们将数据存放在本地的云数据库上,因为我们不可能真正被允许通过代码保持任意状态。我们不得不信任这些后端。函数必须在没有任何缓存或配置的情况下运行,因为总是有许多版本在不断的出现和消失。数据库胶水代码会填满我们的代码,这一切就如同美剧《怪奇物语》中“倒置”世界里的那些藤蔓一样。
对比它们的成本唯一可行的办法是在所有的平台上都创建一个应用,不过这是一件非常具有挑战性的工作。由于它们都可运行Node.js,因此在三个平台之间移动一些代码是可行的。即便如此,我们还是会遇到一些不得不隐忍的差异(例如,我们在微软和谷歌的平台上可直接处理HTTP请求,但是在AWS上就需要通过API网关)。
好消息是我们不需要如此偏执。在我的体验中,许多基础应用几乎用不上什么资源,我们可以在三个平台为兜里没钱的开发者提供的免费层上干许多事情。无服务器模式确实可以为我们在总开支方面省下一大笔资金。除非我们属于全天候满负荷运行服务器的类型并且有免费的空调,否则采用无服务器解决方案可以为我们节约很多费用。无论价格是每百万次调用1美元还是1.5美元,它们将能够为我们节省很多钱,这一点是无可争辩的。
这里还有一个更深层次的问题。如果我们对这三个云平台都不满意,那么我们就遇到了很大麻烦。将代码从这些平台中抽出来让它们运行在商用服务器上,就像我们在Docker容器那样几乎是不可能的。如果幸运的话,我们可以复制相同的原始架构和基础的JavaScript函数,但是在此之后,我们需要重写所有地方的数据库胶水代码。因为这三家公司都有自己专利的数据存储层。
还有一点不明确的地方是如果出现了运行故障会发生什么情况。在运行自己的服务器时,如果出现了故障,老板会狠掐我们的脖子。当无服务器平台出现了故障,会发生什么并不明确。谷歌的页面上有这样一句警告“这是Google云函数的测试版。 此API可能会以不兼容的方式进行更改,并且不受任何SLA或弃用策略的约束。”
与刚刚涉及这一领域时相比,亚马逊的服务条款已经变得好多了。但是这些条款中仍然包含了需要我们牢牢记住的警告,“如果您的内容运行未超过三(3)个月,我们可能会在30天内通知您并且在没有任何责任的情况下删除您上传到AWS Lambda的任何内容。”如果我们想一直使用它们,那么就要确保自己的代码处于运行状态。这种警告是无疑正确的(因为我知道自己的老的Lambda函数将永远不会再使用),但是这表明我们将放弃一些控制权。
微软则为Azure服务提供了服务级协议,承诺将依据服务信用对宕机进行经济补偿。那么当我们的函数不工作了,这些承诺仍然适用吗?或许吧,前提是我们不要尝试该服务的一些测试功能。如果我们将要创建一些对任务起着关键作用的东西,而非为小朋友创建一个聊天室,那么花些时间研究这些条款是值得的。
在許多情况下,我们所做的仅仅限于对亚马逊、谷歌和微软的服务和功能进行比较,而忽略了函数层。如果我们支持的用户对Office应用情有独钟,那么通过OneDrive上的Office文件触发Azure Functions的能力将具有很大的吸引力。谷歌Firebase使得通过函数为web应用提供通知和认证等支持服务变得更加容易。AWS Lambda则引入了许多亚马逊服务,但是这似乎也限制了它们的发展前景。
将这些云和函数进行混搭在技术上是可行的,因为它们在HTTP API调用方面都使用相同的PUT和GET语言。无法在一个应用中使用汇聚了三个云服务优势的微服务在说法上是不成立的。但是严重的延时会让我们放弃尝试,因为数据包离开了本地云,转而在开放而广阔的互联网上传输。此外,由于在语法分析和结构上还存在稍许差异,这也使得我们只会选择一家公司的平台。
只选择一个云在安全方面可能更具意义。你真的喜欢谷歌Maps并希望将它们应用到自己的项目中吗?那么即便内心深处更希望通过微软的Azure Functions使用F#,你可能还是会选择使用谷歌云函数。亚马逊的语音识别、谷歌的图像分析API以及其他不同的服务和机器学习API也面临同样的道理。这些函数并不重要,重要的是它们与哪些实际的事情有着密切的联系。