忽如一夜春风来,千树万树梨花开
,云原生的浪潮伴随着云计算的迅速发展仿佛一夜之间,迅速侵袭了技术的每个角落。每个人都在谈论云原生,谈论云原生对现有技术的变革。
Kubernetes 已经成为容器编排的事实标准,Servicemesh 正在被各大厂商争先恐后的落地实践,Serverless 从一个一直以来虚无缥缈的概念,到如今,也被摆在台面,有隐隐约约崛起的势头。
只是没想到,在后端闷头搞容器化、上 Kubernetes、尝试 Servicemesh 的时候,Serverless 却先在前端火了起来。从今年的 GMTC 全球前端技术大会上,Serverless 主题的火爆就可见一斑。笔者也看过一些网上流行的讲 Serverless 的文章,观点大同小异,实践几乎没有,误解与谬论满篇飞。本文倒无意挑起争论,只是试图从一个云计算研发的视角出发,聊一聊我们眼中的 Serverless。
诉求:为什么需要 Serverless
前端为什么想要上 Serverless,其实也很好理解,node.js 的普及、前端工程化以及 BFF 的兴起,越来越多的前端需要关心服务的构建、部署、运维,服务的日志、监控报警等等,严重拖累了前端的开发效率,让前端花很多时间在服务器上排查问题,无疑是痛苦而低效的。
对于前端来说,最原始的诉求是,我不愿意管服务器等底层资源,哪台节点宕机了,麻烦不用通知我;流量太大了,服务需要扩容了,我也不想关心;我只需要写好代码,就可以自动部署到服务器上,代码有 bug,能让我看日志和监控排查问题就行。
其实这也不单单是前端的梦想,很多后端或者数据类的研发,也有同样的需求。不过咋看很美好,但是仔细想想,有一些后端的业务很复杂,服务间调用关系以及各种特异化需求其实很难适用于 Serverless,想完全不关心底层的服务器,有点困难。
所以,Serverless 并非银弹,关键是看业务场景和需求,就算只有 50% 的业务适合,能解决这 50% 业务的问题,那也是了不起的成就。
解释:到底什么是 Serverless
Faas 和 Baas 又是什么?
了解 Serverless 的同学,或多或少都听过 Faas,Faas 即 Function as a service,一般都称为函数计算。
作为开发人员,只需要写一个函数,就可以在例如 AWS Lambda 等各种函数计算平台上运行起来,真正实现了对服务器的无感知,同时可以对外快速暴露 API 接口,可以基于函数级别的自动扩缩容,可以监听各种事件进行触发。
而且 Faas 结合云平台的 webIDE,如果 webIDE 设计的足够好,可以给我们带来云平台上更方便的开发体验,结合云上的各种工具和生态,未来会有更大的想象空间。
不过,显而易见,以函数为最小粒度,有一些局限性。
微服务是以功能职责为划分,拆分成一个一个专注于特定功能和需求的服务,为了解决微服务之间的网络调用和流量管理,引入了很多服务治理等相关的功能和组件,可以想象一下,如果把服务模块再拆分为函数的粒度,函数之间的调用关系无疑会爆炸,再思考一下,如何把老的服务改造成 Faas 形态,如何复用函数之间的逻辑,如何管理大量函数代码,这无疑对开发者带来了很多困扰。
所以,Faas 不太适合一般后台长期运行的 web 服务型应用,真正适合的是那些数据计算、批处理等业务,这些业务逻辑比较单一,运行完可以停止,而且更适合 Serverless 中基于事件触发的特性,冷启动的延时也无所谓。
Faas 的一个基本特征是无状态,那实际上的数据或者状态该如何存储呢,所以说到 Faas 一般都会提及 Baas,即 Backend as a service,不过类似的 Xaas 的名词太多了,Baas 这个名词看着就像是有人为了强行补充 Faas 没有干的活儿而起的。因此有些人粗暴的总结 Serverless = Faas + Baas,当然如果你要强行认为 Serverless 就是函数计算,那这个也没有问题。
不过,我们的观点是:Faas 只是 Serverless 的一种特例。在这个世界上,除了 Faas,还有更多的无状态工作负载适合以 Serverless 的形态去运行。
Serverless 的特性
除了服务的粒度不一样之外,无状态工作负载和 Faas 一般都具有以下 Serverless 的特性:
- 1-step deploy
既然是 Serverless,开发者真正关心和面对的是代码层面,所以不管是函数还是一个代码工程,一键构建和部署是我们的终极期望。
Kubernetes 生态下有各种 CI/CD 解决方案,但是缺乏更加一键式的工具可以帮我们将代码(函数)迅速转变成部署的服务。所以,一个足够好用的本地 client 工具、一个完善而高效的 CI/CD 平台很重要。对于 Faas,可以让用户便捷的将函数部署到 Serverless 平台,对于无状态负载,则可以根据用户需求暴露一些构建的自定义配置和流程。
- Automatically
在 Kubernetes 上一般服务实际的运行都或多或少的需要我们创建很多的 Kubernetes 资源,例如 service、ingress 等,而 Serverless 会做更多的自动化操作,以便更方便的提供服务。例如,Serverless 平台会自动提供流量入口和路由,部署完成后可以迅速对外提供服务,同时提供类似蓝绿发布、灰度等流量管理等功能。
- Auto-scale
毫无疑问,Kubernetes 也有 HPA 可以提供自动扩缩容。不过,HPA 敢让服务副本数缩为 0 吗?当然不敢,试想一下,如果服务的副本数为 0,相当于不再运行了,用户的流量如何导入呢,用户连服务的接口都调不通了,HPA 更没有 metric 数据来感知去扩容服务了。HPA 无法缩容为 0,对于某些短运行的计算类服务来说,是无法接受的,因为这样就不能真正的做到无服务,不实际运行时不占资源不计费。
当然 Serverless 可以做到,让服务在没有请求时自动缩为 0,在有流量的时候从 0 启动,或者流量增大时快速的扩容,迅速应对流量的变化。
不过,还有一个 Serverless 业界都很关注的点,就是服务从 0 扩容为多副本时启动的延时时间,一般称为冷启动的问题。如果冷启动时间太长,对于用户的第一次请求肯定有很大影响,业内也有很多大厂在做一些优化。但是如果不是直接面向用户流量的服务,例如我只想跑个数据处理算法,其实也不在乎这几百毫秒的启动延迟,如果是类似前端的 web 服务,恐怕大部分人还是宁愿空跑一个单副本的服务,也不愿意冒这个风险吧。
- Eventing
Serverless 的另外一个特征是基于 eventing 事件进行触发,事件实际上是一个比较抽象的说法,很多东西都可以理解为事件。例如,用户的请求可以认为是一个事件,git 的 webhook 可以认为是事件,kafka 上有了消息可以理解为一个事件,包括 Kubernetes 的各种资源操作等等都是。所以,其实事件触发我们并不陌生,我们的平时开发和设计架构里经常都会有意无意的使用到事件触发的机制,只是太过平常,反而没有人去注意和抽象出这么一个理念。
现在大家都在倡导云原生,很多服务都是往云上迁移和部署,事件触发机制在云上可以有更多的扩展性和想象力。例如,我们的 Serverless 应用可以监听云上的中间件或者基础组件的事件,通过这些事件,触发特定的 Serverless 应用,从而打通云上的 Paas 服务,实现云上服务的一体化。
总结下来,虽然目前 Serverless 很火但我们更应该静下心来思考,为什么会有 Serverless 的诞生,Serverless 最原始的需求和驱动力在哪?是 Kubernetes 不够好用还是 Servicemesh 不够友好?
Kubernetes 被认为是下一代的分布式操作系统,操作系统上必然会运行各种各样千奇百怪的程序,有的需要直面系统内核,有的只是提供用户更好的 UI,不过,有一类程序可以以更便捷的方式去编译、运行,而提供这一切的工具与平台就是 Serverless。
所以,Serverless 其实只是一种云原生应用更为特殊的实现和表现方式,也有很多的应用并不适合以 Serverless 的方式去运行。无服务器固然是愿景,大量的封装和抽象让开发者无需感知很多东西,但这个宇宙运行的规律可能并非直白的线性系统,混沌和复杂性才是常态。如果有人告诉你,Serverless 是所有应用的终极目标,那只能引用一句长者的话,too young,too simple
。
适合 Serverless 的场景
基于 Serverless 的特性,我们也可以推导出比较适合 Serverless 的应用都有哪些:
- 前端、小程序、爬虫等
- 事件触发或定时的批量数据处理
- ⼤数据、实时流处理、机器学习的场景
- 经常应对流量突发的推广活动等⽆状态服务
- 视频转码等处理服务
其实还有很多,不过需要指出的是,这些都能在我们常规的容器云平台上构建部署运行,只不过,有了 Serverless 更高层次的抽象和封装,我们可以更快的开发构建部署,服务可以有更好的运行姿态,从而一步步接近我们想象中的那个只用写代码,不关心服务器的美好愿景。
Serverless 现状与局限
作为 Faas 鼻祖的 AWS Lambda 其实早就已经推出了,但最近的几年内其他云厂商才慢慢跟上推出函数计算服务。相信国内的很多公司也在尝试 Faas 或者 Serverless 架构,不过可以猜测出大家或多或少都心存疑虑或者有不放心之感,不敢真正的放上自己的线上服务。
目前看来,虽然 Serverless 市面上都吹的很火,但实际落地的寥寥可数,星星点点的一些火花,还是难以形成燎原之势。Serverless 这片土地十分辽阔,但是各大云厂商却都是在自己和自己过家家,至于其他人怎么玩的,就不怎么关心了。
所以,目前一个很大的问题就是我们在一家函数计算平台跑了自己的服务之后,基本上就和这个平台绑定了,特别是如果你还用到 Eventing,由于都是基于平台内部特定的事件触发机制,迁移成本还是比较高的。
终极原因,目前还没有一个强势而大一统的框架和平台,可以让大家甘愿臣服。想当年,容器编排领域兴起,Kubernetes 和 Mesos 大战,两边各自有人站队,云厂商也各自押宝,如今 Kubernetes 一统江湖,大家都默默的建立起了基于 Kubernetes 的容器云平台,于是围绕着 Kubernetes 的云原生生态蓬勃发展。
由于没有统一的平台,针对 Serverless 目前还存在的一些局限,大家做的优化和改进也是各自为战,难以落地生根。
虽然如今已然是 2019 年了,但我们还是看到了一些希望和未来的苗头。CNCF 云原生计算基金会的 Landscape 中专门对 Serverless 分出一页(https://landscape.cncf.io/format=Serverless
),上面总结了 Serverless 相关的各种平台和框架,其中目前最为火热和最有前景的便是 Knative 了。Google Cloud 已经基于 Knative 推出了 Cloud Run,阿里云的 Kubernetes 容器服务也最近引入了 Knative 的内测,这让我们无疑看到了未来统一 Serverless 平台的希望。
实现:Knative
Knative 是谷歌开源的 Serverless 架构方案,旨在提供一套简单易用的 Serverless 平台,把 Serverless 标准化。Knative 不局限于 Faas,而是期望能够运行所有的无状态工作负载。像其他绝大部分的 Faas 或者 Serverless 平台一样,Knative 也是基于 Kubernetes,不过,Knative 还基于 Istio 或者 Gloo 网关等实现流量的分发和管理。
还在不久之前,Knative 分为三个组件:
- Build
- Serving
- Eventing
Build 负责将代码转换成我们需要的容器镜像,Serving 则是提供 Serverless 的运行方式,Eventing 则致力于提供标准化的事件触发机制。
不过,现在 Build 模块已经被弃用,被由 Build 的设计思路而发起的 Tekton 项目替换(参考:《Kubernetes 原生 CI/CD 工具:Tekton 探秘与上手实践》)。当然你也可以使用其他合适的 CI/CD 工具替代。
Serving 模块主要做的工作本质上就两个:
- 流量入口的自动创建和管理
以基于 istio 为例,Knative 自动创建 istio 的 ingressgateway,服务的 service 等,将流量导入新部署的服务,而不需要手动的创建各种 service,ingress 暴露服务和流量入口。同时,将不同版本对应不同的 deployment,可以方便的实现蓝绿、灰度等发布部署方式。如下图所示:
- 冷启动和自动扩缩容
Knative 有自身基于流量请求算法的 metric 自动扩缩容(KPA)的方式,也支持 Kubernetes 原生的 HPA 实现自动扩缩容。同时,在服务缩容为 0 之后,Serving 会将服务流量路由到冷启动的组件,缓存请求,然后扩容服务,再将流量导入启动后的服务副本。
Knative Eventing 则联合 CNCF Serverless WG 制定一套事件格式规范并推广(https://github.com/cloudevents/spec/blob/master/spec.md#design-goals
),只需要各个云厂商都按照这个规范,我们的 Serverless 服务就可以进行跨平台的事件触发,也不会被特定的云厂商绑定。
当然,Knative 的具体使用和实现细节很难用简单的几句话解释清楚,如果有兴趣,欢迎关注我们后续对 Knative 深度剖析的系列文章。