1.4 技术难点与挑战
前面我们已经介绍了一些Serverless架构的劣势,实际上,当时举例说明的一些劣势是包括设计特性及技术难点在内的。所谓设计特性就是说,Serverless架构本身设计之初就是无状态的,是函数级别的,所以有超时机制存在也算合理现象,这就导致了用户代码不能长期运行。但是随着时间的推移,技术的发展,以及用户本身需要的函数状态的保留,各大厂商也在积极对云函数进行改造和升级,例如腾讯云Serverless团队提出了HTTP Service概念。除了所谓设计特性产生的劣势,还有技术难点产生的劣势,这一部分也是Serverless架构现在和未来一段时间面临的挑战。
在众多挑战中,函数的冷启动是最值得关注和重视的。使用Serverless架构的时候,一定要考虑冷启动问题,由于函数是无状态的,所以每次执行某个函数,实际上都可能从头开始。这里所谓的从头开始,在某些情况下也指从建立镜像、运行容器到初始化函数及执行函数的过程,对于某些对时间要求严格的项目而言,或者对于某个可能有多个函数组合的请求而言,这样的流程是会大大增加延时的。
2019年5月29日,腾讯云+社区技术沙龙“互联网架构”成功举办,腾讯工程师对函数冷启动部分做了一个描述,如图1.10所示。
图1.10 本地函数调用与云函数调用区别图
开发者提交代码之后你不知道他是否会调用,第一次调用函数会有一个函数冷启动的过程,把网络环境全部打通,这个函数才能提供服务。如果没有优化好冷启动部分,可能一些比较关键的产品首次启动会产生超时,体验非常不好。以前开发者在本地运行函数的时候,并不会关注本地函数执行花了多少毫秒或微秒,但是在云函数场景下就不一样了,云函数有一个部署的过程,无论是在公有云平台上还是在开源方案上,冷启动都是值得不断探讨的话题和优化的方向。
另外,New Relic官方博客上的Understanding AWS Lambda Performance—How Much Do Cold Starts Really Matter?一文曾对AWS Lambda的冷启动时间进行研究和分析,如图1-11所示。
图1.11 AWS冷启动时间
可以看到,大部分请求都落在了50ms以内,但是还有很多请求超过100ms甚至150ms,这也充分说明了冷启动问题的存在。在这篇文章中作者只对AWS Lambda进行了冷启动的分析和研究,但是实际上不同厂商对于冷启动的优化程度是不同的,以文章Serverless:Cold Start War为例,在文章中作者对AWS Lambda、Azure Functions和Google Cloud Function等三个工业化的Serverless产品进行了冷启动测试,作者将函数启动划分成四个部分,如图1.12所示。
图1.12 冷启动与热启动示意图
然后通过对多种语言的“Hello World”与是否有依赖等进行搭配,进行测试,测试结果如图1.13所示。
图1.13 不同厂商的不同语言的冷启动情况测试结果
可以看到,冷启动问题确实存在,而且不同厂商、不同语言、不同测试方法得到的冷启动数据都是有差异的。这也充分说明,各个厂商也在通过一些规则和策略努力降低冷启动率。除此之外,文章Understanding Serverless Cold Start、Everything you need to know about cold starts in AWS Lambda、Keeping Functions Warm、I'm afraid you're thinking about AWS Lambda cold starts all wrong 等也对冷启动现象进行了描述和深入的探讨,并且提出了一些业务侧应对函数冷启动的解决方案和策略。
就目前而言,对函数冷启动问题进行研究和探索,仍然是一件非常重要的事情,无论是在各个云厂商内,还是在部分开发者大会上或者技术沙龙中,只要是关于Serverless架构的问题,冷启动必然出现。若想解决函数冷启动问题,就要先对函数启动过程有所了解,与文章Serverless:Cold Start War中描述的类似,腾讯专家工程师周维跃在分享中这样描述函数启动流程:客户端发起一个请求之后,到了Invoker接入层,先判断函数实例是否已经创建好,如果创建好了这个调用是毫秒级别的,如果没有创建好要走冷启动的过程。冷启动又包含几个关键的阶段。首先就是要创建一个容器,现在容器创建是秒级别的,这个时间是比较长的。当你创建完容器以后,还没有代码,因为代码在代码仓库你需要去下载。当然不要看你是否已经缓存过,如果缓存过就不需要下载。代码下载部署到容器后面,函数要对外访问还需要一些打通网络的过程,在那么大的规模下打通网络的过程涉及大量路由数据的下发,现在基本上是秒级别的。
从以上描述中可以分析出,目前函数冷启动产生的主要原因是容器启动、代码载入及网络等周边资源准备得从零开始,占用了大量的时间。如果要解决这个问题,则需要从几个方面入手,如图1.14所示。
图1.14 降低函数冷启动基本方案
从复用层面来说,对容器的复用是相对来说比较重要的,数据用完以后就不要太急着销毁它,因为有可能下次还要使用,我们可以在接入层对函数实例进行一定程度的复用保留。但是这里又有新的问题,函数执行结束,我们不进行销毁,要保留多久呢?这个时间阈值也是值得众多厂商进行研究和分析的。
从预热层面来说,解决冷启动问题或者说降低冷启动率,可以从容器预创建、热点代码缓存、网络优化、预启动等几个维度进行探索及优化。关于容器预创建,有一个预创建池子,只是空的容器,并不包含代码,这个容器池设置多大,是值得考虑和探索的,太小可能突然来了一些请求会把这个池子消耗掉,太大又将面临成本的问题;关于热点代码缓存,函数平台会有一个代码管理仓库,在容器已经准备好的情况下就会涉及代码的下载过程,此时热点代码二级缓存是非常重要的,一是在容器的Node本地缓存,二是在机房内缓存,缓存需要资源调度的配合,其主要目的就是希望系统可以快速找到需要的代码所在的位置,并且可以快速初始化到容器中。对于网络等相关资源的优化,现在传统的函数平台都是函数容器里绑定弹性网卡去访问开发者其他资源的,这个网络的部署是秒级别的,但是腾讯云Serverless团队则把弹性网卡绑在函数容器上,转移到集中网关上,并且使网络模块的冷启动时间从秒级降到毫秒级,极大地减少了弹性网卡的消耗时间,转移到网关的方案只需要消耗两个IP就够了。预启动是非常难的,要在预启动层面降低冷启动率,一是要知道如何确定是否需要扩容,二是缩短扩容请求响应时间,当函数数量非常大的时候,做到实时地分析所有函数下一个5秒是否需要扩容,三是降低函数实例启动时间。
当然,虽然我们已经明确影响冷启动的因素,以及优化冷启动的路径,但是实际上在做每一步优化的时候,都是要进行非常多的探索与调研的。所以在解决冷启动的问题方面,还面临着非常严峻的挑战。
除了冷启动,Serverless架构还存在着不同厂商可能存在不同的一些规范及标准的问题,这就导致了云函数过分依赖第三方,一旦因为某些不可抗因素涉及服务迁移,就需要付出巨大的成本,包括人力资源及精力等。另外,Serverless架构的本地调试及代码打包,也面临着一些挑战,例如每次想要调试代码,可能都需要将代码部署到云端进行调试,这是一个非常浪费时间和精力的过程。除此之外,很多语言在使用的时候需要依赖二进制包,这时候就可能需要本地的环境与服务商所提供的云函数环境保持严格一致,否则可能会出现异常。
总而言之,Serverless架构毕竟是刚刚兴起的,其面临的困难和挑战还是很多的。但是无论如何,不可否认的是,Serverless架构的热度在持续上升,人们对它的期望和关注也在日益增加。