导读:了解Serverless架构,必须与深入理解其中的弹性计算以及资源分配逻辑。本文摘自2018年的ArchSummit全球架构师峰会。根据来自腾讯技术工程事业群研发专家jerome所做的主题为《基于弹性计算的无服务器化实践》的演讲内容整理而成。
据Gartner和麦肯锡统计,全球的服务器CPU平均利用率只有6%到12%。国内阿里,腾讯等大型互联网企业CPU平均利用率均10%出头左右,这里主要是三种原因导致:
• 单一业务难以均衡利用各类资源导致99%情况下有资源闲置
• 在线服务的特点决定了有30%的时间段处于低负载
• 服务器流转过程可能造成5%的时间段闲置
理想的情况是像Google那样有公司级borg资源调度平台,各业务基于统一平台构建,共享资源池混搭调度,但基于BG分开运营的历史现状,公司运管联合架平弹性计算及各BG运营部门联合打造的共享算力平台,尝试收集全公司的空闲资源,从现网中挖掘金矿,以docker容器的形式满足计算类的需求。
经过一年的建设,当前已挖掘了约140w核,支持了亿级的视频转码,图片压缩以及腾讯围棋,王者荣耀等游戏AI,手机浏览器等,但当前依然存在许多挑战,比如:
比如业务接入门槛较高,需要业务理解多样变化弹性的资源并做好适配,长尾业务接入困难;另外利用率也不受控,有些有状态的业务利用率跑不高,且没法自动扩缩容;小资源碎片,比如剩下1核1GB这种用不出去;最后平台没法准确监控业务的运行状态和质量,比如请求延时等,总结起来,当前困境根源还是我们只是提供资源层面的服务,而资源要用好确依赖很多业务层面的工作。回顾集群资源管理平台走过的历程,从最开始使用物理机(Nothing-as-Service)到IaaS虚拟机服务,再到PaaS容器服务,其实每一步都在尝试为业务做更多的事情,解放更多的生产力同时提升利用效率,下一步资源管理平台能否直接跳出资源服务层级,让业务只需关系业务逻辑,按需付费便可以了呢?
答案是肯定的,它名字叫serverless,serverless不是指没有服务器,而是指用户无须关注服务器的存在,广义上来讲,现在的SaaS,BaaS,FaaS,PaaS都可以称为serverless,区别是从下到上平台控制力越来越强,从左到右,平台通用性越来越好;但Serverless真正广为人知,是因为以aws lambda为代表的FaaS函数即服务的出现,所以当前狭义上来讲,Serverless一般指FaaS函数即服务,我们今天就用Serverless来指代函数即服务。
Serverless正好是解决我们当前问题的答案,业务接入门槛高,用serverelss可以不用关心资源,做到更快业务上线;利用率不受控,用serverless平台可以完全控制资源的分配,利用率不再受制于人;碎片资源用不出去,用serverless可以让业务把计算切成函数更小粒度的函数级别;业务质量难以监控,用serverless我们感知业务的每次调用与返回,知道延时及返回结果。
于是我们基于现有的容器平台构建了serverless云函数平台,主要包括:
• 构建函数管理模块来管理函数的配置,包括元数据,代码,权限,触发方式等;
• 构建函数调用模块来分发调用函数,解决负载均衡,故障容灾,扩缩容等问题;
• 构建多语言的运行时环境,代理函数的网络监听请求,运行用户函数代码,监控函数运行过程,收集函数日志等;
• 构建函数触发器等模块,实现云函数事件触发式的自动调用;
新平台的构建有诸多考虑点,比如怎么做到足够的易用,使得业务可以乐于尝试;怎么做到足够的稳定,来留住业务;如何做到比容器平台的成本更加节省,让业务看到实实在在的好处;如何做到更快、更安全、能持续发展等,本文将一一分享。
足够易用的关键在于能否让用户做更少的事情,在开发上,云函数提炼业务逻辑之外的部分代为实现,比如网络数据包收发,负载均衡,扩缩容,故障容灾等,平台把龙画好,只需要业务最后点睛即可上线;在运维上,托管代码包管理,服务器故障处理,服务质量监控及容灾、负载均衡、扩缩容等配置;甚至在应用上,提供文件上传\删除,定时器,主题的消息时等事件触发式自动调用,虽然当前一些微服务平台也在做类似的事情,但依然以容器为维度,容器内部对平台而言是黑盒;云函数参与了容器内部每一次用户请求调用和返回的过程,可以获取更多业务维度的信息,使得在资源调度和质量控制上可以更加的精准。
对比传统的分布式系统,云函数平台要做到足够稳定还需要解决好函数调用流的问题,最简单的调用流程是从云api进来,走invoker做分发,再到计算节点执行,在同步调用一来一回的场景这样没问题,因为同步调用失败时业务可以自行重试;但异步调用时不会返回调用结果给用户,请求丢了用户感知不到,所以需要有持久化方式保存所有的异步调用,以免丢失;另异步调用业务无法自行重试,当平台自动重试依然失败时,需持久化保存便于问题追溯;重试本身也需要谨慎的设计,因为资源是调用请求到来时实时分配,重试可能会造成后台资源成倍增加,且在某些流式计算场景,计算有严格的时序要求,重试的时候还需要阻塞整个流的计算等。稳定性最常见的挑战是热更新能力,而Serverless云函数平台天生支持热更新,因为能获知业务的每次请求和返回,且能控制业务请求的分发过程,做热更新,其实就是简单的禁掉节点,等待请求结束,更新后再重新启用节点。
云函数平台要比容器平台做到更低成本,需要尽量避免闲置及无效浪费的资源,在传统的业务架构下,一个业务模块最少需要1个实例,如果要容灾,至少需要2个;而云函数平台被设计为调用时实时分配资源,业务模块最小实例数可以缩减到0,无负载时不保留资源,收到业务请求时再实时分配资源;业务申请资源时,会按照峰值指定cpu核心数,内存大小,磁盘容量,网络带宽等一系列资源参数,但绝大多数情况下用不完,所以云函数平台一般只让用户配置内存大小,因为内存是不可压缩资源,少了程序跑不起来,而对于cpu,带宽等可压缩资源,都由平台方根据内存大小及实际所需来配置且动态调整,以避免由于业务过量申请资源造成的浪费;另外云函数有一个特殊点是支持事件触发执行,但触发配置不当可能造成无效循环造成资源浪费,比如配置文件上传时执行某函数,但函数内又上传了文件形成循环触发,所以云函数会监控函数调用流,发现无效循环,避免资源浪费。
由于云函数的资源是收到请求后实时分配并初始化的,而用户能容忍的等待时间一般不超过3s,故第一次请求的冷启动的时间要足够的快,而函数的初始化需要做很多的事情:先要申请资源,确定位置后要下载镜像,下载函数代码,启动容器,初始化函数后才能执行函数,返回结果。其中镜像下载的时间一般都超过3s,所以我们用了很多预处理,缓存和并行化的方式来提升性能,比如镜像预分发到服务器避免实时下载,容器资源使用多级缓存以重利用,小到用指针代替内存拷贝来传递参数等,这里有个小问题,一个用户函数用过的容器可以交给另一个用户去使用吗?并行化容器启动与代码下载流程,函数大参数传递使用共享内存指针方式传递等,最终将冷启动的时间控制到200ms,热启动的时间控制到了5ms内,达到业界一流水准。
云函数平台由于共享粒度更细,不可避免需要共享服务器内核,在安全方面挑战会更大一些,所以我们在docker隔离的基础上,额外做了一些安全措施,比如限制函数运行时环境只有/tmp目录可写,通过seccomp等内核特性限制端口监听,端口扫描等敏感系统调用;但做安全的同时,也关注业务兼容性,比如大家习惯性的使用root来运行程序,禁止root可能会给用户带来一些额外的适配成本,所以我们使用root namespace技术把容器内的root映射成了容器外的普通用户以控制权限,另外由于我们无法审核用户代码,而用户技术上可以写代码去做尝试做任何事情,比如收集运行环境信息,窥探管理服务器位置等,为了安全,对比开源的serverless平台,实现了函数运行环境和管理环境的分离,函数网络包收发代理及日志收集等均放在容器外,容器内函数运行时环境不感知管理节点的的任何IP,端口信息及平台日志等信息。
由于业务除核心逻辑之外的非功能性需求都依托云函数实现,对云函数平台必然会有更多的需求,为支持业务可持续发展,跟上业务迭代速度,我们也做了一些优化,比如在过去一年,业务对我们平台提得最多的需求是支持各种编程语言,运行时环境安装各种库等,为了加快迭代效率,我们提炼了各类语言运行时环境的公共部分用C语言实现,因为各种高级语言均易实现对C库的直接调用,这样新增加一种语言支持能够在1~2周内完成,在库更新方面,我们把所有的运行时库部署到母机上,通过目录mount的方式挂进容器,使得库的更新无须变更运行时环境镜像。
汇总一下刚才的关键设计,我们通过让用户做得更少来提升易用性;谨慎的设计异步调用及重试来提升稳定性;消除闲置及过量申请来降低成本;利用缓存及并行来提升启动速度;通过内核层技术及管理环境与运行环境隔离来提升安全性;通过提炼公共功能及避免重制镜像来提升迭代速度,那实际效果怎样呢?接下来给大家分享一下我们当前的一些用户案例及经验教训。
当前云函数平台在内部最大的用户是游戏AI的特征提取,游戏AI在整个行业目前还处于摸索阶段,算法变化很快,程序经常更新,计算量非常巨大,比如王者荣耀1天需执行上亿录像文件的特征提取。如果基于云函数实现,只需要AI工程师用python写两个函数,一个脚本将从录像文件中提取特征,生成HDF5文件,另一个函数将HDF5文件打散随机化再推送到训练平台进行训练,算法更新时,提交新的函数即可,可以更专注于算法研究,无需再关心服务器的管理,计算的分布,扩缩容,故障容灾等。除游戏AI之外,微信小程序的开发者工具也开始集成云函数正在内测中,欢迎大家体验。
回顾这1年多来的建设历程,最大的教训主要有2点:
• 前不久腾讯云云函数出现了一个安全漏洞,在云函数的上下文通过bash反射,尝试各种linux命令可获知k8s的kubelet访问地址,且由于k8s没有配置鉴权,用户可直接控制k8s去获取其它容器内部数据,造成安全隐患,幸亏公司安全团队及时发现未造成严重后果,这个问题的原因在于我们对k8s的安全机制缺乏了解,给我们的教训是每引入一个开源组件,都需要吃透后再开放服务;
• 由于云函数平台被设计为7*24不间断工作,每次计算节点升级时需要经历屏蔽,等待函数执行完,升级再启用的过程,早年未做自动化,整个集群升级一次要耗费将近一周的时间,拖慢了版本迭代过程;
同时,我们觉得好的经验主要有2点:
• 设计之初就考虑了平台的平滑升级,所以一年来变更了大大小小50来个版本,从未让业务停过机;
• 另外我们重视了平台公共功能的提取,比如多语言支持方面,1~2周可搞定一种新编程语言,远比其它serverless平台快;
在推广serverless云函数平台的过程中,我们发现对于无状态微服务,负载波动大,对延时不敏感的场景,比较容易服务好,但对于有状态的服务,需要业务自行保存状态,对于持续高负载的业务,没法发挥资源按需取用的优势,对于延时敏感应用,由于中间函数的分发多了一层,延时有差不多5ms的损耗,对这些场景,serverless当前还不太适合。
但未来,serverless是可以期待的,当前serverless已经成了各大公有云的标配,对于公有云而言,serverless不仅仅是一种新形态的计算服务,更能充当整个云平台的粘合剂,便于打包推广其它云服务,让公有云从各个分散的云产品集合变成了有机的整体,成为用户公共的云后台;在开源社区,serverless平台也蓬勃发展,每隔一段时间都会出现新的值得关注学习的serverless平台;虽然当前serverless主要战场在数据中心,但随着IOT的发展,IOT边缘设备可能成为serverless更大的战场,因为边缘设备的计算资源管理,软件包发布更为复杂,更加有挑战性,能解决好相当于雪中送炭。
武学修为对各路招数融汇贯通时,可以达到无招胜有招的境界,对于我们做集群资源调度的程序员来说,能把资源调度做到极致,让业务根本感知不到服务器的存在,是我们最高的追求。