abstract

本文简单介绍serverless的基础概念、特点,在下篇中会介绍开源实现和原理等细节。

引言

IFTTT

在介绍serverless之前,我想先提一下IFTTT, 不知有读者对这个是否熟悉?

tip

IFTTT 是 If This Then That 的缩写,它是一个新生的网络服务平台。通俗的来讲,IFTTT 的作用就是如果触发了一件事,则执行设定好的另一件事。所谓的「事」,指的是各种应用、服务之间可以进行有趣的连锁反应。用户可以在 IFTTT 里设定任何一个你需要的条件,当条件达到时,便会触发下一个指定好的动作。它就像是一座神奇的桥梁,能连接我们日常所用的各种网络服务。

简单来说IFTTT就是一款手机App,可以让我们自定义一些触发条件和动作来完成一些功能。

那么具体可以做什么呢?我这里举几个例子。

首先来一个简单的:

如果第二天气温降到 10 度以下,自动发通知提醒我添衣保暖。

再来两个复杂的:

可以看出来IFTTT能做的事情还是相当多的,那么最早我是怎么关注到这个应用的呢?

其实是最早有一次,我收到来自女朋友公司保安大叔的电话,说我的车一直停在那没有熄火(女朋友开我的车去上班),我黑人问号脸中想到了可能是女司机综合征?在一边心疼车的时候,一遍捉摸着怎么能再避免粗心的女朋友再次忘记熄火呢?

这时候通过IFTTT就十分简单了,贴两张图就很清晰了:

这样当女朋友的手机到达指定地点,就会自动收到这样一条短信,再也不怕停车忘记熄火了(facepain)。

总结

可以看到,我们需要指定一个触发器(trigger),这个触发器代表了一个事件,以及一个动作(action),当达到指定的条件,就做指定的事情,这样一个简单的逻辑却可以帮我们做很多事情。

那么我们可以总结一下,这几个case都有什么共同点呢?

abstract

  1. 首先他们都是事件(event)触发的,如温度达到10度、收藏一篇文章、进入一个区域等等,这些事件都是IFTTT这个平台提供的能力;
  2. action是用于响应事件的一个具体动作,在IFTTT中,也是平台提供的能力,换言之,具体能执行哪些动作,得看平台支持哪些;
  3. SaaS的使用体验,我们只需要关心业务逻辑即可。

为什么要举这个例子呢,我觉得其实这个和serverless的特征很像,是一种具象化的、概括化、简单化的的serverless的场景体现。

什么是Serverless

Serverless的字面意思就是无服务器,无服务器是一种技术架构,指的是由开发者实现的服务端逻辑运行在无状态的计算容器中,它由事件触发, 完全被第三方管理,其业务层面的状态则被开发者使用的数据库和存储资源所记录。简单来说就是我们交付应用时,只需要交付函数即可,这个函数会响应一定的事件,由触发器在指定事件到来时,自动运行改函数。

这是Google Cloud官网放过的一张图,是对serverless很好的一个分层概括。最早我们是裸金属(bare metal)的服务器上面直接跑业务,后来我们将基础设施(infrastructure),我们有了virtual machine和container,我们基于这些infrastructure去部署我们的业务(app)。而这之中,serverless就是构建在虚拟机和容器之上的一层,serverless和应用本身的关系更为紧密。

通过infrastructure,我们不再关心底层运行环境(虚拟机、OS、容器等)的一切细节,而只关心我们的App和function,如果app和function可以脱离底层infrastructure层,独立交付、部署和运维,则其属于serverless的定义。

我的理解是,当年容器出现时,改变了软件的交付、部署和上线模式,serverless也是如此。

为何会出现serverless

这是Azure官网的一张图。

无服务器是云平台多次迭代的巅峰。这种演变始于数据中心的物理计算机,然后经历了基础架构即服务(IaaS)和平台即服务(PaaS)的发展阶段,最终来到无服务(Serverless)。

这句话描述了云平台的演变历史。

在最初上线一个服务时,我们需要考虑的是买什么型号的服务器,需要什么型号的CPU?多大内存?是否使用磁盘整列?这属于前文提到的裸金属时代(bare metal)。

后来有了IaaS,但还不是太完美,虽然我们不用关心物理服务器了,但是我还需要关心系统需要什么版本,系统是否需要备份,是否需要升级,是否需要监控等等。

再后来有了PaaS之后,连这些都不用关心了,可是依然有问题,我的服务需要几个节点,怎么做扩容和缩容,什么时候做?

最终来到Serverless面前,我们只需要关心我们的函数,连扩容和缩容我们也不用再关心了。

纵观云平台的演变历史,果然懒是第一生产力(笑)。

FaaS和BaaS

Serverless有两个领域,分别是FaaS和BaaS。

BaaS(Backend as a Service)后端即服务,我的理解是,云平台提供的后端API能力,这些能力可以直接被开发者所使用。比如身份验证服务Auth0,这些BaaS通常会用来管理数据,还有很多公有云上提供的我们常用的开源软件的商用服务,比如亚马逊的RDS可以替代我们自己部署的MySQL,还有各种其它数据库和存储服务。类比前文提供的IFTTT,BaaS就表现为IFTTT平台上提供的API能力,这些API贡献了所有可被用来做触发器的事件。

FaaS(Function as a Service)函数即服务,是无服务器计算的主要形式,开发者提供的函数作为实际的业务逻辑,在事件到达时会被执行,function中一般也会依赖BaaS提供的后端能力来完成具体的事件响应。当前使用最为广泛的FaaS服务是AWS的Lambda。

FaaS服务

这是阿里云官网对阿里云FaaS的介绍,我们再来看一下AWS的。

可以看到他们各自都提到了FaaS的两个显著特点:不用关心基础设施、只在函数运行期间才收费。

使用AWS Lambda之后,我们不需要再关心vm和容器,只需要关心我们的业务逻辑和代码(函数)。运行时,Lambda会根据请求量进行扩容和锁容,而这一切都是自动完成的。更棒的是,只有函数的实际执行期间才会收费。这意味着,Serverless可完美解业务峰值(如促销)和低谷期间的扩缩容调整,以及这背后节省下来的成本。

FaaS的优势

在Serverless之前,我们在部署业务申请机器资源时,一般都会根据峰值来估计最大开销,并还需要预留一定的buffer空间,这使得我们申请的机器资源往往是过度配置的,利用率不高的,同时还意味着即使在闲置状态依然需要按照峰值去计费。

所有的这些都能概括为FaaS的几个显著的优势:

  1. 无需管理服务器:这个我们我们已经提及很多了,开发者只需聚焦自己的业务函数,降低了运维成本;
  2. 持续扩展:FaaS会自动完成扩容和缩容,这使得FaaS拥有近乎无限扩容的能力,也非常适合像MapReduce这样的大规模计算场景。在Lambda中,当平台收到第一个触发函数的事件时,它将启动一个容器来运行你的代码。如果此时收到了新的事件,而上一个函数仍然在处理上一个事件,平台将启动第二个容器实例来处理事件。容器在运行完代码之后会立刻销毁,始终按需响应、创建和释放。
  3. pay-as-you-go:最关键的是FaaS拥有无比低廉的价格(Lambda每百万次请求0.2美元+每百万GB秒运行时间16.67美元),此外对于Lambda而言,顺序访问1个容器的100次请求,和并发访问100次容器中的1个请求实际收费是一样的。

FaaS的应用

AWS对Lambda的一个显著应用,即把Lambda放到了CDN上。在此之前,CDN只能分发静态内容,并对静态内容做一些定义好的预处理操作,比如图片的resize。但是CDN上有了lambda之后,相当于对内容的进一步处理交给了客户。

客户可以在最靠近用户的地方完成对CDN资源的进一步处理,如根据cookie等信息进行rewrite、A/B测试、对图片语音等资源transcoding等等。

再举几个来自Azure官网的例子:

FaaS的劣势

Serverless的有自己适用和使用场景,但主要受限的地方在于以下几个方面:

  1. 不适合有状态的服务: Serverless架构中,每次容器实例运行完即释放一切资源和状态信息,这要求不同请求处理间都是无状态的,而有状态的服务Serverless无法胜任;
  2. 延迟始终是个问题: Serverless中的应用是高度分布式的、低耦合的,这意味服务间的网络RPC调用带来的时延将始终是一个问题,此外也会有我们后面提到的cold start的问题;
  3. 强平台依赖性: 对于触发器和事件的支持、Function中依赖的Backend API都对底层平台有非常强的依赖性,这意味在平台间迁移的成本会非常高;
  4. Runtime受限: 容器实例的内存、磁盘、MaxFD、最长执行时间、并发数量等等都有严格限制。

其中,以Lambda为例,对于实例的限制有:

如何使用FaaS

触发器

触发器是触发函数执行的方式。在事件驱动的计算模型中,事件源是事件的生产者,函数是事件的处理者,而触发器提供了一种集中的和统一的方式来管理不同的事件源。

在事件源中,当事件发生时,如果满足触发器定义的规则,事件源则调用触发器所对应的函数。

以Azure为例,Azure当前支持的触发器有:

包括HTTP、定时器、数据库、对象存储、消息队列等,这些触发器所支持的事件,如对象存储中上传了一个新的文件,构成了函数的触发方式,而开发者只能依附于平台提供的触发器和给定的事件类型,作为函数的触发依据。

以下是不同FaaS平台对触发器的支持情况对比。

函数

FaaS平台都约定了不同语言的函数签名,以阿里云的python语言为例,给定的函数是这样的:

1
2
3
# The main function
def handler(event, context):
    pass

而开发者去实现具体的函数逻辑,这里贴一个响应阿里云OSS上传事件的函数实例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# coding=utf-8

import json
import logging
from hashlib import md5
import oss2

 # The main function
def handler(event, context):
    logger = logging.getLogger()
    logger.info('start worker')
    evt = json.loads(event)
    endpoint = 'oss-{}-internal.aliyuncs.com'.format(context.region)
    creds = context.credentials
    auth = oss2.StsAuth(creds.access_key_id, creds.access_key_secret, creds.security_token)
    bucket_name = evt['bucket']
    bucket = oss2.Bucket(auth, endpoint, bucket_name)
    object_name = evt['object']
    r = bucket.get_object(object_name)
    m = md5()
    while 1:
        data = r.read(4096)
        if not data:
            break
        m.update(data)

    return m.hexdigest()

其中,event对象是一个json字符串,随输入的事件定义而异,context对象包含了此次事件的上下文,如认证信息、地域、用户信息等。

此外,Java函数对象的定义如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/*
if you open the initializer feature, please implement the initializer function, as below:
module.exports.initializer = function(context, callback) {
  console.log('initializing');
  callback(null, ''); 
};
*/

module.exports.handler = function(event, context, callback) { 
  console.log('hello world');
  callback(null, 'hello world'); 
};

以下是不同FaaS平台对语言的支持情况对比:

总结

对Serverless特点的总结主要有:

  • 一个松散的、高并发的、高分布式的系统
  • 事件驱动的计算模式
  • 一个平台深度绑定的系统
  • 一个开发高效、上线简单、无需运维的系统
  • 一个受限的Runtime:
    • 无状态
    • 最大资源限制
    • 最大运行时长限制
  • 一种软件交付、部署模式
  • 一种微服务的极致形态

其中,Serverless的Runtime限制,要求你需要把函数设计的尽可能简单,一次只做一件事,但做到最好,很符合unix的设计哲学。

对于开发者而言,Serverless提高了资源利用率和开发效率,提供了动态扩锁容的调度能力,降低了后期的运维成本。

之前看到一个人说的很好,生产力决定生产关系,云计算的演变本质上还是对生产关系和生产力的配置和优化。在我看来,Serverless这种体系结构,就像当年docker提供了一种基于容器打包runtime的应用交付模式一样,只不过这里更细更深,从容器交付到代码层面的、函数交付。

下篇会具体聊聊开源FaaS的具体实现,以OpenFaaS为例,分析一下其架构和实现细节。