Serverless工程实践:从入门到进阶
上QQ阅读APP看书,第一时间看更新

1.3 Serverless的特点

1.3.1 优势与特点

前面已经说过,在云计算发展的过程中,从IaaS到PaaS再到SaaS的过程中,去服务器化已越来越明显。

到了Serverless架构,去服务器化已经上升到了一个新的高度。所谓无服务器,不是说脱离了服务器或者说不需要服务器,而是指去除有关对服务器运行状态的关心和担心。另外,Serverless架构也一直在演进,如图1-14所示。

057-01

图1-14 虚拟机、容器、Serverless架构演进简图

单体架构时代的应用比较简单,应用的整体部署、业务的迭代更新,物理服务器的资源利用效率足以支撑业务的部署。随着业务的复杂程度飙升,功能模块复杂且庞大,单体架构严重阻塞了开发部署的效率,业务功能解耦,单独模块可并行开发部署的微服务架构逐渐流行开来,业务的精细化管理不可避免地推动着基础资源利用率的提升。虚拟化技术打通了物理资源的隔阂,减轻了用户管理基础架构的负担。容器/PaaS平台则进一步抽象,提供了应用的依赖服务、运行环境和底层所需的计算资源,这使得应用的开发、部署和运维的整体效率再度提升。Serverless架构技术则将计算抽象得更加彻底,将应用架构栈中的各类资源的管理全部委托给平台,免去基础设施的运维,使用户能够聚焦高价值的业务领域。而整个过程,实际上就是在诉求或者技术驱动下向Serverless演进。在伯克利团队发表的“Cloud Programming Simplified: A Berkeley View on Serverless Computing”一文中针对Serverful和Serverless也进行了比较详细的总结。

  • 弱化了存储和计算之间的联系。服务的存储和计算被分开部署和收费,存储不再是服务本身的一部分,而是演变成了独立的云服务,这使得计算变得无状态化,更容易调度和扩缩容,同时也降低了数据丢失的风险。
  • 代码的执行不再需要手动分配资源。不需要为服务的运行指定需要的资源(比如使用几台机器、多大的带宽、多大的磁盘等),只需要提供一份代码,剩下的交由Serverless平台去处理就行了。当前阶段实现平台分配资源时还需要用户方提供一些策略,例如单个实例的规格和最大并发数、单实例的最大CPU使用率。理想的情况是通过某些学习算法来进行完全自动的自适应分配。
  • 按使用量计费。Serverless按照服务的使用量(调用次数、时长等)计费,而不是像传统的Serverful服务那样,按照使用的资源(ECS实例、VM的规格等)计费。

Serverless架构的优点主要包括降低运营成本、降低开发成本以及拥有优秀的扩展能力,更简单的管理以及符合“绿色”计算的思想。

在使用传统服务器时可以发现,服务器每时每刻的用户量是不同的,资源使用率也是不同的,可能白天资源使用率比较合理,夜间的时候就会出现大量的资源闲置问题。按照《福布斯》杂志的统计,在商业和企业数据中心的典型服务器仅提供5%~15%的平均最大处理能力的输出。这无疑是一种资源的巨大浪费。而Serverless架构的出现,则可以让用户委托服务提供商管理服务器、数据库和应用程序甚至逻辑,这样一方面减少了用户自己维护的麻烦,另一方面用户可以根据自己实际使用函数的粒度进行成本的支付。对于服务商而言,他们可以将更多的闲置资源进行额外的处理,这从成本的角度、“绿色”计算的角度来说,都是非常不错的。

如图1-15所示,对于用户和开发者而言,Serverless架构有降低人力成本、降低风险、减少资源开销、增加缩放灵活性、缩短创新周期等优点,使用Serverless架构,用户不需要自己维护服务器,也不需要自己操心服务器的各种性能指标和资源利用率,而是可以付出更多的时间和精力去关心和关注应用程序本身的状态和逻辑。同时Serverless应用本身的部署十分容易,只要上传基本的代码即可,例如Python程序只需要上传其逻辑与依赖包,C/C++、Go等语言只需上传其二进制文件,Java只需要上传其Jar包等,无须使用Puppet、Chef、Ansible或Docker来进行配置管理,这大大降低了运维成本。对于运维来说,Serverless架构也不再需要监控底层的数据,例如磁盘使用量、CPU使用率等,可以更加专注地将监控目光放到监控应用程序本身的度量。同时在Serverless架构下,运维人员的工作角色会有所转变,部署将更加自动化,监控将更加面向应用程序本身。

058-01

图1-15 传统项目上线和Serverless下项目上线对比图

在降低风险层面,组件越多、结构越复杂,系统出故障的风险就越大。而在Serverless架构中,很多模块都可以托管给服务商,例如存储系统、API网关系统等,那些以前要自己维护的触发模块、路由模块、存储模块也不再直接维护,如果出现问题,可以交给服务商来处理,让服务商的专业人员来处理有时候比自己来处理更可靠,利用专业人员的知识来降低停机的风险,缩短故障修复的时间,可以让系统的稳定性更高。当然,这一点也充分说明找到一个专业的服务商是非常有必要的。

亚马逊AWS首席云计算技术顾问费良宏曾说:今天大多数公司在开发应用程序并将其部署在服务器上的时候,无论是选择公有云还是私有的数据中心,都需要提前了解究竟需要多少台服务器、多大容量的存储和什么样的数据库功能等,并需要部署运行应用程序和依赖的软件到基础设施之上。假设不想在这些细节上花费精力,是否有一种简单的架构模型能够满足这种想法?这个答案已经存在,这就是今天软件架构世界中新鲜且热门的一个话题——Serverless架构。确实如此,在传统项目上线过程中,需要申请主机资源,这时候一般会非常花时间和精力去评估一个峰值最大开销来申请资源,即使某些服务按照最大消耗去申请资源,也要有专人在不同时间段进行资源的扩容或缩容,以达到保障业务稳定且降低成本的效果;而对于另一些服务来说,有些时候申请的资源还需要在最大开销基础上评估,即使可能出现很多流量波谷,并产生大量的资源浪费,也不得不这样去做,比如数据库这种很难扩展的应用就是“尽管很多时候都觉得浪费资源也比当峰值到来时应用程序因为资源不足而无法服务好”。正如费良宏所说,在Serverless架构下,这个问题得到了比较好的解决,不计划到底需要使用多少资源,而是根据实际需要来请求资源;根据使用时间来付费,根据每次申请的计算资源来付费,让计费的粒度更小,将更有利于降低资源的开销。这是对应用程序本身的优化,例如让每次请求耗时更短,让每次消耗的资源更少,能够显著节省成本。

CNCF也对Serverless架构的优点进行了总结,认为Serverless架构拥有零服务器运维和空闲时无计算成本两个优点,其中零服务器运维指不需要配置、更新和管理服务器基础架构,并具有灵活的可扩展性。

综上所述,Serverless架构的优势非常明显。

  • 降本提效。云厂商为使用者提供服务器的管理和运维工作,为使用者提供数据库、对象存储等BaaS服务,让用户将更多的注意力放在自身的业务逻辑上,提升研发效率,缩短项目的创新周期。同时Serverless的使用者不用担心服务器运维、基础设施运维等工作,更不用承担相应的成本等。Serverless架构提供了较为完善、全面的按量付费模型,使用者只需按照自己实际使用的资源量付费即可。
  • 安全、方便、可靠。把更专业的事情交给更专业的人去做,Serverless架构将更多服务器运维、安全相关的事情交给云厂商来做,大规模提升项目整体的安全性。Serverless架构明显比其他架构更简单,因为更多的BaaS服务都是云厂商提供的,使用者将会管理更少的组件,这意味着Serverless的使用者可以更简单更方便地管理项目。另外,Serverless架构拥有弹性能力,即自动伸缩的能力,让项目在流量增加的时候可自动扩容,在流量降低的时候可自动缩容,进而保证整个业务的安全、稳定。

1.3.2 面临的挑战

当然,事物并没有十全十美的,Serverless架构也不例外,在Serverless架构为使用者提供全新的编程范式的同时,当用户在享受Serverless带来的第一波技术红利的时候,Serverless的缺点也逐渐地暴露了出来,例如函数的冷启动问题,就是如今颇为严峻且备受关注的问题。

由于Serverless架构具有弹性伸缩的能力,Serverless服务的供应商会根据用户服务的流量波动进行实例的增加或缩减,其示意图如图1-16所示。

060-01

图1-16 函数计算根据流量进行函数扩缩示意图

以阿里云函数计算为例,当系统接收到第一个触发函数的事件时,它将启动一个容器来运行代码。如果此时收到了新的事件,而第一个容器仍在处理上一个事件,平台将启动第二个代码实例来处理第二个事件,Serverless架构的这种自动的零管理水平缩放,将持续到有足够的代码实例来处理所有的工作负载为止。当然,不仅仅是并发情况下会比较容易触发函数冷启动,在函数的前后两次触发时间间隔超过了实例释放时间的阈值时,也会触发函数的冷启动,如图1-17所示。

060-02

图1-17 函数冷启动产生示意图

然而这里就涉及一个问题,当新的请求或者说是事件到来时,在广义上可能出现以下两种情况。

  • 存在空闲且可以直接复用的实例:热启动。
  • 不存在空闲且可以直接复用的实例:冷启动。

在本地执行一个函数,通常情况下是环境都已经准备妥当,每次执行只需要执行函数对应的方法即可,但是Serverless架构下并不是,本地与FaaS的函数调用区别示意图如图1-18所示。

061-01

图1-18 本地与FaaS的函数调用区别示意图

在Serverless架构下,开发者提交代码之后,通常情况下,代码只会被持久化,并不会为其准备执行环境,所以当函数第一次被触发时会有一个比较漫长的准备环境的过程,这个过程包括把网络的环境全部打通、将所需的文件和代码等资源准备好,这个从准备环境开始到函数被执行的过程被称为函数的冷启动。

New Relic网站上曾发表过一篇研究AWS Lambda冷启动时间的文章,其分析图如图1-19所示。

061-02

图1-19 AWS Lambda的冷启动时间研究和分析图

研究结果表明,当对AWS Lambda发起请求时,大部分的请求都落在了50ms以内,但还是有很多请求超过100ms甚至是150ms,这也充分说明了冷启动的存在。不同厂商对于冷启动的优化程度是不同的,曾有人对AWS Lambda、Azure Function以及Google Cloud Function三个工业级的Serverless架构产品进行过冷启动测试,并将函数启动划分成四个部分,如图1-20所示。

062-01

图1-20 函数启动的四个部分

通过这四个部分,其实可以简单地区分出冷启动和热启动的区别。冷启动包括准备环境的过程,就是当请求或者事件到来但没有可复用的实例资源时,系统将会初始化环境,包括网络环境、实例资源等,之后进行一些文件的下载、系统配置,然后再装载代码和一些依赖,最后执行代码。而热启动流程更短,它更多出现在厂商完成了实例的预热或实例的复用的情况下,相对冷启动而言,它的环境、配置、代码都是准备好的,只需要执行代码即可。通常情况下,热启动都是毫秒级启动,而冷启动可能是百毫秒级、秒级。不仅不同厂商对于冷启动的优化程度不同,同一厂商对不同语言的冷启动优化、对同一种语言下不同依赖的优化都是不同的,这也充分说明各厂商也在通过一些规则和策略努力降低冷启动率。

如图1-21所示,通常情况下,冷启动的解决方案包括几个部分:实例复用、实例预热以及资源池化。

062-02

图1-21 函数冷启动常见解决方案

从资源复用层面来说,对实例的复用相对来说比较重要,一个实例并不是在触发完成之后就结束其生命周期,而是会继续保留一段时间。在这段时间内如果函数再次被触发,那么可以优先分配该实例来完成相应的触发请求。在这种情况下可以认为函数的所有资源是准备妥当的,只需要再执行对应的方法即可,所以实例复用是大多数厂商都会采取的一个降低冷启动率的措施。在实例资源复用的方案中,实例静默状态下要被保留多久取决于厂商对成本的考量。保留时间过短会导致请求出现较为严重的冷启动问题,影响用户体验;实例长期不被释放则很难被合理地利用起来,会大幅度提高平台整体成本。

从预热层面来说,解决函数冷启动问题可以通过某些手段判断用户的函数在下一个时间段可能需要多少实例,并且进行实例资源的提前准备。函数预热的方案是大部分云厂商所重视并不断深入探索的方向,常见的预热方案如图1-22所示。

063-01

图1-22 函数预热常见方案

1)被动预热通常指的是非用户主动行为预热,是系统自动预热函数的行为,主要包括规则预热、算法预热以及混合预热,所谓的规则预热是指设定一个实例数量范围(例如每个函数同一时间点最低0个实例,最多300个实例),然后通过一个或几个比例关系进行函数下一时间段的实例数量的扩缩。例如设定某个比例为1.3倍,当前实例数量为110,实际活跃实例数量为100,那么实际活跃数量乘以所设定的比例的结果为130个实例,与当前实际存在时110个实例相比需要额外扩容20个实例,那么系统就会自动将实例数量从110个提升到130个。这种做法在实例数量较多和较少的情况下会出现阔缩数量过大或过小的问题(所以有部分厂商引入不同实例范围内采用不同的比例来解决这个问题),在流量波动较频繁且波峰波谷相差较大的时候,该方案会出现预热滞后的问题。算法预热实际上是根据函数之间的关系、函数的历史特征进行,通过深度学习等算法进行下一时间段的实例的扩缩操作,但是在实际生产过程中,环境是非常复杂的,对流量进行一个较为精确的预测非常困难,所以算法预测的方案是很多人在探索但却迟迟没有落地的一个重要原因。还有一种方案是混合预热,即将规则预热与算法预热进行一个权重划分,共同预测下一时间段的实例数量,并提前决定扩缩行为和扩缩数量等。

2)主动预热通常指的是用户主动进行预热的行为。由于被动预热在复杂环境下的不准确性,所以很多云厂商提供了用户手动预留的能力,目前来说主要分为简单配置和指标配置两种。所谓的简单配置就是设定预留的实例数量,或者某个时间范围内的预留实例数量,所预留的实例将会一直保持存活状态,不会被释放掉。另一种是指标配置,即在简单配置基础上,可以增加一些指标,例如当前预留的空闲容器数量小于某个值时进行某个规律的扩容,反之进行某个规律的缩容等。通常情况下,用户主动预留模式比较适用于有计划的活动,例如某平台在双十一期间要进行促销活动,那么可以设定双十一期间的预留资源以保证高并发下系统良好的稳定性和优秀的响应速度,通常情况下主动预留可能会产生额外的费用。

3)混合预热,即将被动预热和主动预热按照一个权重关系进行结合。如果用户配置了主动预热规则,就执行主动预热规则,辅助被动预热规则;如果用户没有配置主动预热规则,就使用默认的被动预热规则。

最后一种解决冷启动问题的方法是资源池化,但是通常情况下这种所谓的资源池化带来的效果可能不是热启动,可能是温启动。所谓的温启动是指实例所需要的相关资源已经提前准备了,但是并没有完全准备好的情况。所谓的池化就是在实例从零到一的过程中所进行的每一步准备工作,如图1-23所示。

064-01

图1-23 函数池化程度示意图

池化的好处是可以降低实例启动的链路出现完全冷启动的概率,例如VPC层面的池化,可以避免底层资源准备时产生的时间消耗,让启动速度更快。同时池化也可以更加灵活地面对更多情况,例如在运行时层面的池化,可以将池化的实例分配给不同的函数,不同函数被触发的时候,可以优先使用池化资源,达到更快的启动速度。当然池化也是一门学问,例如池化的资源规格、运行时的种类、池化的数量以及资源的分配和调度等。

通常情况下,在冷启动的过程中,比较耗时的环节包括网络资源的打通、实例的底层资源的准备以及运行时等准备。除此之外,对一个实例冷启动有一定影响的还有代码包的大小,过大的代码包可能会导致下载代码时间变长,进一步导致冷启动现象严重。

除了冷启动之外,Serverless架构还存在着厂商锁定等比较严重的问题。厂商锁定问题是很多人非常在意的,由于函数计算需要依靠事件触发,所以事件源以及函数本身与事件源规约的数据结构就显得格外重要。以对象存储为例,对象存储与函数计算所规约的数据结构,不同厂商的数据格式就是不同的。

这就意味着业务逻辑可能需要针对不同厂商进行适配,除此之外很多事件源是不能跨运营商触发的,所以这对业务迁移、多云部署等操作实际上是有一定影响的。

除了厂商锁定之外,Serverless目前缺少完备的开发者工具,这也是比较大的问题,会在不同程度上影响函数的调试和部署、依赖的安装、相关日志的查看以及函数资源的管理。为了改善这个问题,目前各个云厂商都在针对自身产品的特点建立自己的工具链体系,例如AWS Lambda的SAM、阿里云函数计算的Funcraft等。当然,除了各个厂商自己针对自身所推出的开发者工具,也有一些通用性比较强的多云Serverless开发者工具,例如阿里云开源的Serverless Devs等。

综上所述,Serverless架构拥有诸多优点,也面临一些困难和挑战,包括但不限于函数冷启动问题严重、开发工具不完善、厂商锁定严重等问题。Serverless架构虽然已出现了很多年,但是真正步入“元年”并得以快速发展的时间其实还是比较短的,但不可否认的是,近些年Serverless架构的热度在持续上升,人们对它寄予厚望,各个厂商对其投入也非常大,目前所遇到的问题也都是短暂的,Serverless架构会朝着更好用、更易用的方向不断演进。