美团Serverless平台Nest的探索与实践

 

Serverless是目前比较热门的技术话题,各大云平台以及互联网大厂内部都在积极建设Serverless产品。本文将介绍美团Serverless产品在落地过程中的一些实践经验,其中包括技术选型的考量、系统的详细设计、系统稳定性优化、产品的周边生态建设以及在美团的落地情况。虽然各个公司的背景不尽相同,但总有一些可以相互借鉴的思路或方法,希望能给大家带来一些启发或者帮助。

  • 1 背景

  • 2 快速验证,落地MVP版本

    • 2.1 技术选型

    • 2.2 架构设计

    • 2.3 流程设计

    • 2.4 函数触发

    • 2.5 函数执行

    • 2.6 弹性伸缩

  • 3 优化核心技术,保障业务稳定性

    • 3.1 弹性伸缩优化

    • 3.2 冷启动优化

    • 3.3 高可用保障

    • 3.4 容器稳定性优化

  • 4 完善生态,落实收益

    • 4.1 提供研发工具

    • 4.2 融合技术生态

    • 4.3 开放平台能力

    • 4.4 支持合并部署

  • 5 落地场景、收益

    • 5.1 落地场景

    • 5.2 落地收益

  • 6 未来规划

  • 作者简介

  • 招聘信息

1 背景

Serverless一词于2012年被提出,2014年由于亚马逊的AWS Lambda无服务器计算服务的兴起,而被大家广泛认知。Serverless通常被直译成“无服务器”,无服务器计算是可以让用户在不考虑服务器的情况下构建并运行应用程序。使用无服务器计算,应用程序仍在服务器上运行,但所有服务器管理工作均由Serverless平台负责。如机器申请、代码发布、机器宕机、实例扩缩容、机房容灾等都由平台帮助自动完成,业务开发只需考虑业务逻辑的实现即可。

回顾计算行业的发展历程,基础设施从物理机到虚拟机,再从虚拟机到容器;服务架构从传统单体应用架构到SOA架构,再从SOA架构到微服务架构。从基础设施和服务架构两条主线来看整体技术发展趋势,大家可能会发现,不论是基础设施还是服务架构,都是从大往小或者由巨到微的方向上演进,这种演变的本质原则无非是解决资源成本或者研发效率的问题。当然,Serverless也不例外,它也是用来解决这两个方面的问题:

  • 资源利用率:Serverless产品支持快速弹性伸缩能力,能够帮助业务提升资源利用率,在业务流量高峰时,业务的计算能力、容量自动扩容,承载更多的用户请求,而在业务流量下降时,所使用的资源也会同时收缩,避免资源浪费。

  • 研发运维效率:在Serverless上开发人员一般只需要填写代码路径或者上传代码包,平台能够帮助完成构建、部署的工作。开发人员不直接面对机器,对于机器的管理,机器是否正常以及流量高低峰的是否需要扩缩容等问题,这些统统不需要去考虑,由Serverless产品帮助研发人员去完成。这样就能使他们从繁琐的运维工作中解放出来,从DevOps转向NoOps,更加专注于业务逻辑的实现。

虽然AWS在2014年就推出了第一个Serverless产品Lambda,但Serverless技术在国内的应用一直不温不火。不过近两三年,在容器、Kubernetes以及云原生等技术的推动下,Serverless技术迅速发展,国内各大互联网公司都在积极建设Serverless相关产品,探索Serverless技术的落地。在这种背景下,美团也于2019年初开始了Serverless平台的建设,内部项目名称为Nest

截止到目前,Nest平台已经过两年的建设,回顾整体的建设过程,主要经历了以下三个阶段:

  • 快速验证,落地MVP版本:我们通过技术选型、产品与架构设计、开发迭代,快速落地了Serverless产品的基本的能力,如构建、发布、弹性伸缩、对接触发源、执行函数等。上线后,我们推进了一些业务的试点接入,帮助验证打磨产品。

  • 优化核心技术,保障业务稳定性:有了前期的试点业务验证,我们很快发现产品的存在的一些稳定性相关的问题,主要有弹性伸缩的稳定性、冷启动的速度、系统与业务的可用性、容器的稳定性。针对这些问题我们对各个问题涉及的技术点做了专项的优化改进。

  • 完善技术生态,落实收益:优化了核心技术点后,产品逐渐成熟稳定,但依然面临生态性问题,如研发工具欠缺,上下游产品没有打通、平台开放能力不足等问题,影响或阻碍了产品的推广使用。因此,我们继续完善产品的技术生态,扫清业务接入使用障碍,落实产品的业务收益。

2 快速验证,落地MVP版本

2.1 技术选型

建设Nest平台,首要解决的就是技术选型问题,Nest主要涉及三个关键点的选型:演进路线、基础设施、开发语言。

2.1.1 演进路线

起初Serverless服务主要包含FaaS(Function as a Service)和BaaS(Backend as a Service),近几年Serverless的产品领域有所扩张,它还包含面向应用的Serverless服务。

  • FaaS:是运行在一个无状态的计算容器中的函数服务,函数通常是事件驱动、生命周期很短(甚至只有一次调用)、完全由第三方管理的。业界相关FaaS产品有AWS的Lambda、阿里云的函数计算等。

  • BaaS:是建立在云服务生态之上的后端服务。业界相关BaaS产品包括AWS的S3、DynamoDB等。

面向应用的Serverless服务:如Knative,它提供了从代码包到镜像的构建、部署以及实例弹性伸缩等全面的服务托管能力,公有云产品有Google Cloud Run(基于Knative)、阿里云的SAE(Serverless Application Engine)。

在美团内部,BaaS产品其实就是内部的中间件以及底层服务等,它们经过多年的发展,已经非常丰富且成熟了。因此,在美团的Serverless产品演进主要在函数计算服务和面向应用的Serverless服务两个方向上。那究竟该如何演进呢?当时主要考虑到在业界FaaS函数计算服务相对于面向应用的Serverless服务来说,更加成熟且确定。因此,我们决定“先建设FaaS函数计算服务,再建设面向应用的Serverless服务”这样一条演进路线。

2.1.2 基础设施

由于弹性伸缩是Serverless平台必备的能力,因此Serverless必然涉及到底层资源的调度和管理。这也是为什么当前业界有很多开源的Serverless产品(如OpenFaaS、Fission、Nuclio、Knative等)是基于Kubernetes来实现的,因为这种选型能够充分利用Kubernetes的基础设施的管理能力。在美团内部基础设施产品是Hulk,虽然Hulk是基于Kubernetes封装后的产品,但Hulk在落地之初考虑到落地难度以及各种原因,最终未按照原生的方式来使用Kubernetes,并且在容器层采用的也是富容器模式。

在这种历史背景下,我们在做基础设施选型时就面临两种选项:一是使用公司的Hulk来作为Nest的基础设施(非原生Kubernetes),二是采用原生Kubernetes基础设施。我们考虑到当前业界使用原生Kubernetes是主流趋势并且使用原生Kubernetes还能充分利用Kubernetes原生能力,可以减少重复开发。因此,最终考量的结果是我们采用了原生Kubernetes作为我们的基础设施

2.1.3 开发语言

虽然无论在云原生领域,还是Kubernetes的生态中,Golang都更加主流,但在美团Java才是使用最广泛的语言,相比Golang,Java在公司内部生态比较好。因此,在语言的选型上我们选择了Java语言。在Nest产品开发之初,Kubernetes社区的Java客户端还不够完善,但随着项目的推进,社区的Java客户端也逐渐丰富了起来,目前已经完全够用了。另外,我们也在使用过程中,也贡献了一些Pull Request,反哺了社区。

2.2 架构设计

基于以上的演进路线、基础设施、开发语言的选型,我们进行了Nest产品的架构设计。

在整体的架构上,流量由EventTrigger(事件触发源,如Nginx、应用网关、定时任务、消息队列、RPC调用等)触发到Nest平台,Nest平台内会根据流量的特征路由到具体函数实例,触发函数执行,而函数内部代码逻辑可以调用公司内的各个BaaS服务,最终完成函数的执行,返回结果。

图1 FaaS架构图

在技术实现上,Nest平台使用Kubernetes作为基础底座并适当参考了一些Knative的优秀设计,在其架构内部主要由以下几个核心部分组成:

  • 事件网关:核心能力是负责对接外部事件源的流量,然后路由到函数实例上;另外,网关还负责统计各个函数的进出流量信息,为弹性伸缩模块提供伸缩决策的数据支撑。

  • 弹性伸缩:核心能力是负责函数实例的弹性伸缩,伸缩主要根据函数运行的流量数据以及实例阈值配置计算函数目标实例个数,然后借助Kubernetes的资源控制能力,调整函数实例的个数。

  • 控制器:核心能力是负责Kubernetes CRD(Custom Resource Definition)的控制逻辑实现。

  • 函数实例:函数的运行实例。当事件网关流量触发过来,会在函数实例内执行相应的函数代码逻辑。

  • 治理平台:面向用户使用的平台,负责函数的构建、版本、发布以及一些函数元信息的管理等。

图2 Nest架构图

2.3 流程设计

在具体的CI/CD流程上,Nest又与传统的模式有何区别呢?为了说明这个问题,我们先来看一看在Nest平台上函数的整体生命周期怎样的?具体有以下四个阶段:构建、版本、部署、伸缩。

  • 构建:开发的代码和配置通过构建生成镜像或可执行文件。

  • 版本:构建生成的镜像或可执行文件加上发布配置形成一个不可变的版本。

  • 部署:将版本发布,即完成部署。

  • 伸缩:根据函数实例的流量以及负载等信息,来进行实例的弹性扩缩容。

就这四个阶段来看,Nest与传统的CI/CD流程本质区别在于部署和伸缩:传统的部署是感知机器的,一般是将代码包发布到确定的机器上,但Serverless是要向用户屏蔽机器的(在部署时,可能函数的实例数还是0);另外,传统的模式一般是不具备动态扩缩容的,而Serverless则不同,Serverless平台会根据业务的自身流量需要,进行动态扩缩容。后续章节会详细讲解弹性伸缩,因此这里我们只探讨部署的设计。

部署的核心点在于如何向用户屏蔽机器?对于这个问题,我们抽象了机器,提出了分组的概念,分组是由SET(单元化架构的标识,机器上会带有该标识)、泳道(测试环境隔离标识,机器上会带有该标识)、区域(上海、北京等)三个信息组成。用户部署只需在相应的分组上进行操作,而不用涉及到具体机器。能够做到这些的背后,是由Nest平台帮助用户管理了机器资源,每次部署会根据分组信息来实时初始化相应的机器实例。

图3 函数生命周期

2.4 函数触发

函数的执行是由事件触发的。完成函数的触发,需要实现以下四个流程:

  • 流量引入:向事件源注册事件网关的信息,将流量引入到事件网关。如针对MQ事件源,通过注册MQ的消费组,引入MQ的流量到事件网关。

  • 流量适配:事件网关对事件源进入的流量进行适配对接。

  • 函数发现:对函数元数据(函数实例信息、配置信息等)的获取过程,类似微服务的服务发现过程。事件网关接受的事件流量需要发送到具体的函数实例,这就需要对函数进行发现。这里发现实质是获取Kubernetes中的内置资源或者CRD资源中存储的信息。

  • 函数路由:事件流量的路由过程,路由到特定的函数实例上。这里为了支持传统路由逻辑(如SET、泳道、区域路由等)以及版本路由能力,我们采用了多层路由,第一层路由到分组(SET、泳道、区域路由),第二层路由到具体版本。同版本内的实例,通过负载均衡器选择出具体实例。另外,通过该版本路由,我们很轻松的支持了金丝雀、蓝绿发布。

图4 函数触发

2.5 函数执行

函数不同于传统的服务,传统的服务是个可执行的程序,但函数不同,函数是代码片段,自身是不能单独执行的。那流量触发到函数实例后,函数是如何执行的呢?

函数的执行的首要问题是函数的运行环境:由于Nest平台是基于Kubernetes实现的,因此函数一定是运行在Kubernetes的Pod(实例)内,Pod内部是容器,容器的内部是运行时,运行时是函数流量接收的入口,最终也是由运行时来触发函数的执行。一切看起来是那么的顺利成章,但我们在落地时是还是遇到了一些困难,最主要的困难是让开发同学可以在函数内无缝的使用公司内的组件,如OCTO(服务框架)、Celler(缓存系统)、DB等。

在美团的技术体系中,由于多年的技术沉淀,很难在一个纯粹的容器(没有任何其他依赖)中运行公司的业务逻辑。因为公司的容器中沉淀了很多环境或服务治理等能力,如服务治理的Agent服务以及实例环境配置、网络配置等。

因此,为了业务在函数内无缝的使用公司内的组件,我们复用公司的容器体系来降低业务编写函数的成本。但复用公司的容器体系也没那么简单,因为在公司内没有人试过这条路,Nest是公司第一个基于原生Kubernetes建设的平台,“第一个吃螃蟹的人”总会遇到一些坑。对于这些坑,我们只能在推进过程中“逢山开路,遇水搭桥”,遇到一个解决一个。总结下来,其中最核心的是在容器的启动环节打通的CMDB等技术体系,让运行函数的容器与开发同学平时申请的机器用起来没有任何区别。

图5 函数执行

2.6 弹性伸缩

弹性伸缩的核心问题主要有三个:什么时候伸缩,伸缩多少,伸缩的速度快不快?也就是伸缩时机、伸缩算法、伸缩速度的问题。

  • 伸缩时机:根据流量Metrics实时计算函数期望实例数,进⾏扩缩。流量的Metrics数据来自于事件网关,这里主要统计函数的并发度指标,弹性伸缩组件每秒中会主动从事件网关获取一次Metrics数据。

  • 伸缩算法:并发度/单实例阈值=期望实例数。根据收集的Metrics数据以及业务配置的阈值,通过算法计算出期望的实例数,然后通过Kubernetes接口设置具体实例数。整个算法看起来虽然简单,但非常稳定、鲁棒性好。

  • 伸缩速度:主要取决于冷启动时间,在下个章节会详细讲解这块内容。

除了基本的扩缩容能力,我们还支持了伸缩到0,支持配置最大、最小实例数(最小实例即预留实例)。伸缩到0的具体实现是,我们在事件网关内部增加了激活器模块,当函数无实例时,会将函数的请求流量缓存在激活器内部,然后立即通过流量的Metrics去驱动弹性伸缩组件进行扩容,等扩容的实例启动完成后,激活器再将缓存的请求重试到扩容的实例上触发函数执行。

图6 弹性伸缩

3 优化核心技术,保障业务稳定性

3.1 弹性伸缩优化

上面提到的伸缩时机、伸缩算法、伸缩速度这三要素都是理想情况下的模型,尤其是伸缩速度,当前技术根本做不到毫秒级别的扩缩容。因此,在线上实际场景中,弹性伸缩会存在一些不符合预期的情况,比如实例伸缩比较频繁或者扩容来不及,导致服务不太稳定的问题。

  • 针对实例伸缩比较频繁问题,我们在弹性伸缩组件内维护了统计数据的滑动窗⼝,通过计算均值来平滑指标,还通过延时缩容,实时扩容来缓解频繁扩缩问题。另外,我们增加了基于QPS指标的伸缩策略,因为QPS指标相对并发度指标会更加稳定。

  • 针对扩容来不及问题,我们采取提前扩容的手段,当达到实例阈值的70%就扩容,能够比较好的缓解这个问题。除此之外,我们还支持了多指标混合伸缩(并发度、QPS、CPU、Memory),定时伸缩等策略,满足各种业务需求。

下图展示的是线上弹性伸缩的真实案例(配置的最小实例数为4,单实例阈值100,阈值使用率0.7),其中上半部分是业务每秒的请求数,下半部分是扩缩实例的决策图,可以看到在成功率100%的情况下,业务完美应对流量高峰。

图7 弹性伸缩案例

3.2 冷启动优化

冷启动是指在函数调用链路中包含了资源调度、镜像/代码下载、启动容器、运行时初始化、用户代码初始化等环节。当冷启动完成后,函数实例就绪,后续请求就能直接被函数执行。冷启动在Serverless领域至关重要,它的耗时决定了弹性伸缩的速度。

所谓“天下武功,无坚不破,唯快不破”,这句话在Serverless领域也同样受用。试想如果拉起一个实例足够快,快到毫秒级别,那几乎所有的函数实例都可以缩容到0,等有流量时,再扩容实例处理请求,这对于存在高低峰流量的业务将极大的节省机器资源成本。当然,理想很丰满,现实很骨感。做到毫秒级别几乎不可能。但只要冷启动时间越来越短,成本自然就会越来越低,另外,极短的冷启动时间对伸缩时函数的可用性以及稳定性都有莫大的好处。

图8 冷启动的各个阶段

冷启动优化是个循序渐进的过程,我们对冷启动优化主要经历了三个阶段:镜像启动优化、资源池优化、核心路径优化。

  • 镜像启动优化:我们对镜像启动过程中的耗时环节(启动容器和运行时初始化)进行了针对性优化,主要对容器IO限速、一些特殊Agent启动耗时、启动盘与数据盘数据拷贝等关键点的优化,最终将启动过程中的系统耗时从42s优化到12s左右。

图9 镜像启动优化成果
  • 资源池优化:镜像启动耗时优化到12s,基本已经快达到瓶颈点,再继续优化空间不大。因此,我们想能否绕开镜像启动的耗时环节?最终,我们采用了一个比较简单思路“空间换时间”,用资源池方案:缓存一些已启动的实例,当需要扩容时,直接从资源池获取实例,绕开镜像启动容器的环节,最终效果很明显,将启动的系统耗时从12s优化到3s。这里需要说明的是资源池自身也是通过Kubernetes的Depolyment进行管理,池中实例被取走会立即自动补充。

图10 资源池优化成果
  • 核心路径优化:在资源池优化的基础上,我们再次精益求精,针对启动流程中的下载与解压代码两个耗时环节进行优化,过程中我们采用了高性能的压缩解压算法(LZ4与Zstd)以及并行下载和解压技术,效果非常好。另外,我们还支持了通用逻辑(中间件、依赖包等)下沉,通过预加载的方式,最终将函数端到端的启动耗时优化到2s,这就意味着扩容一个函数实例只需要2s(包含函数启动)。如果排除掉函数自身的初始化启动耗时,平台侧的耗时已在毫秒级别。

3.3 高可用保障

说到高可用,对于一般的平台,指的就是平台自身的高可用,但Nest平台有所不同,Nest的高可用还包含托管在Nest平台上的函数。因此,Nest的高可用保障需要从平台和业务函数两个方面着手。

3.3.1 平台高可用

对平台的高可用,Nest主要从架构层、服务层、监控运营层、业务视角层面都做了全面的保障。

  • 架构层:我们针对有状态服务,如弹性伸缩模块,采用了主从架构,当主节点异常时从节点会立即替换。另外,我们还实现了架构上的多层隔离。横向地域隔离:Kubernetes两地两集群强隔离、服务(事件网关、弹性伸缩)集群内两地弱隔离(上海的弹性伸缩只负责上海Kubernetes集群内的业务伸缩,事件网关存在两地调用需求,需访问两地Kubernetes)。纵向业务线隔离:服务业务线强隔离,不同业务线使用不同集群服务;在Kubernetes层的资源用namespace实现业务线弱隔离。

图11 部署架构
  • 服务层:主要指的是事件网关服务,由于所有的函数流量都经过事件网关,因此事件网关的可用性尤为重要,这层我们支持了限流和异步化,保障服务的稳定性。

  • 监控运营层:主要通过完善系统监控告警、梳理核心链路并推动相关依赖方进行治理。另外,我们会定期梳理SOP并通过故障演练平台实施故障注入演练,发现系统隐患问题。

  • 业务视角层:我们开发了在线不间断实时巡检服务,通过模拟用户函数的请求流量,实时检测系统的核心链路是否正常。

3.3.2 业务高可用

对于业务高可用,Nest主要从服务层、平台层两个层面做了相关的保障。

  • 服务层:支持了业务降级、限流能力:当后端函数故障时,可通过降级配置,返回降级结果。针对异常的函数流量,平台支持限制其流量,防止后端函数实例的被异常流量打垮。

  • 平台层:支持了实例保活、多层级容灾以及丰富的监控告警能力:当函数实例异常时,平台会自动隔离该实例并立即扩容新实例。平台支持业务多地区部署,在同地区将函数实例尽可能打散不同机房。当宿主机、机房、地区故障时,会立即在可用宿主机、可用机房或可用区重建新实例。另外,平台自动帮业务提供了函数在时延、成功率、实例伸缩、请求数等多种指标的监控,当在这些指标不符合预期时,自动触发告警,通知业务开发和管理员。

图12 业务监控

3.4 容器稳定性优化

前文已提到,Serverless与传统模式在CI/CD流程上是不同的,传统模式都是事先准备好机器然后部署程序,而Serverless则是根据流量的高低峰实时弹性扩缩容实例。当新实例扩容出来后,会立即处理业务流量。这听起来貌似没什么毛病,但在富容器生态下是存在一些问题的:我们发现刚扩容的机器负载非常高,导致一些业务请求执行失败,影响业务可用性。

分析后发现主要是因为容器启动后,运维工具会进行Agent升级、配置修改等操作,这些操作非常耗CPU。同在一个富容器中,自然就抢占了函数进程的资源,导致用户进程不稳定。另外,函数实例的资源配置一般比传统服务的机器要小很多,这也加剧了该问题的严重性。基于此,我们参考业界,联合容器设施团队,落地了轻量级容器,将运维的所有Agent放到Sidecar容器中,而业务的进程单独放到App容器中。采用这种容器的隔离机制,保障业务的稳定性。同时,我们也推动了容器裁剪计划,去掉一些不必要的Agent。

图13 轻量级容器

4 完善生态,落实收益

Serverless是个系统工程,在技术上涉及到Kubernetes、容器、操作系统、JVM、运行时等各种技术,在平台能力上涉及到CI/CD各个流程的方方面面。

为了给用户提供极致的开发体验,我们为用户提供了开发工具的支持,如CLI(Command Line Interface)、WebIDE等。为了解决现有上下游技术产品的交互的问题,我们与公司现有的技术生态做了融合打通,方便开发同学使用。为了方便下游的集成平台对接,我们开放了平台的API,实现Nest赋能各下游平台。针对容器过重,系统开销大,导致低频业务函数自身资源利用率不高的问题,我们支持了函数合并部署,成倍提升资源利用率。

4.1 提供研发工具

开发工具能够降低平台的使用成本,帮助开发同学快速的进行CI/CD流程。目前Nest提供了CLI工具,帮助开发同学快速完成创建应用、本地构建、本地测试、Debug、远程发布等操作。Nest还提供了WebIDE,支持在线一站式完成代码的修改、构建、发布、测试。

4.2 融合技术生态

仅支持这些研发工具还是不够的,项目推广使用后,我们很快就发现开发同学对平台有了新的需求,如无法在Pipeline流水线、线下服务实例编排平台上完成对函数的操作,这对我们项目的推广也形成了一些阻碍。因此,我们融合这些公司的成熟技术生态,打通了Pipeline流水线等平台,融入到现有的上下游技术体系内,解决用户的后顾之忧。

4.3 开放平台能力

有很多Nest的下游解决方案平台,如SSR(Server Side Render)、服务编排平台等,通过对接Nest的OpenAPI,实现了生产力的进一步解放。例如,不用让开发同学自己去申请、管理和运维机器资源,就能够让用户非常快速的实现一个SSR项目或者编排程序从0到1的创建、发布与托管。

Nest除了开放了平台的API,还对用户提供了自定义资源池的能力,拥有了该项能力,开发同学可以定制自己的资源池,定制自己的机器环境,甚至可以下沉一些通用的逻辑,实现冷启动的进一步优化。

4.4 支持合并部署

合并部署指的是将多个函数部署在一个机器实例内。合并部署的背景主要有两个:

  • 当前的容器较重,容器自身的系统开销较大,导致业务进程资源利用率不高(尤其是低频业务)。

  • 在冷启动耗时不能满足业务对时延的要求的情况下,我们通过预留实例来解决业务的需求。

基于这两个背景,我们考虑支持合并部署,将一些低频的函数部署到同一个机器实例内,来提升预留实例中业务进程的资源利用率。

在具体实现上,我们参考Kubernetes的设计方案,设计了一套基于Sandbox的函数合并部署体系(每个Sandbox就是一个函数资源),将Pod类比成Kubernetes的Node资源,Sandbox类比成Kubernetes的Pod资源,Nest Sidecar类比成Kubelet。为了实现Sandbox特有的部署、调度等能力,我们还自定义了一些Kubernetes资源(如SandboxDeployment、SandboxReplicaSet、SandboxEndpoints等)来支持函数动态插拔到具体的Pod实例上。

图14 合并部署架构

除此之外,在合并部署的形态下,函数之间的隔离性也是不可回避的问题。为了尽可能的解决函数(合并在同一个实例中)之间的互相干扰问题,在Runtime的实现上,我们针对Node.js和Java语言的特点采取了不同的策略:Node.js语言的函数使用不同的进程来实现隔离,而Java语言的函数,我们采用类加载隔离。采用这种策略的主要原因是由于Java进程占用内存空间相较于Node.js进程会大很多。

5 落地场景、收益

目前Nest产品在美团前端Node.js领域非常受欢迎,也是落地最广泛的技术栈。当前Nest产品在美团前端已实现了规模化落地,几乎涵盖了所有业务线,接入了大量的B/C端的核心流量。

5.1 落地场景

具体的落地前端场景有:BFF(Backend For Frontend)、CSR(Client Side Render)/SSR(Server Side Render)、后台管理平台、定时任务、数据处理等。

  • BFF场景:BFF层主要为前端页面提供数据,采用Serverless模式,前端同学不需要考虑不擅长的运维环节,轻松实现了BFF向SFF(Serverless For Frontend)模式的转变。

  • CSR/SSR场景:CSR/SSR指的是客户端渲染和服务端渲染,有了Serverless平台,不用考虑运维环节,更多的前端业务来尝试使用SSR来实现前端首屏的快速展现。

  • 后台管理平台场景:公司有很多的后台管理平台的Web服务,它们虽然相较于函数是比较重的,但完全可以直接托管Serverless平台,充分享受Serverless平台极致的发布和运维效率。

  • 定时任务场景:公司存在很多周期性任务,如每隔几秒拉取数据,每天0点清理日志,每小时收集全量数据并生成报表等,Serverless平台直接与任务调度系统打通,只需写好任务的处理逻辑并在平台上配置定时触发器,即完成定时任务的接入,完全不用管理机器资源。

  • 数据处理场景:将MQ Topic作为事件源接入Serverless平台,平台会自动订阅Topic的消息,当有消息消费时,触发函数执行,类似定时任务场景,作为用户也只需写好数据处理的逻辑并在平台上配置好MQ触发器,即完成MQ消费端的接入,完全不用管理机器资源。

5.2 落地收益

Serverless的收益是非常明显的,尤其在前端领域,大量的业务接入已是最好的说明。具体收益,从以下两个方面分别来看:

  • 降成本:通过Serverless的弹性伸缩能力,高频业务资源利用率能提升到40%~50%;低频业务函数通过合并部署,也能极大降低函数运行成本。

  • 提效率:整体研发研发效率提升约40%。

    • 从代码开发来看,提供完备的CLI、WebIDE等研发工具,能够帮助开发同学生成代码脚手架,聚焦编写业务逻辑,快速完成本地测试;另外,让业务服务零成本具备在线查看日志与监控的能力。

    • 从发布来看,通过云原生的模式,业务无需申请机器,发布、回滚都是秒级别的体验。另外,还能利用平台天然能力,配合事件网关,实现切流、完成金丝雀测试等。

    • 从日常运维来看,业务无需关注机器故障、资源不足、机房容灾等传统模式该考虑的问题,另外,当业务进程异常时,Nest能够自动完成异常实例的隔离,迅速拉起新实例实现替换,降低业务影响。

6 未来规划

  • 场景化解决方案:接入Serverless的场景众多,如SSR、后台管理端、BFF等,不同的场景有不同的项目模板、场景配置,如伸缩配置、触发器配置等,另外,不同的语言,配置也有所不同。这无形中增加了业务的使用成本,给新业务的接入带来了阻碍。因此,我们考虑场景化的思路来建设平台,将平台的能力与场景强关联起来,平台深度沉淀各场景的基本配置和资源,这样不同的场景,业务只需要简单的配置就可以将Serverless玩转起来。

  • 传统微服务Serverless化:即是路线选型中提到的面向应用的Serverless服务。在美团使用最广的开发语言是Java,公司内部存在大量的传统的微服务项目,这些项目如果都迁移到函数模式,显然是不现实的。试想如果这些传统的微服务项目不用改造,也能直接享受Serverless的技术红利,其业务价值不言而喻。因此,传统微服务的Serverless化是我们未来拓展业务的一个重要方向。在实施路径上,我们会考虑将服务治理体系(如ServiceMesh)与Serverless做技术融合,服务治理组件为Serverless提供伸缩指标支持并在伸缩过程中实现精准的流量调配。

  • 冷启动优化:当前虽然函数的冷启动优化已经取得了较好的成绩,尤其是平台侧的系统启动耗时,提升空间已经非常有限,但业务代码自身的启动耗时还是非常突出,尤其是传统Java微服务,基本是分钟级别的启动耗时。因此,后续我们的冷启动优化会重点关注业务自身的启动耗时,争取极大降低业务自身的启动时间。在具体优化方法上,我们会考虑采用AppCDS、GraalVM等技术,降低业务自身启动耗时。

  • 其他规划

    • 丰富完善研发工具,提升研发效率,如IDE插件等。

    • 打通上下游技术生态,深度融入公司现有技术体系,减少因上下游平台带来使用障碍。

    • 容器轻量化,轻量化的容器能够带来更优的启动耗时以及更佳的资源利用率,因此,容器轻量化一直是Serverless的不懈追求。在具体落地上,准备联合容器设施团队一起推进容器中的一些Agent采用DaemonSet方式部署,下沉到宿主机,提升容器的有效载荷。

作者简介

  • 殷琦、华珅、飞飞、志洋、奕锟等,来自基础架构部应用中间件团队。

  • 佳文、凯鑫,亚辉等,来自金融技术平台大前端团队。

阅读更多

---

前端 |  算法 | 后端 | 数据

安全 | Android | iOS  | 运维 | 测试

  数据已成为很多公司的核心资产,而在数据开发的过程中会引入各种质量、效率、安全等方面的问题,而数据治理就是要不断消除引入的这些问题,保障数据准确、全面和完整,为业务创造价值,同时严格管理数据的权限,避免数据泄露带 ...