美团早期业务快速发展,各业务在 Node 应用上各取所长,但在可用性和运维上需要付出额外的维护成本。随着美团建设了 Serverless 平台,前端也紧随其后,将 Node 应用由传统架构向 Serverless 架构演进,通过 Serverless 方式升级 Node 基础设施。
本次分享将介绍美团建设 Serverless 前端研发体系的建设思路,并与大家分享如何为各业务提供统一的 Serverless 平台,支持业务向 Serverless 迁移,最后分享了美团的部分具体业务案例,希望给大家在 Serverless 技术转型和实践带来一些启发。
本文整理自美团前端技术专家张方成在 ArchSummit 全球架构师峰会(深圳站)上的演讲分享,主题为“美团基于 Serverless 的前端研发体系建设和业务实践”。分享主要分为四部分:1. 背景;2. 美团 Serverless 前端建设;3. 前端交付实践;4. 未来规划。
背景
为什么做 Serverless?
首先我们为什么要做 Serverless?美团是一个一站式的生活服务平台,为消费者提供外卖旅游等服务,为商户提供营销、配送等服务。作为美团技术团队,我们需要为消费者、商户还有内部的开发者提供很多的技术平台。随着美团业务复杂度不断增加,技术团队面临的挑战也越来越大。
技术架构演进
我们的技术架构演进可以分为三个阶段,整体趋势是复杂度下沉并服务化。
第一个阶段,我们称为基础设施的服务化,这一阶段由基础技术平台负责基础设施的维护工作,业务同学需要负责业务应用,以及一些平台软件的开发和维护工作。
第二个阶段,我们称为平台服务化,这时候我们将业务应用原来维护的平台软件部分下沉到技术基础平台,由技术基础平台统一负责平台和基础设施的维护工作。
第三个阶段,我们称为业务逻辑托管服务化,这个时候我们将业务应用当中大量的通用逻辑下沉到技术基础平台,由技术基础平台统一负责基础设施、平台软件、业务应用底座相关的一些维护工作,业务开发者只需要维护相应的业务逻辑即可。
目前我们正处在由平台服务化向业务逻辑托管架构演进的阶段,在这个时候,Serverless 是一个很重要的技术点。
Serverless 可以为前端解决什么问题?
2019 年我们上线了 Serverless 平台,Serverless 使业务开发者聚焦业务逻辑开发工作,主要帮助前端解决 Web 应用场景下的问题。美团的 Node 主要应用在 B 端、C 端以及工具型产品:B 端产品的特点是系统本身很复杂,更关注研发的效率;C 端产品的特点是访问量比较大,更关注服务的稳定性;工具型产品的特点是访问量比较低,但仍然需要占用大量的机器,更关注机器的费用问题。总结下来业务面临的痛点主要有三个:
1、研发效率低,体现在研发过程当中,开发者仍然需要负责很多业务无关的工作;
2、运维成本比较高,体现在开发者需要在稳定性保障上做很多事情;
3、机器费用比较高,比如说机器需要冗余部署,资源利用率整体也比较低。
Serverless 是如何帮助我们解决这些问题的?假如我们现在要开发一个 Node 应用,通常会涉及到基础设施、平台软件等工作,当然在业务应用层还需要考虑框架还有服务的可用性,包括性能、高并发这些问题,整体下来我们的运维成本从上至下越来越高。
Serverless 对前端开发者屏蔽了基础设施、平台软件,以及业务应用底座当中一些复杂的事情,使前端开发者可以聚焦在业务逻辑开发当中。
这是我们 Serverless 前端的全景,主要包括研发套件、PaaS 平台、技术组件,以及业务层的解决方案。我们通过研发套件的建设和技术组件的建设来提升业务的开发效率,通过 PaaS 平台的建设来为业务提供服务的架构和稳定保障能力,同时 PaaS 的弹性特点,可以很好解决原来系统与部署的问题。
美团 Serverless 前端建设
研发套件建设
研发套件主要是指研发流程当中需要的一些工具,比如说框架、开发工具和部署工具等等。在前端研发与 Serverless 结合的背景下,我们面临的主要问题是,原有的研发流程当中一些工具不好用甚至是用不了,这些工具面临重构。公司内开发就有很多研发框架,在 Serverless 下,我们需要的是一个开箱即用的 Serverless 研发框架。在调试阶段,我们希望可以去调试 Serverless 下的云函数能力;在构建部署阶段,Serverless 当中我们部署的是函数,原有的发布工具支持也不是很好,我们希望可以去部署对函数友好的项目;在线上运维阶段,我们也希望去提供 Serverless 架构下的一些运维工具,帮助业务降低在 Serverless 架构下的运维成本。
我们的整体思路展开来说,第一是在开发阶段,通过开放能力的建设,去集成业务研发框架,使已有的框架可以低成本地在 Serverless 架构下运行起来;第二是提供整个研发流程中一些必要的工具,比如说在调试阶段,提供本地 / 远程的调试能力,在构建部署阶段,提供部署组件、灰度组件,在线上运维阶段,提供服务的监控、链路追踪以及性能诊断等运维工具。
下面来看一下我们通过开放能力集成业务的框架,和在云端开发体验上的一些探索。
首先看一下我们如何通过开放的能力集成已有的框架。我们的策略是与公司内框架的提供者团队合作共建,使已有的业务框架遵循相关的规范,沉淀到我们 Serverless 研发套件中来,这样就可以面向公司范围去提供框架服务,我们制订了架构规范和部署规范,以及运维相关的规范。
同时我们基于 Serverless 架构,正在探索云端的开发体验,探索完全在线的开发结构模式,开发者只需要打开浏览器就可以实现项目的开发部署、运维等相关所有的事情,云端开发可以帮助我们实现整个开发周期的闭环,包括项目的创建、开发、调试、部署,还有运维当中的一系列工作。
与传统的本地开发模式相比,云端开发帮助我们消除了本地线上频繁的切换动作,提高了开发效率,同时云端开发可以帮助我们实现高效的同步合作,云端开发提供一致的横向配置,以及可共享的开发环境,可以使代码协作就像在线文档协作一样简单,提高整体的协作效率。有了云端开发并不意味着我们放弃了传统的开发模式,两者是可以互补的,目前我们也在探索实践当中。
基础组件建设
下面我们来看一下基础组件,主要是指 PaaS 产品和运维产品。我们面临的主要问题是学习和使用的成本比较高,主要体现在 SDK 同步、配置比较零散。我们有几十个技术组件,比如数据库、对象存储、文件存储等等,我们原有的使用方式是需要在代码当中引入技术组件的 SDK,通过一系列的配置还有调试工作,使代码和组件正常跑起来,整个流程下来的成本还是比较高的。我们的策略是为业务提供两种简化的使用方式:第一种是基于事件模式的使用方式,开发者只需要在 FaaS 管理端去配置化启用相关的组件,不需要引入 SDK,开发者只需要提供一些 API 方法,我们去处理相关的逻辑即可,技术组件的 SDK 调用还有事件的触发都由事件网关这一层来负责。第二种是基于运行时的使用方式,我们在函数运行时内部提供技术组件加载器,主要负责加载这些技术组件,开发者只需要在文件当中去配置相应的组件,同时也不需要引入 SDK,就可以在函数上下文当中去调用相应的实例以及相应的 API 方法。
FaaS 平台建设
我们下面来看一下 FaaS 平台,即云函数平台。我们大体上可以把它分为运行态和管理态,运行态要负责事件流转的过程。首先由触发源来产生事件,经过事件网关分发到具体的业务实例当中的函数里去处理,业务函数会对我们的事件做处理和响应。事件网关除了分发流量之外,还会做一些限流降级、流量统计等相关的工作。在实例这一层提供了函数沙箱,里面运行的是业务函数,对业务函数起隔离的作用,在管理系统里提供函数的管理、发布以及监控等运维能力。
下面我们来看三个问题,首先是我们如何应对高并发问题,第二是我们如何降低机器的费用,第三个我们在稳定性保证建设方面做了哪些事情。
首先看一下我们是如何应对高并发的。Serverless 架构下,弹性的特点可以帮助我们在需要的时候得到更多的实例来承担流量,事件网关会将我们的请求分散到不同的实例上,达到负载均衡的作用。我们支持高并发的一个关键是需要支持快速扩容,需要在短时间之内扩容到所有的实例,这样服务才是足够稳定的。支持快速扩容的关键就是减小整体的冷启动时间,冷启动时间长意味着我们需要更多的时间来得到足够的实例,我们的服务就会有风险。
我们看一下冷启动优化治理,整体流程包括容器启动、镜像下载、业务代码下载、启动容器、业务函数运行,以及代码加载这么多过程。其中容器启动里包含了美团的一些定制化的流程。我们做了一些相应的优化,第一个方式是消除实例初始化的时间,主要的策略是提供资源池的方案,提前启动好一批常用配置的机器,当业务需要的时候从资源池当中调度和分配给相应的业务即可。第二个优化方式是减小业务代码体积来降低业务代码的下载和加载时间。
下面我们来看一下如何降低机器费用。Serverless 架构下弹性的特点可以帮助我们把传统模式下机器浪费的问题给解决掉,这里我们要说的是如何进一步降低机器的费用,主要有四种方式:
第一种是提供单实例、多并发的能力
第二种是减小沙箱的配置
第三种是提供实例为 0 的能力
第四种方式是提供合并部署的方式
下面我们来看一下稳定性保障,我们整体的策略是稳定性和建设下沉,使业务专注于解决自身的调整。作为 PaaS 平台的提供方,我们希望为业务承担更多业务无关的工作。这是我们稳定性保障的全景,整体分为 7 层,下面的三层主要是与故障的识别、风险规避和故障处理相关。上面的四层主要是与软件的开发和产品的发布流程相关,我们在各个方面都做了一些建设,这里我们主要看一下在高可用架构、可观测性,以及灰度发布部分的工作。
首先在高可用架构上,我们提供了部署架构、弹性伸缩、容灾降级等方面的保障。在部署架构这里,我们为不同的业务线提供了一些独立的集群来运行相关业务,这样可以做到业务线的隔离。在业务的独立集群内我们做了多机房的部署,做到了按地域隔离。在弹性伸缩这里我们支持定时伸缩、预留实例,以及提前扩容,提前扩容是指当流量达到扩容条件的时候,提前准备实例来承接流量。在平台降级这一块,我们在事件网关这一层提供了业务层的降级能力,配合着我们对业务提供的限流和熔断这些能力,整体保障业务服务的可用性。
下面我们来看一下可观测性,可观测性主要是指发现问题和定位问题的能力。发现问题方面,我们希望提供 Serverless 架构下的服务交换能力,帮助业务更好地观测云函数的运行状态,在定位问题方面,我们提供了服务日志与性能诊断等平台,来帮助业务去排查业务问题,还有代码的性能问题,在服务监控方面,基于公司统一的无监控平台,定制了 Serverless 架构下的系统监控、应用监控还有健康监控。系统监控提供的是实例纬度的 CPU、磁盘这些监控数据,应用监控提供的是应用粒度的访问量、QPS 等监控的数据,进程的监控提供的是代码实际运行进程内的 CPU 和内存相关的数据。进程监控可以帮助开发者更清晰地看到函数实际运行进程内的情况。
在 Serverless 架构下,函数平台内部的事件网关还有函数运行时成为请求链路当中的关键节点,我们也提供了一些平台内部的日志,帮助业务去排查问题。同时结合业务的用户端日志还有业务函数相关的日志,我们将所有的日志全部下沉到日志系统,为用户提供统一查询的能力,以及链路拓扑全链路日志等日志分析的能力,帮助业务在遇到问题时更快速排查问题。
PaaS 平台同时为我们提供了灰度发布的能力,灰度发布的策略主要有流量百分比,还有一些自定义的条件。通过流量百分比还有自定义条件,业务可以实现比如平滑发布、AB 测试等灵活的灰度方式。平滑发布可以帮助我们在日常发布的过程中保障服务整体可用性。
前端交付实践
下面来看两个业务案例,看一看我们为业务实际解决了哪些具体的问题。
**案例⼀:流量波动较大的服务 **
首先第一个案例是一个流量波动比较大的服务,这个服务提供的是 SSR 页面和 API 服务,业务的特点是平时流量比较小,在高峰的时候流量突然增加,QPS 最高和最低差距能达到 30 倍以上,这是一个一体化的漏斗服务,部署在我们传统的容器当中,需要我们开发者去维护机器。业务面临的痛点主要有三个:
服务器的成本比较高
运维成本比较高
应急响应不及时
我们为业务提供了一个低成本的迁移方式,主要思路是在平台侧提供一个 Web 适配器的能力,来解决传统架构和 Serverless 架构的差异点,使传统架构更平滑过渡到 Serverless 架构当中来,后面逐步引导新的需求甚至原有的服务慢慢迁移到标准的 Serverless 架构下。业务经过了一些适配和改造,最终接入到了我们 Serverless 架构,并带来了一些改变。首先实例是可以自动伸缩的,通过自动扩容、定时扩容,可以应对流量高峰场景;其次是有全面的监控能力,包括系统、流量、性能监控等监控数据会有一站式的解决方案。体现在收益上有以下几个方面:
服务器成本降低:资源利用率由 10% 提升到 35% 以上;
运维成本降低:一站式监控,迁移后零运维;
应急响应:秒级发布、实例异常自动隔离,系统层面的紧急情况由平台承担。
案例⼆:低频访问 SSR⻚⾯场景
我们看一下案例 2,这是一个访问 SSR 页面的场景,业务已经由传统的架构迁移到我们的 Serverless 架构,但仍然面临着一些问题。它的业务特点是:
页面粒度部署
日常流量较小
流量有波峰波谷
业务的痛点主要有两个,首先是因为隔离性比较差,所有的页面全部部署在一个沙箱之内,之间的稳定性相互影响。第二个痛点是发布回滚耦合在一起,整体的发布时间比较长。业务针对这些痛点做了一些演进,每个页面部署在一个沙箱之内,同时每个页面需要一个实例,原来是仅需要一个实例,现在是需要更多的实例来承担同样的服务。体现在收益上,因为页面间做到了沙箱的隔离,在发布效率上有所提升。这时候业务面临一个新的痛点,就是整体的资源利用率比较低,大约是在 10%。最终针对于业务这种情况,我们为业务提供了合并部署的方式,将业务演进为多页面每个页面部署在一个沙箱当中,这些沙箱部署在一个实例之内,将业务原有的发布 / 回滚时长的 10 分钟级别,降低到了发布只需要五秒,回滚只需要 0 秒,页面间原来是耦合的,现在依然可以做到沙箱的隔离。
在这个业务场景下,我们沉淀了满足业务对隔离性相关需求的能力:
函数运行时支持插件能力
业务定制 SSR 运行时
支持多框架扩展
冷启动时间降低
未来规划
未来我们首先要提升 Serverless 架构下整体的资源利用率,希望将资源利用率推到 65% 以上,这是一个很有挑战的目标,涉及到我们在稳定性保障、稳定性能优化,以及各个方面的优化和提升。其次是继续提升我们的研发效率,在工具建设还有组件集成上做进一步的努力,来帮助业务去做更多的事情,降低业务的开发成本,进而提升业务的研发效率。最后是迎接 Serverless 架构下的运维挑战,在 Serverless 架构下,当函数体量比较小的时候,对业务来说几乎是 0 运维的,当我们又把传统的大应用全部拆分成函数,部署到我们 Serverless 架构下,我们需要运维的函数或者服务会越来越多,未来一定会有一些挑战,目前我们还在探索 Serverless 架构下函数服务体量的过程中。