vlambda博客
学习文章列表

【第1824期】Serverless 函数应用架构升级

前言

Serverless从年头火到年尾。今日早读文章由阿里@张挺授权分享。

@陈仲寅,花名张挺,淘系前端技术专家,长期耕耘于 Node.js 技术栈,为淘宝和阿里其他 BU 提供框架和中间件解决方案,阿里集团 Serverless 标准化规范负责人,负责淘宝整体的 Node.js 体系基础建设,解决全栈开发的各种维护和稳定性问题,也同时负责 MidwayJs 系列内部和社区开源产品,包括 Midway、Sandbox、Pandora.js、Injection 等开源产品的开发、维护等工作。

正文从这开始~~

本次 D2 分享的话题叫 《Serverless 应用架构升级》,主要讲述的是阿里集团内部从传统一步步抽离出 midway-faas 框架,并赋予其核心能力,解决用户诉求的一些思考和实践。

6月的 GMTC 和 2019 JSConf 我们都有去分享,社区的 Serverless 在不断升温,今年成了阿里经济体前端委员会四大方向之一,给了前端非常大的机遇和挑战,业务在不断尝试之余,也逐步沉淀出了一套可迭代,可维护,可扩展,可复用的开发。如今云厂商不断发力,无声的战争正在打响。

现在,随着 Serverless 的深入人心,云厂商都在说,“我们在定义 Serverless”,而开发者都说”我们在做 Serverless“,用户都是“在用 Serverless”,人人都在往 Serverless 体系上靠,或多或少的沾点边,使得整个社区欣欣向荣。

而对于我们前端本身来说,引进 Serverless,也能在各个场景下带来帮助。目前 Serverless 体系,首选的是 FaaS + BaaS 模型,使用了 FaaS。

首先是更快的开发速度,基于时间驱动模型,能够更方便的进行开发和迭代,让用户快速上线;其次是更加安全的隔离环境,用户再也不能,也不希望登录服务去排查问题;第三是按量付费,由于计费模型的变化,比传统的按实例付费,在访问率低的时候,更能降低成本;最后是弹性实例,在服务时,不再考虑峰值等情况,不需要提前预估流量,预先准备实例。

【第1824期】Serverless 函数应用架构升级

这些优势,看到了一个新的契机,让前端能够减少开发成本,弱化运维,同时又能更多的关注数据,真正的向着后端、一体化发展,也为跨技术栈研发效能提升提供了可能。

【第1824期】Serverless 函数应用架构升级

社区背景

现有的社区,有非常多的云厂商提供函数服务。不管是 Amazon,微软,还是谷歌,以及国内的阿里云和腾讯云,都推出了自己的云函数,同时,每个云厂商都有着一套自己的标准和规范,同时,在这些标准之上,云厂商为了吸引用户,更快的占领市场,做了很多创新,阿里云的 CustomRuntime,以及前段时间腾讯云推出的 Service 2.0 概念,就是比较好的例子。

【第1824期】Serverless 函数应用架构升级

作为普通用户,如果想快速的使用 Serverless,在这么多平台面前,光是选择就得斟酌一番,在各个平台之间反复对比,同时,如果想多尝试几个平台,就会发现每个平台的规范,代码行为,启动方式,触发器都不尽相同,对于用户来说,能有跨平台的方案,可以在不同的厂商之间做一些迁移,互调等有意思的尝试。

【第1824期】Serverless 函数应用架构升级

在社区中已经有一些能够帮助我们更好的部署到多云的工具链,Serverless Framework 就是其中做的不错的代表,已经有不少云厂商通过开发插件接入了 serverless 工具链。它通过 serverless.yml 文件,定义了多云的能力,使用云厂商开发的插件,一定程度上解决了跨平台的问题。

【第1824期】Serverless 函数应用架构升级

这其中没有很好的解决云厂商之前代码层面的不一致(因为是云厂商自己做的),第二,这些插件是云厂商自己提供,如果某些厂商不做支持,那么能力就会缺乏。

【第1824期】Serverless 函数应用架构升级

而另一家 ZEIT,推出了 now 系列工具,以轻量简单快速闻名,他们的体验非常棒,目前支持部署到他们接入的平台,示例基本以前端项目为主,和我们 FaaS 本身的实践有所区别,定位也有一些差异。

【第1824期】Serverless 函数应用架构升级

在调研完社区的生态之后,我们发现,没有非常契合我们的诉求的产品,我们觉得自己设计一套符合我们定位,且能够满足场景,又面向未来的框架。经过讨论,我们将框架的特性归纳、总结出以下四点。

第一,需要能够防厂商锁定,由于 FaaS 目前没有成熟的标准,导致社区平台割裂,同时因为集团内部也有多套环境的需求,所以我们决定把这个特定放到第一位。

第二,是灵活性,我们称之为 Flexibility,传统的 FaaS 部署模型和计费模型,在某些场景下,成本和性能都有所欠缺,同时,在扩展性、复用性上也不够灵活。

第三,在团队规模越来越大的今天,标准,复用和扩展性已经变的越来越重要,如何解决这些问题,我们也进行了深入思考

最后,是一些实际的诉求,比如在多个函数之前,复用一些逻辑,统一埋点,监控等,我们需要在这一方面做一些扩展,目前的社区平台无法满足,我们需要设计出一套生命周期,来完善整个体系,这样内外的逻辑保持一致,也可以把我们的实践输出出去。

【第1824期】Serverless 函数应用架构升级

防厂商锁定

前面我们提到,不同的云厂商提供的云函数服务,标准不同,写法不同,特别是出入参,会有一些差异,给我们的同学带来了不少困扰。参数这一块这是由不同的运行时决定,像阿里云,就会有 req, res, context 以及 event, context, callback 两种写法,腾讯云和 aws 也有一些异步处理以及参数个数上的差异。

【第1824期】Serverless 函数应用架构升级

社区的 Serverless 由于起步比较早,各家云厂商都对它有一定程度的支持,通过 serverless.yml 来定义每个平台的配置、资源、能力,前面也提到,由于是社区化,各家云厂商对插件的支持力度不一,更多的云厂商还是会着重打造自己的 cli 工具链,以及结合自家平台或者 IDE。

我们基于 Serverless Framework 的想法,希望能进一步做标准化和扩展,这样既可以复用社区生态,也可以进一步扩展我们自己的能力。由此,我们的方向分为两部分:

1、进一步标准化 serverless.yml

2、针对不同的云厂商,标准化代码层面的出入参

【第1824期】Serverless 函数应用架构升级

severless.yml 的标准化其实比较困难,这一块会参考现有的 serverless 体系,在他们没有做好的地方继续做规范和定义,我们的优势在于,会结合实际场景去考虑,也没有云厂商的包袱,相对来说可以自由一些。

我们设想不同的云平台,都会有类似的能力,比如 http,api gateway,以及 timer,对象存储,消息队列等等,把这些常用的都定义为相同的字段,通过构建来生成出各个平台自己的字段,可以帮用户省下了解的时间。通过这样的变化,我们直接将一份 yml 文件,转变为不同平台自己的 yml 文件,这样缓冲层的设计,也屏蔽了后续因为平台的变化导致用户使用层面的差异性。

【第1824期】Serverless 函数应用架构升级

标准化出入参,只是代码上的包裹,相对来说就容易许多。

1、把 callback 变为 Promise(async)

2、把原有的入参做一些抽象,变为一个叫 FaaSContext 的请求上下文(包含了原有 event,context 的部分内容,更像 koa 模型)

3、把原有的 context 变为 FaaSContext 上的属性(保留,后续可能有用)

这样做完之后,针对不同的平台,统一将上下文作为第一个参数传入(必选),event 第二个参数(可选),同时整个方法直接是 async 函数。

【第1824期】Serverless 函数应用架构升级

实现层面相对简单,我们把这些内容做成了参数包裹器(parameter wrapper),和之前的 yml 文件一起,结合用户代码的构建产物,分发部署到各个云平台,目前社区我们还只完成了阿里云和腾讯云,这种模型相当轻量,和原有的平台能力也不冲突,后续我们将会继续支持其他平台。

【第1824期】Serverless 函数应用架构升级

同时,在这些过程之中,我们采用了 interface 的定义方式,产出了几份针对不同平台的 yml 标准化定义,后续可以用它们快速的格式化,校验,减少用户开发成本。

【第1824期】Serverless 函数应用架构升级

下面是我们发布到多平台的演示,展示的是同一份代码文件,在不修改代码本身的情况下,发布到多云。

【第1824期】Serverless 函数应用架构升级

灵活性

我们也把灵活性是灵活性(Flexibility)作为第二个设想的能力,理想情况下,是部署模型的延伸,可以让函数在水平和垂直两个层面自由扩展。

【第1824期】Serverless 函数应用架构升级

我们以一个传统 Web 栈作为示例。

传统 Web 应用,路由会写在一个固定的文件中,每个路由一般会关联到一个 Controller 上,在函数的体系下,每个路由都将是一个 http 触发器,都将拆为一个独立的的函数,有着独立的代码、配置等。由于函数的热度不同,调用的频次也会不同。同时,如果原来的路由和 Controller,每个函数可以绑定多个路由。

所以说,FaaS 栈和传统 Web 栈有着很高的相似度,但是不同的是,这些函数都将部署到不同的容器中(隔离性),再加上平台本身宣传的那样,按调用量付费,看起来十分完美。

【第1824期】Serverless 函数应用架构升级

殊不知,函数整体的计费模型,除了传统的调用次数,还有一个 CU 的概念,一般来说,CU 会和容器本身的 CPU、内存消耗相关,调用次数容易计算,CU 要优化就比较困难。如果按请求调用量付费,每次调用都启动一个新的实例的话,创建实例本身的开销加上函数调用其实是比较浪费的。

【第1824期】Serverless 函数应用架构升级

为此,我们希望在某些场景下,能够尽可能的节省成本。这就产生了一些可能性,如果我们能复用函数实例,并发处理业务,或者共享一些资源,在一定程度上就能降低实例重复创建本身的开销。

我们设想,如果把多个请求在一个实例内处理,创建新实例的次数会变少,如果多个函数的逻辑在一个函数里完成,这样函数冷启动概率是不是就会降低,从而在另一个层面降低成本呢?

如果多个函数在一起,是否可以选择性聚合,假如某个函数请求量变高(热点函数),是否能够随时拆分出来呢?

【第1824期】Serverless 函数应用架构升级

这个答案是肯定的,经过实践,我们已经可以做到函数本身的随意组合部署。这就是我们设想的 Flexibility 的一部分,让函数在部署模型上变的更加自由,更加灵活。每个函数可以按传统的部署模型,部署到多个容器中,也可以按照自己的想法(流量情况)聚合到一起部署,随意的组合。

【第1824期】Serverless 函数应用架构升级

但是有些同学觉得这样的组合很不错,就会担心成本的问题,毕竟函数的部署模型调整的话,代码也会跟着调整,如果改动复杂的话,这个成本不会接受。

经过我们的设计,我们已经做到了只在 yml (配置)层面做调整,代码层面不需要改动,同时聚合的配置部分不影响原有的函数配置(随意增减),这就是我们 Flexibility 水平聚合能力,我们也叫它 ”高密度部署“。

【第1824期】Serverless 函数应用架构升级

开发效率

第三点,我们考虑的是开发效率,在团队越来越大之后,开发的标注化,扩展性和可维护性一直是关注的重点,而随着 TypeScript 的推出,越来越多人将应用本身迁移到了这个体系中,我们也在不断思考,但是目前为止,没有在 FaaS 上看到这些方案。

【第1824期】Serverless 函数应用架构升级

前面提到过,我们将传统的 Event 和 Context 字段做了转换,同时,因为对代码做了变化,也需要给用户足够多的可用性提示,为此,我们定义一些 interface(比如 FaaSContext)辅助开发。

将 TypeScript 优秀的特性引入 FaaS 体系一直是我们的目标,从 17 年的 pandora 开始,包括去年我们开源的 midway,都全部使用 TypeScript 进行研发,内部的所有库已经全部迁移到了 TypeScript 体系上,让 FaaS 用上 TypeScript 也并不困难。

【第1824期】Serverless 函数应用架构升级

为此,我们将 midway 逐步改成了多场景方案,迁移出了名为 midway-faas 的函数框架,在标准的 Class 模型基础上,提供了基于 IoC,装饰器等新的特性,既和原来的体系一脉相承,能力共享,也在 FaaS 场景提供了更多的 Feature。

【第1824期】Serverless 函数应用架构升级

运行时扩展

在上面这些特性定义完之后,我们觉得函数的开发本身能力差不多了,但是还有些问题没有解决。

【第1824期】Serverless 函数应用架构升级

在使用一段时间之后,我们发现了一些用户诉求。

现有的函数,用户面对的直接是入口参数,我们如果要在跨函数前做一些参数校验,转换的的事情,目前需要做基类继承,或者直接写多次方法调用,这种类 middleware,或者 AOP 的做法,是否能够有方案支持。

内部运行时还好,运行时是由平台掌控,有些有初始化方法,有些没有,那么能不能在一定程度上抹平这些差异,让用户使用无感。

还有一些统一监控,埋点的需求,以及集团内部一直提的治理的需求

【第1824期】Serverless 函数应用架构升级

这些能力不一定需要在框架层本身实现,由于我们的构建特性以及隐藏了真实的入口,我们完全可以在整个调用之前加入这些能力。

为此我们设计了一套针对运行时的生命周期,包括 RuntimeStart、FunctionStart、Invoke、Close 四个阶段,以及他们的 before 和 after 的 hook 能力。同时将内部的运行时都基于这套编写,目前已经实现了多个平台的完整运行时。

不过这一套在社区平台稍有不一致,社区的平台基本无法自定义运行时,也就无法执行完整的生命周期,所以我们针对社区平台(例如阿里云 FC),就进行了简化处理。

整个运行时扩展简单来说分为几个部分,最底层的是 runtime-engine,用于整个生命周期的管理和执行。之上的是 LightRuntime,一个轻量的运行时,用于在不同的平台之中完成我们的运行时生命周期,同时适配各个平台的出入参。

【第1824期】Serverless 函数应用架构升级

而另一块是每个运行时复用的能力,我们从 Lambda 中借鉴了概念,称之为 Layer。Layer 在设计上可以和任意一个运行时合并,赋予其额外的能力,我们一般用在多场景兼容(传统应用迁移),统一监控、metrics 等能力上。

【第1824期】Serverless 函数应用架构升级

经过快一年的迭代更新,我们将淘宝的导购业务迁移到了 Serverless 体系,同时集团其他 BU 也逐步的复制了这套方案,都顺利通过了双促的大考。

【第1824期】Serverless 函数应用架构升级

我们计划将这套 midway-faas 的能力开放给社区,如今,代码已经提交到了 github,还在飞速迭代中,算是 public beta 阶段,我们希望在明年的一月发布 v1.0,提供更多的 Feature 支持。

Serverless 的路依然还处在起步阶段,不断尝试,降低成本,为前端赋能是我们的目标,也欢迎更多的同学加入到我们的队伍中。

@张挺曾分享过




你可能还需要