实战指南 | Serverless 架构下的应用开发
作者 | 刘宇、田初东、卢萌凯、王仁达
UC Berkeley 认为 Serverless 架构的出现过程类似于 40 多年前从汇编语言转向高级语言的过程,在未来 Serverless 架构的使用会飙升,或许服务器式云计算并不会消失,但是将促进 BaaS 发展,以更好地为 Serverless 架构提供支持。
Serverless 架构的应用开发流程
基于 Serverless 架构的应用开发流程将会比基于传统架构的应用开发更简单。在 Serverless 架构下进行应用开发,用户通常只需要按照规范编写代码、构建产物,然后部署到线上即可。
如图 1 所示,CNCF Serverless Whitepaper v1.0 指出函数的生命周期从编写代码并提供规范元数据开始,一个 Builder 实体将获取代码和规范,然后编译并将其转换为工件,接下来将工件部署在具有控制器实体的集群上。该控制器实体负责基于事件流量和/或实例上的负载来扩展函数实例的数量。
图-1 函数部署流水线示意图
如图 2 所示,函数创建和更新的完整流程如下。
图-2 函数创建/更新流程示意图 1)在创建函数时,提供其元数据作为函数创建的一部分,对其进行编译使其具有可发布的特性。接下来启动、禁用函数。函数部署要能够支持以下用例。
事件流(Event Streaming):在此用例中,队列中可能始终存在事件,但是可能需要通过请求暂停/恢复进行处理。
热启动(Warm Startup):在任何时候,具有最少实例的函数能使所接收的“第一”事件快速启动,因为该函数已经部署并准备好为事件服务(而不是冷启动),其中函数通过“传入”事件在第一次调用时部署。
2)用户可以发布一个函数,这将创建一个新版本(最新版本的副本),发布的版本可能会被标记或有别名。
3)用户可能希望直接执行/调用函数(绕过事件源或 API 网关)以进行调试和开发过程。用户可以指定调用参数,例如所需版本、同步/异步操作、详细日志级别等。
4)用户可能想要获得函数统计数据(例如调用次数、平均运行时间、平均延迟、失败次数、重试次数等)。
5)用户可能想要检索日志数据,这可以通过严重性级别、时间范围、内容来过滤。Log 数据是每个函数级别的,它包括诸如函数创建/删除、警告或调试消息之类的事件,以及可选的函数的 Stdout 或 Stderr。优选每次调用具有一个日志条目或者将日志条目与特定调用相关联的方式(以允许更简单地跟踪函数执行流)。
图-3 开发 Serverless 应用的流程
如图 3 所示,以阿里云 Serverless 产品为例,在生产环境中开发 Serverless 应用的流程是:
**步骤 1:**根据 FaaS 供应商所提供的 Runtime,选择一个熟悉的编程语言,然后进行项目开发、测试;**步骤 2:**完成之后将代码上传到 FaaS 平台;**步骤 3:**上传完成之后,通过 API/SDK 或者由一些云端的事件源触发上传到 FaaS 平台的函数;**步骤 4:**FaaS 平台就会根据触发的并发度等弹性执行对应的函数;**步骤 5:**最后用户根据实际资源使用量按量付费。
与 ServerFul 应用开发流程对比
下面我们将通过生产环境中的案例,对传统架构下的应用开发与 Serverless 架构下的应用开发进行举例对比。
以一个 Web 应用为例。如图 4 所示;
图-4 传统三层 C/S 架构下某电子商务网站应用简图
通常情况下一些 Web 应用都是传统的三层 C/S 架构,例如一个常见的电子商务应用,假设它的服务端用 Java,客户端用 HTML/JavaScript;在这个架构下服务端仅为云服务器,承载了大量业务功能和业务逻辑,例如,系统中的大部分逻辑(身份验证、页面导航、搜索、交易等)都在服务端实现。当把它改造成 Serverless 应用形态时,架构如图 5 所示。
图-5 Serverless 架构下某电子商务网站应用架构简图
**步骤 1:**在 Serverless 应用形态下,移除最初应用中的身份验证逻辑,换用一个第三方的 BaaS 服务;**步骤 2:**允许客户端直接访问一部分数据内容,这部分数据完全由第三方托管,这里会用一些安全配置来管理客户端访问相应数据的权限。**步骤 3:**前面两点已经隐含了非常重要的第三点,即先前服务器端的部分逻辑已经转移到了客户端,如保持用户会话、理解应用的 UX 结构、获取数据并渲染用户界面等,客户端实际上已经在逐步演变为单页应用;**步骤 4:**还有一些任务需要保留在服务器上,比如繁重的计算任务或者需要访问大量数据的操作。这里以“搜索”为例,搜索功能可以从持续运行的服务器端拆分出来,以 FaaS 的方式实现,从 API 网关(后文做详细解释)接收请求并返回响应。这个服务器端函数可以和客户端一样,从同一个数据库读取产品数据。原先的搜索代码略做修改就能实现这个“搜索”函数;**步骤 5:**还可以把“购买”功能改写为另一个 FaaS 函数,出于安全考虑,它需要在服务器端而非客户端实现,它同样由 API 网关暴露给外部使用。传统云主机架构下应用的开发和上线如图 6 所示。
图-6 传统云主机架构下应用开发和上线流程简图
开发者完成代码开发之后,需要进行上线前的准备,包括但不限于评估资源、购买服务器、安装操作系统、安装服务器软件等,完成之后再进行代码部署,之后还需要有专业的人或者团队,对服务器等资源进行持续监控和运维等,例如流量突然提升时需要进行服务器的平滑扩容,流量突然降低时需要进行服务器的平滑缩容等。
但是在 Serverless 架构下,整个开发模式发生了比较大的改变。
图-7 Serverless 架构下应用开发和上线流程简图
结合上面某电子商务网站见图 7,在上述应用开发和上线流程中,Serverless 架构开发者实际关心的只剩下函数中的业务逻辑,至于身份验证逻辑、API 网关以及数据库等原先在服务器端的一些产品和服务统统由云厂商提供。
同时,**用户不需要关注服务器层面的维护,也无须为流量的波峰和波谷进行运维资源的投入。**用户也无须为资源闲置进行额外的支出,Serverless 架构的按量付费以及弹性伸缩能力、服务端低运维/免运维能力,可以让用户的资源成本、人力成本降低,整体研发效能大幅提升,让项目的性能、安全性、稳定性得到极大的保障。
综上所述,Serverless 架构与传统架构应用开发流程的明显区别是,前者让开发者将更多的精力放在自身业务逻辑上,并强调 Noserver 的心智,将更专业的事情交给更专业的人去做,这有助于业务的创新和效率的提升,降低业务上线及迭代周期等。
面临的挑战
虽然 Serverless 架构发展迅速,被更多的人认为是真正意义的云计算,甚至在 2020 年的云栖大会上,Serverless 被再次断言“将会引领云计算的下一个十年”。但是,Serverless 架构仍然面临着诸多挑战。
19 年 UC Berkeley 的文章“Cloud Programming Simplified:A Berkeley View on Serverless Computing” :针对 Serverless 架构总结出了下面五项挑战。
1)Abstraction Challenge 资源需求(Resource Requirement):通过 Serverless 产品,开发人员可以指定云功能的内存大小和执行时间,而无法指定其他资源需求。这阻碍了那些想要控制更多指定资源的人,例如 CPU、GPU 或其他类型的加速器等资源。数据依赖(Data dependence):今天的云功能平台不了解云功能之间的数据依赖性,更不了解这些功能可能交换的数据量。这可能导致次优放置,从而导致通信模式效率低下。
2)System Challenge 临时性存储(Ephemeral Storage):为 Serverless 应用提供临时存储的一种方法是使用优化的网络栈构建分布式内存服务,以保证微秒级的延迟。
持久性存储(Durable Storage):与其他应用程序一样,Serverless 数据库应用受到存储系统的延迟和 IOPS 的限制,需要长期的数据存储和文件系统的可变状态语义。协调服务(Coordination Service):功能之间的共享状态通常使用生产者-消费者设计模式,这需要消费者在生产者获得数据时立即知道。
最小化启动时间(Minimize Startup Time):启动时间包括 3 个部分,一是调度和启动资源以运行云功能的时间,二是下载应用软件环境(例如操作系统、库)以运行功能代码的时间,三是执行特定于应用程序的启动任务的时间,例如加载和初始化数据结构和库的时间。资源调度和初始化可能会因创建隔离的执行环境以及配置客户的 VPC 和 IAM 策略而产生显著的延迟和开销。
3)Networking Challenge 云功能可能会对流行的通信原语(如广播、聚合和混洗)产生巨大的开销。
4)Security ChallengeServerless 架构重新分配了安全责任,将其中许多人从云用户转移到云提供商,而没有从根本上改变它们。但是,Serverless 架构还存在应用程序分解多租户资源固有的风险。
5)Computer Architecture Challenge 主宰云的 x86 微处理器性能提升速度缓慢。当然,此处所认为的 Serverless 架构面临的挑战相对来说是比较抽象的。就目前的工业界实际情况来看,这些挑战依旧普遍存在,也是当今众多云厂商不断努力的方向。站在 Serverless 开发者角度来说,将上述挑战与开发者最为关注的几个问题结合起来,可以认为 Serverless 架构目前所面临的挑战包括不限于冷启动问题、厂商锁定、配套资源不完善等。
1 冷启动问题所谓的冷启动问题,指的是 Serverless 架构在弹性伸缩时可能会触发环境准备(初始化工作空间)、下载文件、配置环境、加载代码和配置、函数实例启动完整的实例启动流程,导致原本数毫秒或数十毫秒可以得到的请求响应需要在数百毫秒或数秒得到,进而影响业务处理速度。
正如前面所说,任何事物都有两面性。Serverless 架构具备弹性伸缩优势的同时,相对 ServerFul 架构而言也引入一个新的问题:冷启动问题。Serverless 架构下,开发者提交代码之后,平台一般只会对其持久化并不会为其准备执行环境。所以,当函数第一次被触发时会有一个比较漫长的准备环境的过程,这个过程包括把网络的环境全部打通、将所需的文件和代码等资源准备好。
这个从准备环境开始到函数被执行的过程,被称为函数的冷启动。由于 Serverless 架构具有弹性伸缩能力,Serverless 服务的供应商会根据流量波动进行实例的增加或缩减,所以平台就可能频繁准备新的环境、下载函数代码、启动实例来应对不断产生的请求。
图-7 函数计算根据流量进行函数扩缩示意图如图 7 所示,当 Serverless 架构的 FaaS 平台中某函数被触发时,FaaS 平台将会根据具体情况进行实例的复用或者新实例的启动。
图-8 FaaS 平台实例启动流程简图如图 8 所示,当有空闲且符合复用要求的实例时,FaaS 平台将会优先使用,这个过程是所谓的热启动过程。否则,FaaS 平台将启动新的实例来应对此时的请求,这个过程则是对应的冷启动过程。
Serverless 架构这种自动的零管理水平缩放,将持续到有足够的代码实例来处理所有的工作负载为止。
其中,“新实例的启动”包括初始化工作空间、下载文件和配置环境、加载代码和依赖、函数实例启动等几个步骤。相对于热启动在数毫秒或者几十毫秒内的启动,冷启动所多出来的这几个步骤耗时可能是数百毫秒甚至数秒。这种在生产时出现的新实例启动,并导致业务响应速度受到影响的情况,通常就是大家所关注的冷启动带来的影响,如图 9 所示。
图-9 函数冷启动产生示意图
综上所述,不难分析和总结出冷启动问题出现的常见场景。
函数的第一次启动:函数部署后的第一次启动,通常是不存在已有实例,所以此时极易出现冷启动问题。请求并发:当前一个请求还没有完成,就收到了新的请求,此时 FaaS 平台会启动新的实例来应对新的请求,进而出现冷启动问题。
前后两次触发间隔太久:函数的前后两次触发时间间隔超过了实例释放时间的阈值,也会引发函数的冷启动问题。目前来看,Serverless 架构所面临的冷启动挑战虽然严峻,但并不致命,因为各个云厂商都正在努力推出冷启动问题的解决方案,包括但不限于实例的预热、实例的预留、资源池化、单实例多并发等。
2 厂商锁定所谓的厂商锁定,指的是不同厂商开发的 Serverless 架构的表现形式是不同的,包括产品的形态、功能的维度、事件的数据结构等,所以一旦使用了某个厂商的 Serverless 架构,通常意味着 FaaS 部分和相对应的配套后端基础设施也都要使用该厂商的,若想进行多云部署、项目跨云厂商迁移等将会困难重重,成本极高。
众所周知,函数是由事件触发的,所以 FaaS 平台与配套的基础设施服务所约定的数据结构往往决定了函数的处理逻辑。如果每个厂商相同类型的触发器所约定的事件结构不同,那么在进行多云部署、项目跨云厂商迁移时就会产生巨大的成本。
当开发者开发一个功能,在不同云厂商所提供的 Serverless 架构中实现时,涉及的代码逻辑、产品能力均是不同的,甚至业务逻辑、运维工具等也是完全不同的。所以想要跨厂商进行业务迁移、业务的多云部署,企业将面临极高的兼容成本、业务逻辑改造成本、多产品的学习成本、数据迁移风险等。
由于目前没有完整、统一的且被各云厂商所遵循的规范,不同厂商的 Serverless 架构与自身产品、业务逻辑绑定严重,开发者的跨云容灾、跨云迁移非常困难。目前来看,各云厂商对 Serverless 架构锁定严重的问题也是开发者抱怨最多、担忧最多的问题之一。
当然就该问题而言,无论 CNCF 还是其他一些组织、团队都努力在上层通过更规范、更科学的方法进行完善和处理。
3 配套资源不完善所谓的配套资源不完善,指的是 Serverless 架构的核心思想之一是将更多、更专业的事情交给云厂商来做,但是在实际过程中,云厂商会碍于一些需求优先以及自身业务素质等问题,没有办法做更多“在 Serverless 架构中该做的事情”,导致开发者对基于 Serverless 架构开发项目、运维应用过程中困难重重,抱怨不断。
在 Serverless 架构飞速发展的过程中,各个厂商也在努力完善自身的配套资源和设施。尽管如此,Serverless 架构还是有很多配套资源不完善,并不能让开发者更顺利地完成 Serverless 应用的开发、更轻松地对 Serverless 应用进行运维,主要表现在以下几个方面。
1)配套的开发者工具复杂多样,且功能匮乏
一方面表现在市面上开发者工具链匮乏,这导致开发和部署难度大,进而增加成本;另一方面表现在缺乏相关的工具链在体验层对 Serverless 体验进一步提升,优质工具链的匮乏导致本来就担心被厂商绑定的 Serverless 开发者变得更难与厂商解绑。
2020 年信通院发布的国内首个《云原生用户调查报告》明确指出在使用 Serverless 架构之前,49% 的用户考虑部署成本,26% 的用户考虑厂商绑定情况,24% 的用户考虑相关工具集完善程度。
这些数据背后透露的实际是:开发者对于完善工具链的强烈需求。就目前情况来看,并没有绝对统一、一致的 Serverless 开发者工具,每个厂商都有自己的开发者工具,而且使用形式、行为表现并不相同,这就导致开发者在开发前的调研、开发中的调试、部署后的运维等多个层面面临很严峻的挑战。
另外,绝大部分的 Serverless 开发者工具更多是资源编排、部署工具,并不能真正称为开发工具、运维工具,尤其在调试中不能保证线上和线下环境的一致;在运维时不能快速对业务进行调试,不能保证更简单地排查错误;在定位问题等方面并没有统一、完整的方案,这就导致 Serverless 架构的学习成本、使用成本对开发者来说会变得非常高。
2)配套的帮助文档、学习资源并不完善,学习成本过高就目前情况来看,Serverless 架构的学习资源相对来说是匮乏的,无论从文字、视频、实验等角度,还是从厂商提供的案例、教程、最佳实践等角度,都没有完善的学习资源和参考案例。正是由于 Serverless 学习资源比较少,开发经验案例比较少,开发者在学习阶段很难找到适合自己的学习资源,开发过程中经常会遇到未知错误,严重阻塞了 Serverless 架构在开发者侧的心智建设。
当然,上述几个方面也只是 Serverless 架构在配套资源、设施层面不完善的部分表现。除此之外,Serverless 架构如何与传统框架更紧密地结合;传统业务如何更容易地迁移到 Serverless 架构;Serverless 架构如何做监控、告警;如何管理 Serverless 应用与 Serverless 资源;Serverless 架构的科学发布、运维的最佳实践是什么样子的……这些问题也都是需要研究与探索的。
时至今日,对于 Serverless 架构尽管还有很多的挑战需要面对,但是大家都在努力希望通过更好的体验助力用户将业务代码更简单、更快速地部署到 Serverless 架构,例如阿里云 Serverless 团队开源的👉Serverless Devs就是一款无厂商锁定的 Serverless 应用全生命周期管理工具。
如图所示,Serverless Devs 可参与到项目的创建、开发、调试、部署与运维的全流程,以阿里云函数计算组件为例。
图-10 Serverless Devs 项目全生命周期管理 Serverless 应用示意图
在项目的创建环节,可通过开发者工具或者应用中心进行项目的最初创建。
在项目开发环节,可以通过本地开发、调试等能力来验证本地开发的正确性。
在项目调试环节,可以通过本地调试与远程调用、日志查询等能力进行项目的最终调试。
在项目部署环节,可以先通过依赖安装、项目构建等流程构建完整的部署包,再进行项目的部署。
在后期运维环节,可以通过指标查询进行项目健康度检查,通过日志查询等进行问题定位,通过项目发布等能力进行版本发布、别名发布以及灰度发布等。
除此之外,在学习资源层面也有大力扶持。图 11 是阿里云开发者社区提供的大量 Serverless 课程。(👉2021版技术图谱)
图-11 阿里云开发者社区提供的大量 Serverless 课程后面我们还筹备了“Serverless 实战攻略”等系列课程,欢迎大家持续关注。
4 其它挑战 Serverless 架构如今已经非常热门,各个厂商也都在付诸更大的努力完善自身的 Serverless 产品,推动 Serverless 生态和心智建设。但是客观地说,Serverless 架构所暴露出的挑战,并不只有前面所描述的。
1)Serverless 架构在某些安全层面会有更大的挑战?
把更专业的事情交给更专业的人,让 Serverless 架构在安全层面有了更大的保障,但是由于 Serverless 架构的极致弹性能力让开发者产生了更多的担心,“如果有人恶意对我的业务进行流量攻击,Serverless 架构的极致弹性和按量付费会不会让我迅速产生巨大的损失?”这与传统云主机所表现出的“无法提供服务”是不同的,但是更让开发者担忧。
尽管现在很多厂商都在通过 API 网关的白名单与黑名单功能、函数计算的实例资源上限配置等相关功能解决该问题,但仍有很多开发者会有担忧和顾虑。
2)出现错误-难以感知也难以排查?
由于 Serverless 架构相对传统云主机架构更有“黑盒”即视感,所以在 Serverless 架构下进行应用的开发,往往会出现一些难以感知的错误。
例如某些经验不足的 Serverless 应用开发者在使用对象存储触发器时就可能会面临严重的循环触发问题,具体为“客户端上传图片到对象存储,对象存储触发函数执行图片压缩操作,之后将结果图片回写到对象存储,如果这里的触发条件设置不清晰,就可能导致循环触发压缩、回写操作”。除了描述的错误难以感知之外,Serverless 架构还存在错误难以排查的挑战。常见的情况是,用户在本地进行业务逻辑开发并调试完成,将代码部署到线上后出现偶然性错误,此时由于无法登录机器进行调试,并且实例可能在触发之后被释放,出现了问题难以定位、难以溯源等挑战。
综上所述,与 Serverless 架构的优势一样,虽然在前文中已经举例说明很多 Serverless 架构所面临的挑战,但其实一些挑战也已经有了很好的解决方案,我们这系列文章也会详细为大家介绍如何解决这些。Serverless 架构虽然面临的挑战很多,但也会因为这些挑战给更多组织、团队带来新的机会!
评论