开源的Serverless核心特点
柏舟 新冠4年 08-15
Serverless是一种云原生开发模型,允许开发人员构建和运行应用程序而无需管理(物理)服务器。目前开源的Serverless方案有Knative, Openfaas, fission, kubeless等。我发现,目前开源的Serverless有以下几个特点:
- 基于Kubernetes,在此基础上提供基于事件的调度方案。默认的调度方案只能设定指定数量的容器,而其它方案需要最少一个容器,Serverless可以完全缩容到0,但是存在冷启动的问题。
- 支持丰富的拓扑结构,比如类似Http的同步响应模型和流处理模型等。
- 最终运行的Pod仍是Server。
- 一般会内置构建系统。
函数的拓扑结构
- Http的同步响应模型,请求一定是双向的,发送和回复都可以有数据。
- 异步的消息队列模型,一个队列对应多个消费者。常用于缓存削锋。
- 流处理模型,根据key进行分区,每个分区对应不同的消费者。常用于数据并行处理。
- 定时队列。
单独强调这个是为了说明Serverless支持丰富的模型,并且(knative)能够一定程度上兼容现有的系统。
Serverless组件和功能简单介绍
以knative为例,Serverless一般存在以下组件:
- Router:请求的入口,当knative service(ksvc)的实例数量为零时,流量转移到Activator上。
- Autoscaler:根据请求的数量、配置和监控来控制ksvc数量。
- Activator:当ksvc缩减到零时,Activator会接受并缓存请求,同时通知Autoscaler起一个ksvc。
- queue:ksvc的边车,控制流量,还可以作为队列。
不管是那种Serverless方案,运行的服务都是以Server/Deployment的方式提供服务,而不是以Job的形式提供服务——处理完请求就销毁了。此外,事件的来源不仅包括外部来的流量,也包括git、kubernetes事件等等。
区别1:接口和事件传递
- Knative是以CloudEvents为接口,程序实现方式比较自由。
- Openfaas和fission只需要使用对应的库编写handler。
这些开源的框架都提供函数和Http Server的方式:
- 可以使用Knative的Function模型,使用对应的knative库只编写handler。Serverless框架抽象掉了传输序列化和响应模型,只需要编写handler的业务逻辑。
module.exports = function(context, callback) {
callback(200, "Hello, world callback!\n");
}
- 也可以使用常用的http server模型,程序员主要编写handler,将CloudEvents作为http的body。knative可以从外部兼容CloudEvents的不同的数据源(如:MQTT、Kafka、Http等)取出数据传递给Pod处理。相当于knative处理了传输层的复杂性,好处是程序员仍然像过去一样编写Web Server,坏处是仍然需要Web Server的样板代码。下面是Asp.NET WebAPI的controller。
[HttpPost]
public async Task<IActionResult> Post([FromBody] CloudEvent receivedEvent) {
try {
if (string.IsNullOrEmpty(SinkUri.Value)) {
return this.ReceiveAndReply(receivedEvent);
} else {
return await this.ReceiveAndSend(receivedEvent);
}
} catch (JsonException) {
return this.BadRequest("Failed to read the JSON data.");
}
}
区别2:构建
构建有两种方式,第一种是部署前构建镜像,编写对应的yaml;第二种是直接上传源代码和配置文件,运行时动态生成执行文件运行。
第一种一般支持镜像和函数两种方式:
- Knative serving,Openfaas编写Http Server,需要打包镜像后,编写对应的yaml配置文件。
- Knative serving,Openfaas需要使用对应的命令行工具生成模板,编写完handler后使用工具上传部署。
Fission
Fission的集群中包含很多各种语言的通用env镜像。当需要开启服务时, 它会拉取volume的代码到配置的位置,运行函数,修改pod标签为一种特化的镜像。由于通用镜像数量变少了,k8s会自动重启一个通用镜像,恢复到指定的状态。
感受
开源的Serverless主要干了两件事,基于事件的调度和制定了构建方案。其实就我个人而言,我开始以为Serverless是类似于lambda的东西,但并不完全是这样。说回Serverless的初衷,微服务和云计算从最初的IaaS到SaaS、BaaS到FaaS,整体的思路就是聚焦到最核心的业务,将底层基础设施、不同数据存储和接入全部托管出去,然后整个开发流程标准化。
这和几年前中台的思路并没有大的区别,只是Serverless更侧重基础设施,提供更具有弹性的部署方式。我觉得Serverless还非常不成熟:从零启动非常复杂,还需要各种镜像配合使用,我个人认为不可靠。而且这个概念也不是很新的概念,像Flink这些大数据引擎,很早就支持动态上传代码(JAR)执行,这不比冷启动强多了?唯一的问题可能是不支持Serverless这么多拓扑模型,只能用于大数据。
Serverless的概念本身没有什么问题,问题就是docker、k8s这一套并没有简化linux的复杂性,并且在此基础上有引入了额外的复杂度,特别是网络和可调试性。这一套东西最初兴起的原因是在linux的namespace和cgroup的隔离性基础上引入了Docker分层镜像构建方式,并且有Dockerhub大大简化了集群的运维和部署。
可是为什么要使用集群呢?现在常见的并行并发模型有:
- 一个CPU多核,使用进程、线程并行并发;
- 一台众核计算机,支持多个CPU,使用内存通信;
- GPU:适合特定算法和数据的并行;
- 多台廉价的计算机使用网络通信。
现实有各种物理的原因限制了模型的扩展性。而编程语言的模型有什么问题呢?现在广泛使用的通用语言都是顺序执行的,而申明式语言如SQL虽然很容易优化但是使用范围受限。我认为一个理想的框架是编写一套代码,编译器可以自动并行优化和集群部署,并且还支持本地调试。但我又仔细想想,我发现这东西跟MapReduce差不多。
唉,有的时候不得不接受现实的不完美,可能开发一个复杂系统就是一件复杂的事情。