FaaS 是一种新兴的技术平台,个人认为 2018 年即将迎来 FaaS 的崛起。本文给大家提供一个 Serverless/FaaS 的现状与未来的报告,也作为自己的年度技术总结。本文首发 gitchat。

什么是 Serverless/FaaS

Serverless(无服务器)这个概念存在已经很久了,最早指不需要服务器端的软件,比如纯客户端软件以及 peer-to-peer (P2P) 软件,在云时代,这个概念才表示不需要关心服务器端的相关技术,比如按量计费的 PaaS 服务(比如 FaunaDB serverless,Aurora Serverless, 对象存储等),BaaS (Backend as a Service),以及 Google App Engine 这样的托管 Application PaaS 也可包含在内。

但传统的 Application PaaS 平台,开发者对服务运行的实例还是有感的,即便是没有调用,也依然需要占用资源,并对资源付费,并不是完全的 Serverless,直到 FaaS 出现。FaaS 全称 Function-as-a-Service,可以理解成给 Function 提供运行环境和调度的服务。Function 可以理解为一个代码功能块,这个功能块具体包含多少功能,无法明确给出定义,但有一个明确的指标:冷启动时间需要在毫秒数量级。因为 FaaS 的本质上是以程序的快速启动来实现正真的按需运行,按需伸缩,以及高可用。Function 配合调度系统,就可以完全做到开发者对服务运行的实例无感,真 Serverless。

也就是说,从外延来看,Serverless 比 FaaS 的外延要广,FaaS 主要解决的是用户自定义的代码逻辑如何做到 Serverless,可以叫做 Serverless Compute,同时它也是事件驱动架构的一种,从一张图可以看出二者区别。

serverless-faas

图片来源:Status of Serverless Computing and Function-as-a-Service(FaaS) in Industry and Research

另外 AWS 的 lambda 文档页面有一个演示,我录制成动画,非常形象的介绍了 FaaS 的工作机制。

lambda

动画中通过点击鼠标模拟事件,事件越多,lambda 的实例也越多,当没有新的事件的时候,lambda 实例自动销毁。

从技术的演进来看 FaaS

没有互联网的时候,应用程序多是单机命令行软件。当互联网出现的时候,有了将应用程序通过网络给其他人提供服务的需求。于是出现了 CGI (Common Gateway Interface),就是把命令行软件执行的结果通过网络输出。当时的应用程序的架构比较简单,启个 web 服务器监听网络端口,然后根据请求调用 CGI 程序,同时把请求数据传递给 CGI 程序,等待程序执行后把输出结果通过网络端口返回给客户端(一般是浏览器)。这种架构下,只有 web 服务器是常驻运行的,后面的 CGI 程序都是按需运行的,运行后就释放资源。于是诞生了一批支持 CGI 协议的脚本语言,比如 perl,php,asp,一方面脚本语言可以粘结 web 服务器和应用程序,另外一方面它本身也可以包含逻辑,于是逐渐出现专门针对 web 的用脚本编写的应用。

这种方式的优势很明显,按需运算,没有调用就几乎没有资源占用,一台机器就能托管大量的中小网站,运维成本也小,只需限制下 CGI 进程的执行时长和内存占用量就行,不用担心有人写了个死循环把整个服务器搞挂。所以最早的 php/asp 等脚本代码托管服务是按照空间计费的,可以理解成早期的『云』计算。

但缺点也很明显,每次调用都独立运行一个进程,如果并发量大,进程管理的资源浪费严重,随着互联网的发展出现了叫做 FastCGI 的技术。这种技术的思路是执行逻辑的进程不再是按需运行,而是常驻方式,web 服务器和 FastCGI 服务之间通过 unix socket 或者 tcp 进行交互。以 php-fpm(FastCGI Process Manager) 为例,php-fpm 启动一个常驻的进程池,每个请求分配给一个进程,进程调用 php 脚本执行后释放资源。当然这种方式带来的另外一个问题是常驻进程很容易产生内存泄露等问题,于是 php-fpm 提供一个选项可以设置执行多少次后重启进程。

如果单纯为了性能考虑,直接把应用程序打包到 web 服务器里,通过 web 服务器调用应该性能更好。但这样更新应用就需要同时更新 web 服务器,开发和维护成本比较高,而 web 服务器和应用程序一般是不同的提供方提供,多个应用程序共享同一台机器也会导致端口冲突。

同时期出现的技术还有 Java EE,提供了另外一种解决思路。Java EE 提供了一种叫做应用服务器或者 web container 的服务器软件,提供 web 服务器的功能,应用按照 Java EE 的规范暴露入口方法,然后打包成独立的发布包,部署到应用服务器中。应用服务器动态将应用加载到内存,相当于以动态方式把 web 服务器和应用程序合并成同一个程序,同时通过多线程而不是多进程来解决并发问题。这种方式兼顾了性能和应用独立发布的需求,同时依托应用服务器可以支持更多特性,所以 Java EE 能广泛占据企业端的市场。

于是 Java EE 厂商坐着火车吃着火锅,一路奔向应用标准化的未来。接连推出各种应用规范,想把应用功能通过规范沉淀到 Java EE 应用服务器中。但这时候互联网服务公司异军突起,打破了企业厂商的盛宴。互联网服务的特点是一个服务给全球人使用,对可移植性和标准化并没那么热衷,但对并发以及性能的要求极高,Java EE 中的特性也没几个能满足这种需求,于是宁愿从头打造工具和组件。

互联网高速发展了 10 多年后,大家开始思考,有没有一种应用的架构方式,即可以满足初期的快速研发需求,也可以承载后期的用户规模,还可以将应用的标准化功能沉淀到平台上独立提供?于是有了微服务,于是有了 FaaS。

从云的发展来看 FaaS

第一阶段的云主要解决硬件资源(网络,计算,存储)的运维和供给问题,也就是 IaaS 云,可以理解成基于硬件资源的共享经济。IaaS 云的交付的主要是资源,接口以及控制台也是面向资源的,尽量以模拟物理机房环境来降低应用的迁移成本。而云发展到当前阶段来看,出现了两种需求:

  1. 正真的按需计算 原来云的按需计算只是虚拟机维度的,按时间计费以及弹性伸缩,并不能正真做到按需计算,计算和内存资源都是预申请规划的,和服务的请求并发数并没有明确的关系,哪怕一段时间一个请求没有,资源还是依然占用。而 FaaS 可以做到按请求计费,不需要为等待付费,可以做到更高效的资源利用率。

  2. 面向应用 本质上用户对云的期望是应用的运行环境,并且最好是只让用户关心业务逻辑,而不需要关心,或者尽量少关心技术逻辑(比如监控,性能,弹性,高可用,日志追踪等)。这也是云原生应用(Cloud Native Application)这个概念提出的背景。

    到底什么样的应用叫做云原生应用?这个当前没有一个准确的标准,但我在一次大会上的演讲中下了一个定义,这里再引用一下:

    云原生应用就是让渡一部分功能给云,以实现弹性,高可用,故障恢复,降低研发运维成本的应用

    也就是说,云原生应用的关键是『让渡』,但具体如何让渡,让渡哪些功能?理论上,只要和业务逻辑无关的功能都应该让渡出去,但如何做到?从持续交付到日志监控追踪,这些非业务功能都一直没能标准化,也很难标准化,如何让应用开发者逐渐接受这些标准?看起来似乎是个无解的问题。

    但 FaaS 给出来一个方案。就是应用只需要把包含自己业务逻辑的 Function 提交给云,其他的事情由云来完成。这样,云相当于直接接管了业务逻辑模块,然后其他的技术功能直接由云来提供,不依赖开发者在自己应用中引入标准化框架来实现。

FaaS 的现状

FaaS 最开始是 2014 年一个叫做 hook.io 的网站提供的,顾名思义,这个网站最早的功能是托管 webhook Function。Webhook 这种场景,按调用次数付费是最易于理解的。之后大厂看到价值迅速跟进,AWS 同年推出 Lambda,2016 年 Google,Microsoft Azure 推出自己的 Cloud Function 服务,2017 年国内云厂商腾讯云,阿里云也跟进。

云厂商 FaaS 比较

在公有 IaaS 云上支持 FaaS,可以从以下角度分析:

  1. 支持的语言以及 Function 标准。
  2. 触发器支持,也就是和 IaaS 上其他系统的整合。
  3. 伸缩和调度策略。这个虽然对用户是个黑盒子,但通过文档和自己的一些实验可以做一定程度的分析。
  4. FaaS 和私有 VPC 资源的交互。FaaS 本身是一个全局的公共池子,如果用户的 Function 要请求自己 VPC 的内网资源,就会遇到问题。
  5. FaaS 之上更高级的应用支持。

本文选取了以下公有云的 FaaS 做比较,精力有限,没能选取全部的公有云:AWS Lambda,Google Cloud Function,Azure Function,阿里云函数计算,腾讯云 Serverless Cloud Function,华为云 FunctionStage。本位为了统一描述,把各家的实现都统称为 Function。 另外如果有描述不准确的地方,还请反馈。

语言支持(以文章撰写时间为准)
  AWS Google Azure 阿里云 腾讯云 华为云
NodeJs
Java    
C#        
Python    
Go √*        

其中 Azure 的 Function 有两个版本,第一版本支持的语言较多,Python/Php/Bash/PowerShell 等都支持,但部分语言的实现机制上有问题,是以每次请求都启动进程的方式实现的,伸缩性上有问题,所以第二版放弃了部分语言。

Go 语言,AWS 官方尚未支持,但社区做了一个支持,后面的开源部分有提到。

语言规范
exports.handler = (event, context, callback) => {
    console.log('Received event:', JSON.stringify(event, null, 2));
    var resp = {
        statusCode: '200',
        body: '{"msg":"hello"}',
        headers: {
            'Content-Type': 'application/json',
        },
        "isBase64Encoded": false,
    }
    callback(null, resp);
};

上面是 AWS 的 Javascript 的支持, 各家大体上长差不多。返回的格式上,如果要挂在 API Gateway (AWS,阿里云)后面,对输出结果的格式有要求。

package example;

import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.Context; 

public class Hello implements RequestHandler<Request, Response> {
    
    public Response handleRequest(Request request, Context context) {
        String greetingString = String.format("Hello %s %s.", request.firstName, request.lastName);
        return new Response(greetingString);
    }
}

上面是 AWS 对 Java 的支持,Java 的支持做的事情多些,可以自定义 request 和 response 对象,由框架进行序列化和反序列化。如果想要更高级的输入输出自定义,还支持 StreamHandler,直接处理 InputStream/OutputStream。看到这里是不是觉得和 serverlet 规范有点像了?

Azure 对 Java 的支持更有特点一些,在 Function 的定义中支持了 Java Annotation,用来标记参数以及认证之类的,也就是说可以直接在代码中定义 FaaS 系统的设置了。

public class Function {
    @FunctionName("hello")
    public String hello(@HttpTrigger(name = "req", methods = {"get", "post"}, authLevel = AuthorizationLevel.ANONYMOUS) String req,
                        ExecutionContext context) {
        return String.format("Hello, %s!", req);
    }
}

Azure 的另外一个有意思的实现是支持代码中 Input/Output 绑定。

public class MyClass {
    public static String echo(String body, 
    @QueueOutput(queueName = "messages", connection = "AzureWebJobsStorage", name = "queue") OutputBinding<String> queue) {
        String result = "Hello, " + body + ".";
        queue.setValue(result);
        return result;
    }
}

比如以上 Function,绑定了 Output queue。收到消息后,先写到 QueueOutput 里,同时返回结果,而真正的写入 Queue 的操作是平台代理的。这样就很容易和其他系统做整合,否则就得在代码里专门引用相应的 queue sdk,创建连接,写入消息,还要处理认证相关的问题,相当于一种资源注入机制。

func Handler(payload []byte, ctx context.RuntimeContext) (interface{}, error) {
    logger := ctx.GetLogger()

    var greeting Greeting
    err := json.Unmarshal(payload, &greeting)
    if err != nil {
        return "", fmt.Errorf("invalid request payload")
    }
    logger.Logf("hello %s.", greeting.Name)
    logger.Logf("RequestId: %s", ctx.GetRequestID())
    return fmt.Sprintf("hello %s", greeting.Name), nil
}

上面是华为云对 Go 语言的支持。Go 语言需要用到 1.8 的 plugin 技术,用户需要将 Function build 成 so 动态库,然后上传。

触发器支持(以文章撰写时间为准)
  AWS Google Azure 阿里云 腾讯云 华为云
HTTP API Gateway API Gateway   API Gateway
定时 CloudWatch 定时      
对象存储 S3 Cloud storage Blob storage OSS COS OBS
消息队列 SNS Pub/Sub topic Queue storage   CMQ SMN/ DMS
日志服务 CloudWatch Logs        
数据库 DynamoDB Firestore Cosmos DB      
流式计算 Kinesis Firebase Realtime DB   Datahub   DIS
IoT Alexa/IoT 按钮          
边缘计算 CloudFront          
其他 Email/Lex/Cognito Crashlytics/Analytics        

FaaS 是一种事件驱动架构,所以事件源很重要。这一方面 AWS 做的最全面,几乎将 FaaS 所有可能支持的事件类型都囊括了,并且探索 FaaS 在边缘计算和 IoT 上的场景。这个表格中并没有把 AWS 支持的事件类型全列出来。

Google 的 Firebase/Firestore 的支持其实是 Firebase 本身提供的,并不是标准的 Google Cloud Function 的 trigger,开发的界面以及语言标准都不一样,Crashlytics/Analytics 也是通过 Firebase 支持的,应该是最早的 Cloud Function 只是 Firebase 中的一个功能子集,Google 当前正在整合中。

微服务场景下,AWS,阿里云,华为云的做法都是通过 API Gateway 配置规则,将请求转发给 Function,Google 和 Azure 是直接提供 http trigger 地址。前者的好处是可以复用 API Gateway 的其他功能,但使用起来略复杂,后者简单易用。

其他维度
  AWS Google Azure 阿里云 腾讯云 华为云
自动伸缩 自动伸缩+预留并发 自动伸缩 手动或自动 自动 自动 自动
IAM绑定          
VPC连通          
高级编排 AWS Step Functions   Azure Logic Apps     FunctionGraph

自动伸缩的实现机制上,各云基本上都是按需运行,然后会常驻一段时间,如果没有新的请求就会销毁实例。并发多的时候会启动多个实例。这个我通过代码中增加一个计数器来探测。本计划做一个性能比较,但由于公网访问的网络问题,暂时放弃。这里有个问题就是并发数的控制机制,AWS 默认的自动伸缩并发数是 1000,如果要更高需要购买预留。而 Azure 分 App Service Plan/Consumption Plan,前者相当于预留实例,Function 是运行在 VPC 内的服务器上的,可以手动干预实例数,后者是公用池。阿里当前则有个不透明的限制,如果调用次数过多就会屏蔽(可能很同一台客户端机器相关)。

IAM 的绑定主要是解决 Function 中调用其他服务的授权问题,如果允许绑定 IAM 身份,从 Function 中访问其他服务就不需要在代码中包含认证密钥之类的身份标志。这个 Google 有 ServiceAccount 机制,但当前尚未支持 Function。Azure 则是通过前面描述的 Input/Output 绑定机制来解决。

VPC 连通的需求是为了解决用户的 Function 中调用自己 VPC 内网资源的问题,因为 Function 开始一般是当前系统的补充,所以需要和现有系统交互。这个 AWS 做的最完备,可以给 FaaS 平台提供一个自己 VPC 的内网网段,这样 Function 实例的 IP 就是 VPC 的内网 IP 了。

更高级的应用上,AWS 支持了 Step Function,提供一种 JSON 的状态机描述语言,将多个 Function 编排在一起,作为一个工作流。Azure 的 Logic Apps 可以理解成一种可视化的逻辑工作流定义,有点像 IFTTT 这样的工具,把许多服务通过逻辑串联在一起,然后关联到 Function 作为自定义 Action。华为云的 FunctionGraph 的工作流描述和 AWS 的 Step Function 类似,不过提供了高级的图形化编排功能。

小结

整体看来,公有云的 FaaS 还都处于起步阶段,主要着力于和 IaaS 已有的系统整合以及新语言的支持。跟已有系统整合上,大致有两种方式。一种是 AWS ,FaaS 只是 Function 的运行平台,触发器在各系统配置,FaaS 是对其他系统自定义需求的补充。另外一种是 Azure 这种,围绕着 Function 定义触发器以及输入输出和其他系统整合,Function 是中心,其他系统是支撑。

开源 FaaS 比较

和公有云上的 FaaS 一起逐渐进入开发者视野的还有众多开源的 FaaS 项目。公有云的 FaaS 只能通过试用,以及文档进行分析,而开源的 FaaS 则可以通过源码进行更深入的分析。主要通过以下角度分析:

  1. Function 的调用和执行方式。一般有两种机制,一种是完全按需,每个请求由独立的进程(容器)完成,一种是保持一种常驻的运行环境。前者我们称为 Exec 模式,后者我们成为 Runtime 模式。
  2. 调度和伸缩策略
  3. 编程和语言规范
  4. 概念和抽象
  5. 更高级的应用

本文主要选取了以下 FaaS 实现进行分析:

  开发语言 创建时间 支持厂商 运行平台 执行机制
openwhisk Scala 2016-2 Apache/IBM Docker Runtime
fnproject Go 2012-12* Oracle Docker Exec
funktion Go 2016-12 Redhat* Kubernetes  
fission Go 2016-8 Platform9 Kubernetes Runtime
kubeless Go 2016-11 Bitnami Kubernetes Runtime
openfaas Go 2016-11 个人(Alex Ellis) Docker/Kubernetes Exec
qinling Python 2017-4 个人(LingxianKong) Openstack/Docker Runtime
funcatron Java/Clojure 2016-8 个人(dpp) Docker/Mesos Runtime

创建时间是通过 git commit log 的最早时间统计的,有的项目是内部开发完成后一次性导过来的,有的保存了最早的历史,所以时间不一定准确。

Openwisk 是 IBM 捐献给 Apache 的项目,当前还在孵化中。IBM 同时在自己的 Bluemix 上提供了 Openwisk 服务。它的架构是基于 Akka 的 actor 模型(不熟悉 actor 模型的可以参看本人的文章《并发之痛 Thread,Goroutine,Actor》)构建,中间以 Kafka 作为消息中间件。OpenWhisk 的抽象概念考虑的场景比较多。它的 Trigger 是可以独立定义的,而不是系统中确定的几种。然后通过 Rule 把 Trigger 和 Action(相当于 Function) 关联起来。有 Package 的概念,相当于可以把多个 Function 打包成同一个 Package,然后进行分发。另外它又抽象了一种 Feed 的概念,相当于一个事件流,可以做一些流式处理上的逻辑。同时它支持 Chain 的机制,把多个 Action 串起来进行 Pipeline 调用。

Fnproject 是 Oracle 收购了 iron.io 后,从 iron-io/function 这个项目直接改造过来的。iron function 创建于 2012 年,2013 项目中断,2015年重新启动,2017 年被 Oracle 收购。它的架构比较简单,Function 的交付结果是容器镜像,每次请求都运行一个 Docker 容器,然后从标准输出获取结果输出,再销毁容器。所以它基本上只需要一个 API Gateway 做路由,然后后面挂一个容器运行环境就行,单机或者容器编排引擎都行。Fnproject 在 FaaS 之上发布了一个叫做 flow 的项目,一种工作流引擎,特点是基于编程语言来调度,而不是编排文件或者自定义描述语言,对开发者友好,并且容易编写复杂的工作流逻辑。

Funktion 最近已经停止更新,提示 Redhat 停止赞助这个项目。看相关公告,Redhat 当前试图整合 openwhisk 以及 fission,还有公有云的 aws lambda 到 openshift,重心应该还是在 openshift,主要是做整合,不再试图创造新的 FaaS 平台。

Fission 是基于 Kubernetes 构建,它的 Function 是 Kubernetes 中的对象,每种语言环境维护一个 Pod 池子,用 Deployment 控制数量,然后自己的调度器把 Function 分配给合适的 Pod 运行。前面有一层 API Gateway,创建 Function 后需要设置路由,通过 HTTP 触发。Fission 在 FaaS 之上也构建了一个工作流引擎,叫做 fission-workflows。通过 yaml 描述语言描述,编排多个 Function 作为工作流。它的工作流本质上也是一种 Function,不过运行在叫做 workflow 的环境中。

Kubeless 也是基于 Kubernetes,但更 Native ,它充分利用了 Kubernetes 的 CRD 特性,将 Function 作为 Kubernetes 中的一种在 Pod 之上的对象,每个 Function 实际上会创建出一个 Deployment,同时暴露一个 Service。自动伸缩机制直接沿用 Kubernetes 的 autoscale 机制,只需要输出自定义的监控数据给自动伸缩组件即可。没有独立的 API 服务,直接复用 Kubernetes 的 API。

Openfaas 的机制和 Fnproject 有点像,只是 Openfaas 并不是直接抓取容器的标准输出,而是写一个 Function Watchdog 作为容器的启动进程,暴露 http 服务,用于和调度系统交互,然后直接调用进程运行 Function 获取输出。

Qinling 的定位是 Openstack 上的 FaaS,特性是和 Openstack 上的对象存储等系统整合。

Funcatron 的特点是和 Swagger 整合,编程规范试图弥合异步和同步事件的差异,以及复用已有的框架,比如 Java SpringBoot。

编程和语言规范

开源项目中我没有专门把编程语言支持作为一项特性进行比较,主要是因为不同的调用和执行方式对语言支持的成本不一样。如果是 Exec 方式的支持,实际上是把任何语言的 Function 都当做命令行程序执行,执行完就释放,配合容器平台,相当于一种分布式的 CGI 程序,所以很容易支持所有的编程语言,并且不需要制定编程规范。而如果是 Runtime 的方式,进程是长期运行的,就需要考虑 Function 的动态加载以及隔离机制。比如 Java 的 ClassLoader 隔离机制,Nodejs 的 VM 沙箱机制等。当然,Runtime 的方式也可以支持一种 Binary 或者 Bash 的 Function 支持,理论上也就支持了所有的编程语言。

从语言规范上来看,开源的各家大体上和前面公有云的例子差不多。

其他的开源项目

有一些 FaaS 相关的开源项目,并不是提供 FaaS 平台运行环境,所以上面没有列出来。

  1. LambCI: 基于 AWS Lambda 的持续集成工具。
  2. Serverless:纯开发工具,和云平台以及开源的 FaaS 平台整合,方便开发者把自己的 Function 部署到不同的云平台。目的是通过代码框架来弥合不同的 FaaS 提供方的差异。
  3. aws-lambda-go-shim: 让 AWS Lambda 支持 go function。思路是通过 c 给 python 写个扩展,然后通过 cgo 和 go 整合起来。
  4. Apex: 打包部署和管理 AWS Lambda。
  5. StackStorm 准确的说它不是 FaaS,而是事件驱动的自动化运维工具。但本质上二者的目标是一致的。
  6. Leveros 目标是通过 Docker 打造一个面向 serverless 微服务的云平台,并且试图将服务间的 RPC 调用透明化。
  7. Stdlib 试图打造基于 FaaS 微服务的标准库,并提供一个 Function 的注册仓库。
小结

整体上,开源的 FaaS 平台成熟度还不足,和公有云上提供的还有差距,周边整合的系统也不足。但从开发和调试的友好性上来说,已近比公有云有优势,大多支持从源码的直接交付,命令行工具也比较友好。

FaaS 的技术问题

  1. 调用和执行方式。Exec 模式虽然开发成本比较小,但运行效率上有差距,本质上是把所有的语言都当做脚本语言对待,等于放弃了各种语言这么多年性能的优化成果。如果是比较重的事件处理,还可以承受,但如果要承载高并发任务就比较困难,这也是为什么 Azure 后来放弃自己的第一版 Function 实现的原因。当前看来,如果要做通用的 FaaS 兼顾性能和效率,还是要通过 Runtime 的方式。但 Runtime 的方式带来的一个问题是依赖的管理和隔离,如果 Runtime 没有很好的机制隔离用户代码和 Runtime 的代码,二者的依赖就有耦合,导致用户的 Function 的依赖选择上受限于 Runtime 的平台。
  2. Function 之间的调用。Function 之间如何调用?当前各平台的实现尚未完美解决这个问题。比如 AWS 可以通过自己的 cloud sdk 来调用,但调用和认证机制和直接从外部调用 Function 没有区别,使用略复杂,同时担心有效率问题,尚无直接从内部调用 Function 的机制。理论上同一个应用的 Function 应该有内部服务直接调用的机制,这样便于应用的分层拆解。当然 Function 之间的调用带来的另外一个问题是死循环问题,如果不小心陷入死循环,平台要有能力追踪并中断,避免引起调用风暴,浪费大量资源。
  3. 自动伸缩判断标准。如果是 Exec 模式,每个进程都只负责一个请求,请求数直接就作为伸缩标准,但如果是 Runtime 的机制,每个 Runtime 环境能否承载并发请求?如果可以,如何判断伸缩标准?比较重的事件(比如视频转换)和轻事件(比如 IoT 事件)处理逻辑上是否可以统一?异步事件和同步调用的处理逻辑上是否可以统一?如果引入 Function 之间的调用之后,这个机制会更复杂。
  4. Function 输入输出的标准化。Function 的输入输出能否标准化?当前各云也是在探索。比如 Function 如果要对接 API Gateway,它的输入输出要求就和对接队列事件不同,这种有没有一种通用的标准化的方式?如果有了这种标准化的方式,在 Function 之上,封装 GraphQL 这种就很容易了。
  5. Function 中如何依赖其他的有状态资源,比如数据库?如果用户在 Function 内部初始化数据库连接,那就没法使用连接池等技术。这个问题 Azure 的解决方案是一种思路,另外 J2EE 的 jndi 或者 serverlet 规范的思路也可以借鉴。
  6. Function 中能不能直接 fork 新的 Function 实例?类似于启动新的线程或者进程?相当于把调度也让开发者通过代码控制。或者说 Function 中的并发机制是如何的?用户启动新的线程是否有意义?这种机制如果配合 Actor 模型,相当于把 FaaS 变成了一种分布式的 Actor 开发平台。
  7. DevOps 以及升级机制。当前公有云的 FaaS 基本只有脚本语言支持在线编辑,编译型的语言都需要用户自己构建打包,调试只能靠日志,如何平滑升级当前也没有很好的方案。未来应该是可以构建统一的 WebIDE + 源码仓库 + CI/CD 交付流。

FaaS 和 PaaS 的关系和区别

从目标来说,二者有相似之处。这里的 PaaS 单指 aPaaS,application platform as a service。PaaS 的目标也是给用户的自定义应用提供一种标准化的运行环境。但当前的以 GAE 以及 Cloud Foundry 为代表的 PaaS 解决方案,无论是公有云还是私有云,都一直处于一种不温不火的状态。这一方面是因为时机问题,早期的云用户主要关注的还是资源交付,不是应用交付,另外一方面 PaaS 如果试图要实现应用标准化,必须对应用有侵入和约束,同时 PaaS 接管的还是应用,开发者也是按应用交付的,也就是说,这种约束还需要应用开发者自己在应用中引入框架和 PaaS 平台交互实现。同时当前的 PaaS 并没有彻底屏蔽资源,没有实现按需运行和按需伸缩,很难做到以同一种架构应对用户规模的增长。

而 FaaS 的解决方案就比较彻底,用户交付的不再是应用,而是具体的业务逻辑模块,应用的组装定义由 FaaS 平台或者更高层的应用平台来定义。给 Function 提供标准化运行环境的难度要小于给应用提供标准化运行环境,同时 FaaS 平台的接管能力要大于 PaaS ,可能实现以同一种架构应对不同用户规模。

所以随着 FaaS 的成熟,PaaS 会逐渐放弃托管用户自定义的应用,而蜕变成给 FaaS 提供事件源以及状态存储的平台,比如数据库,消息队列,API GateWay 等标准化服务,而 FaaS 变成托管用户自定义逻辑的平台。

在公有云上,无论上面提供的这些标准化服务是否运行在统一的 PaaS 平台上,对开发者来说,属于一种统一的平台。而在私有部署场景下,传统的 Cloud Foundry 这样的 PaaS 也只是托管用户自定义的应用,数据库这样的服务依然依赖外部平台提供,它只是负责整合。而随着容器和 Kubernetes 的成熟,基于 Kubernetes 构建的 PaaS 平台,可称之为 Generic PaaS,基本可以托管任何标准化的应用程序,有能力为 FaaS 提供这样的 PaaS 环境。

所以二者有一定的替代关系,可以理解为 PaaS 扩展了自己的外延,而 FaaS 接管了一部分 PaaS 的功能。同时应用如果要实现真的弹性伸缩,需要二者紧密配合。

FaaS 的应用场景以及潜力

  1. 微服务 个人一直认为 FaaS 是微服务的终极演进结果。微服务一直没有一个明确的标准是拆到多细算是微服务,FaaS 给了一个标准:冷启动时间在毫秒量级,以及资源使用受系统上限约束。有人觉得全拆成 Function,是不是太细了?但实际上 Function 只是个抽象的概念,你也可以把整个应用的请求全部指向一个 Function,然后在内部做路由,只要能保证冷启动时间。另外微服务本质上还需要解决服务间的调用,追踪,熔断/重试等机制,这些在传统的方案里,都是通过应用内嵌框架来解决的,而微服务领域最近很火的 Service Mesh 的解决思路是在网络层处理,这样就可以独立交付,而 FaaS 可以说异曲同工,它直接从应用中剥离了一层出来,然后具体是内嵌框架还是通过 Service Mesh 实现,对用户来说没有区别。

  2. 视频,图片以及流式事件处理 本质上是需要一种通用的,可自定义的,工作流应用。当前的工作流一般都是针对具体场景的,尚无支持自定义逻辑并且适用于各种类型事件的分布式工作流。而基于 FaaS 有可能诞生这样一种工作流。另外类似于 Storm 这样的流式大数据处理平台,也可以理解成一种基于特定语言的 FaaS 平台,FaaS 和流式数据处理平台的整合大有前景。

  3. 事件驱动以及响应式架构 这个场景和前一个场景有相似之处,只不过前一个关注的是应用场景,这条单指技术架构场景。服务器端的事件驱动和响应式架构和客户端技术相比,一直缺少一种统一的体系解决方案,主要原因是服务器端缺少分布式系统级别的支持,纯开发框架的方式实现比较困难,如果调度系统和开发框架配合,实现这种架构就比较容易了。

  4. IoT 物联网场景实际上和前面的流式事件处理以及事件驱动架构都有关系。这里单独作为一条阐述,主要是物联网对应用开发带来的不仅仅是架构上的变化。互联网主要是信息技术,主要是面向人的应用,要求及时把信息展示给用户,所以应用多是 http 的请求响应模式,对延迟比较敏感(毫秒级)。而物联网场景下,多是事件触发,哪怕有人参与的场景,比如智能开关,也是触发事件后控制另外的设备,对延迟忍耐度较高(秒级),协议多也不是 http,而是物联网相关的消息协议。

  5. 应用系统的自定义扩展需求 任何一个标准的系统,发展到一定程度都会有不同的自定义扩展需求。一种是提供内置扩展机制,比如 Java 的许多应用,可以允许在应用中增加扩展,应用自己通过 jvm 的隔离机制提供插件运行环境。另外一种是通过远程接口(无论是 http 还是其他远程协议),由用户按照协议实现自定义需求,然后整合,应用本身不提供扩展运行环境。前者对编程语言有约束,隔离性差,后者开发运维成本比较高。如果基于 FaaS 支持一种分布式的扩展运行环境,自动和应用整合,相当于兼有了二者的优势。可以预见,在未来几年里,大多数 SaaS 以及 API 服务都会提供类似 FaaS 的环境来托管用户的自定义扩展。如果私有环境中也有标品,私有部署的应用也会逐渐提供这种整合能力。

  6. 跨云与混合云场景 当前大多数混合云解决方案都只能做到基础设施的混合,至于用户的应用要实现多云,则只能在用户自己的应用中处理,云平台能提供的帮助有限。但因为 FaaS 侵入了应用的架构,接管了应用的事件输入,乃至事件输出,所以它可以做的更多,也可能提供一种基于 FaaS 的混合云开发框架,用户按照架构模式实现逻辑就天然跨云。

  7. 边缘计算 边缘计算当前的应用场景还没凸显出来,但可以预见的是:

    1. 边缘的计算能力肯定不如云端,更小的资源使用粒度对边缘更友好。
    2. 边缘的具体资源要对用户透明。

    从这两点来看, FaaS 对边缘计算是天然友好的。同时,边缘计算要解决的很多问题和混合云场景类似。

国内的 FaaS 案例

  1. 同程旅游首席架构师王晓波在 ArchSummit 2017 上发表《从微服务到 Serverless 架构:享受纯粹的编程乐趣》的演讲,通过 Serverless 提高交付效率,
  2. VPGame CTO 俞圆圆在 GIAC 2017 大会上发表 《电竞数据的容器实践 — Serverless 的电竞数据计算平台》的演讲,通过 Serverless 提高资源利用率。
  3. 今日头条视频架构负责人侯爽在 GIAC 2017 大会上发表 《今日头条大规模视频处理的挑战与应对》,通过 FaaS 构建视频处理平台。

以上是公开的大会上找到的案例分享,肯定还有一些尚未公开分享的实践,但可以看出,有的关注交付效率,有的关注资源利用效率,有的是 web 服务,有的是工作流应用,已经很具有代表性。国内对 Serverless/FaaS 的关注才刚刚兴起,但国内的互联网用户规模的优势,可能促使 FaaS 更快落地。

总结

从前面的分析可以看出,FaaS 所使用的技术并没有非常的新,解决方案也不是新的,甚至有些『返祖』倒退,但放在当前云和分布式容器引擎成熟的场景下,确能发挥出极大的威力,是一种技术的螺旋式上升结果。FaaS 以及它之上的生态链尚未成熟,带来的变革也尚未完全凸显出来。但基于它的运行机制和提供的能力,可以做一些预测:

  1. 它的标准化和冷启动方式,一定程度抹平了程序库(lib)和服务之间的区别。以前程序库如果要变成服务,需要开发者自己把它包装成服务长期运行,而 FaaS 场景下,程序库可以包装成 FaaS 的 Function 标准,直接安装到 FaaS 平台中,按需激活运行。随着 FaaS 的逐渐成熟,肯定会出现 Function 仓库或者市场这样的平台,用于 Function 的分发以及商业化,这个会重构整个软件的交付机制。
  2. 以它对应用的组装方式上来看,可以逐渐演化出许多标准化的工作流或者叫业务流,更有利于标准化业务逻辑的沉淀,自定义的场景下只需要替代部分 Function 即可。
  3. 从它对应用的拆解方式上来看,由于它接管了业务逻辑的输入输出,所以可以提供一些更高级的应用框架。当前的应用框架完全包含到应用内部,而未来的应用框架会是 sdk 和平台的交互。

可以从两个角度看 FaaS,一个是资源利用率,一个是开发效率。

对公有云来说,最明显的特点是资源利用率,FaaS 的按需运行计费机制,貌似只是是帮用户省钱,用户如果将已有的应用改造成 FaaS 模式,公有云的收入应该是降低了。但实际上,这种方式在于激活更多的需求,原来的模式下,许多低频的需求实际上是被忽略了,因为给低频需求提供服务会导致入不敷出。另外 FaaS 可以让云更贴近用户业务,本质上是由云来定义应用,通过云将用户的逻辑块串联起来,可以更方便的实现更高级的调度(比如 AI DevOps),以最大化的提升资源利用率,所以肯定是未来云的兵家必争之地。当前的公有云的优势是资源以及已有用户,但挑战是当前的公有云尚不是一个面向开发者的平台,用户多把它当做资源交付平台,而 FaaS 本质上是一个开发者平台。

而对私有云来说,可以把 FaaS 叫做 Distributed FastCGI 或者 Distributed Function Application Server,更主要关注的是开发交付效率。团队分工上也会更明确,基础架构以及 SRE 团队负责 FaaS 平台的优化改进以及运维,业务团队负责实现业务逻辑。另外当各家公有云的 FaaS 平台都不互相兼容的情况下,开发者有可能因为 vendor lock-in 的问题而选择开源方案,开源的 FaaS 也更贴近开发者。同时开源方案可以做到在语言规范层兼容公有云标准,在混合云或者多云场景下会有优势,但挑战是 FaaS 需要和各种系统事件交互,能否通过抽象让各种系统事件也兼容,这个是比较困难的。

总的来说,这一波以应用为中心的云原生应用浪潮带来的变革,才刚刚开启,会是下个十年云的主题。无论 FaaS 波及的范围会到那一层,无论未来的应用平台是不是现在 FaaS 这个样子,但大的方向是确定的,并且 FaaS 已经在这个方向上前进,值得期待。

相关链接


  1. Status of Serverless Computing and Function-as-a-Service(FaaS) in Industry and Research 这篇论文讨论了当前 FaaS 工业界状态以及未来的可能
  2. Snafu: Function-as-a-Service (FaaS) Runtime Design and Implementation 这篇论文探索了一下 FaaS 的实现以及隔离方式,并且用几种开源以及公有云的 FaaS 进行了比较,提供了prototype 的一种实现
  3. Evolution of Server Computing: VMs to Containers to Serverless — Which to Use When? Gartner 的 serverless 报告
  4. Serverless 应用开发指南 一本 serverless 应用开发的电子书
  5. Introducing Redpoint’s FaaS Landscape 对 FaaS 社区的整体概览描述
  6. 并发之痛 Thread,Goroutine,Actor
  7. 本文的 Gitchat 地址 可以查看答疑实录或者进行提问

更新说明:

2018-01-09:修订文章中关于阿里云 Function 支持语言的描述,阿里云的 FaaS 测试的时候选择语言在第一个界面,不在编辑器界面,所以误认为只支持 nodejs。