无服务器架构
作者:Mike Roberts
目录
无服务器架构是一种应用设计方法,这种方法在 FaaS 平台之上,在受管理的临时容器中,把第三方 BaaS 服务以及客制代码结合起来提供服务。贯彻这种思路,以及单页应用等相关概念,能能够大大降低对保持开机的服务器的需求。无服务器架构能够显著降低运维成本、复杂性以及交付时间;但随之而来的,他增加了对供应商以及相对稚嫩的支持服务能力的依赖。
无服务器运算,或者简单说无服务器,是软件架构界的一个新热点。三大云——亚马逊、谷歌以及微软都在无服务器方面下了重注,我们会看到很多的书籍、开源项目、会议以及软件供应商,都不约而同的关注这一主题。但是什么是无服务器?是否值得重视?为什么值得重视?本文中我希望能在这一问题方面给读者一些启迪。
我们首先来解答“无服务器运算是什么”的问题,然后可以研究一下这一方法的得与失。
无服务器是什么
和软件方面的其他新浪潮一样,目前对无服务器还没有一个清晰的描述。对于初学者,它包含了两个不同但重叠的领域:
无服务器最先用于描述一种主要或者全部由云端第三方应用和服务构成的,用于管理服务端逻辑和状态的(技术)。典型的富客户端应用——比如单页 Web 应用、或者移动应用,会使用大量的云端数据库(例如 Parse、Firebase)、认证服务(例如 Auth0、AWS Cognito)等。在前面的文章中曾经使用 “后端即服务”,简称 BaaS 来形容这些服务。
无服务器还用来形容另一种应用,服务端逻辑还是由应用的开发者编写的,和传统架构的区别是,这种架构由事件驱动,运行于无状态的临时容器中、并且完全由第三方管理。一种理解就是“Functions As a Service”,也就是 FaaS(这个名称的来源,是一条 @marka 的推特,目前已不可见)。AWS Lambda就是 FaaS 平台中最著名的一个,当然,不是唯一一个。
本文的主要焦点就在 FaaS 上:这不仅是因为这一架构比较新潮更加耀眼,更深入的原因是,他和我们现有的技术架构差异颇大。
BaaS 和 FaaS 的一个相似之处是他们的运维属性(淡化资源管理)类似,并经常结合使用。几大公有云都提供了无服务器产品,其中包含了 FaaS 和 BaaS 产品:例如 Amazon 的无服务器产品。Google 的 Firebase BaaS 数据库,也具有 Google Cloud Function for Firebase 提供的 FaaS 支持。
在规模较小的公司中,这两种产品也会有类似的关联。例如 Auth0 初创期间的产品是 BaaS 形态的,提供了各种方面的用户管理方案;接下来开发了相关的 FaaS 形态的服务 Webtask。而且他们还有更进一步的产品 Extend,该产品让其他的 SaaS 和 BaaS 公司能够轻松的在现有产品中加入 FaaS 能力,这样就能创建一个统一的无服务器产品了。
两个例子
界面驱动的应用
我们可以回忆一下一种常见的应用形态:
- 面向客户
- 三层结构
- 服务端逻辑
一个很好的例子就是典型的电子商务 App ——或者怂怂的说:Pet Store?
一般来说这种架构会比较像下面的这张图,例如用 Java 或者 Javascript 完成服务端开发,并使用 HTML + Javasript 组件实现客户端:
这种结构下的客户端相对来说没有智能的,大多数的系统功能——认证、页面导航、搜索、事务等等都是在服务端应用实现的。
在一个 Serverless 架构下,这张图可能要这么画:
虽说这个视图已经做了大幅度的简化,但是和前面比起来,差异还是非常显著的:
- 我们从原有应用中删除了认证逻辑,用第三方的 BaaS 服务(例如 Auth0)取而代之。
- 另外一个 BaaS 的例子,我们让客户端能够直接访问部分数据库(用来完成商品列表),这部分数据库也是由第三方提供的(例如 Google 的 Firebase)。我们的服务端和客户端可以使用不同的安全方案来进行数据库访问。
- 前两点的变化发生之后,可以推导出第三点差异:Pet Store 的一部分服务端逻辑现在转换到客户端了——例如用户会话的跟踪、应用的用户体验结构、读取数据库并转换为可用视图等。这一切都说明,应用本身正在向单页应用的方向转变。
- 我们可能想要在服务端保留一些 UX 相关的功能,例如一些集中运算或者需要访问大量数据的功能。在我们的 Pet Store 中,一个符合这种描述的功能就是搜索。过去我们要实现搜索功能需要有一个持续运行的服务器,而现在,我们可以创建一个 FaaS 功能来响应来自 API 网关的 HTTP 请求。搜索功能的客户端和服务器都从同一个产品数据库中读取数据。如果我们选择使用 AWS Lambda 作为 FaaS 平台的话,因为 Lambda 支持我们旧架构中使用的 Java 和 JavaScript,所以无需完全重写,就可以把原来 Pet Store 的服务端搜索功能的代码迁移到新的 Function 实现中。
- 最后,我们要把我们的订购功能也替换为独立的 FaaS Function,但是这里为了安全考虑,我们选择将其保留在服务端而非客户端。这个功能也会从 API 网关接受请求。在使用 FaaS 的过程中,拆解不同的逻辑需求,分发给不同的部署组件,是一个再寻常不过的任务。
回顾一下,这个例子展示了另外一个很重要的无服务器架构的知识点。在原有版本中,所有的流程、控制和安全都是在中央服务器的服务器应用中管理的。在无服务器版本中,就没有一个集中的关注点了。我们会看到 choreography 而不是 orchestration (个人理解,二者都是服务的编排方式,choreography 是智能的去中心的,同样的差距存在于 SoA 和微服务架构中的端点和管道的关系),每个组件都对架构有更多的感知——这种情况在微服务方法中也是很常见的。
这种实现方法有很多好处。例如 Sam Newman 在 Building Microservices 书中指出,这样构建的应用“通常更具弹性切更易变更”。每个功能都作为一个独立的可更新的组件存在、分拆不同的关注点,同时,Gojko Adzic 在一次谈话中还提到,这一架构方法所具备的成本优势。
当然这种架构设计同样是一种权衡:这种架构需要更好的分布式监控,另外还对底层系统的安全性更加依赖。更有甚者,设计微服务应用时,会有大量的关注点充斥在我们的脑海之中——在设计单体应用的时候可不是这样。多后端组件设计所体现的弹性和成本优势,相对于增加的复杂性来说是否划算,是一个非常需要具体分析的问题。
Building Microservices 一书已经由崔立强、张骏翻译,并由人民邮电出版社出版,中文名为《微服务设计》。
消息驱动的应用
另一个例子是后端数据处理服务。
假设你正在开发一个面向用户的应用,这个应用要求有迅速的 UI 响应,另外还需要捕捉用户所有的活动。联想一下在线广告系统:用户点击广告的时候,我们希望能够迅速的把用户引导到广告的目标连接;同时还需要收集这一点击事件,以此来向广告主收取费用。(这个例子其实并非虚构——我之前的在 Intent Media 的团队就有这个需求,也是用无服务器方式实现的)。
过去会用下图的方式来实现这种应用。“Ad Server” 会同步向用户发送响应(图中没有表示),同时向消息频道发布一个点击消息。这个消息会由 “Click Processor” 应用进行异步处理——更新数据库,扣减广告主的余额等等。
在无服务器的方法中,会变成这样:
看到区别了?架构的变更很小了——这就是异步消息处理在无服务器世界中大放异彩的原因。原本需要有一个长期运行的应用来进行消息消费,现在我们使用一个 FaaS Function 将其替换。这个 Function 在一个供应商提供的事件驱动的上下文中。注意云平台供应商提供了消息转发和 FaaS 环境——这两个系统通常是紧密相关的。
FaaS 环境下,只要运行更多的 Function 代码实例,就可以并行处理更多消息。这可能是我们在实现过程中,需要特别注意的一个新概念。
解密 FaaS
行文至此,我们已经多次提到 FaaS 了,那么 FaaS 到底是什么?这里我们可以看看 Amazon FaaS 产品的说明:
通过 AWS Lambda,无需配置或管理服务器即可运行代码(1)。您只需按消耗的计算时间付费 – 代码未运行时不产生费用。借助 Lambda,您几乎可以为任何类型的应用程序或后端服务运行代码(2),而且全部无需管理。只需上传您的代码,Lambda 会处理运行(3)和扩展高(4)可用性代码所需的一切工作。您可以将您的代码设置为自动从其他 AWS 服务触发(5),或者直接从任何 Web 或移动应用程序调用(6)。
- 首先,FaaS 用来运行后端代码,无需管理自己的服务器以及自己的长期运行的服务应用。“长期运行的服务应用”,是和容器和 PaaS 这样的现代架构的根本差别。回顾刚才提到的点击处理案例,我们把点击处理服务(不一定是物理机,但是肯定是个应用)替换为一个无需具体服务器,也无需保持运行的应用。
- FaaS 的服务侧不依赖具体框架和库。在语言和环境方面来说,FaaS Function 只是普通的应用。例如 AWS Lambda Function 可以使用 Javascript、Python、Go 以及任意的 JVM 语言(Java、Clojure、Scala 等),或者任意的 .NET 语言。然而你的 Lambda Function 还能运行绑定在工件之内的其他进程,所以事实上你可以使用任何能够编译成 Unix 进程的语言(例如后面会提到的 Apex)。 FaaS Function 也有明显的架构限制,尤其是在进行状态处理和长期运行的情况下。我们很快就会遇到这一情况。 再次回到我们的点击处理案例。在向 FaaS 进行迁移的时候,唯一需要修改的代码就是入口方法,也就是启动代码,这段代码用于顶层的消息处理(消息监听器接口的实现),可能只要修改代码签名即可完成任务。
- 跟传统过程比起来,没有了服务应用,所以部署的区别是非常大的。在 FaaS 环境下,我们把我们的代码提交给 FaaS 服务商,服务商为我们提供其他所有的必要资源,完成相应的管理工作,例如虚拟机实例、进程管理等。
- 水平伸缩是完全自动化的、弹性的,当然也是由服务商提供的。如果你的系统需要处理 100 个并发请求,服务商会自行处理,无需额外配置。运行容器是临时的,他的创建和销毁完全是受运行时驱动的。更重要的是,FaaS 语境下,服务商需要处理所有底层资源的供给问题——集群、虚拟机之类的管理工作完全和用户无关了。 再一次检视我们的点击处理应用。某个时间里,我们的客户一反寻常的发出了超出平时十倍的点击量。在传统架构中,我们的应用是否能够安然度过?例如我们的应用是否涉及为同时处理多个消息?如果答案是肯定的,那么一个实例是否就足够处理这种规模的负载?如果我们可以运行多个进程,那么这一伸缩过程是自动的还是手工的?在 FaaS 方法中,这些问题的答案就很确定了——我们的 Function 在设计之初就要有个基本假设:我们要处理水平扩展的并发需求,FaaS 服务商要给我们自动提供处理所有伸缩需求的资源。
- FaaS 中的 Function,通常是被需要一个触发条件的,而触发条件是由服务商定义的。在 AWS 中,触发事件包括 S3(文件或者对象)的更新、时间(定时任务),以及加入消息总线的消息(例如 Kinesis)。
- 多数服务商允许 Function 被外来的 HTTP 请求触发;在 AWS 中一个典型的实现方式就是 API 网关。我们 Pet Store 应用中的搜索和订购两个 Function,都需要使用 API 网关。我们的 Function 也可以直接被内部和外部平台 API 调用,但这就不是一个常用方式了。
状态
FaaS Function 在本地(服务器、绑定实例)状态方面有着严格的限制,这里说的状态包括内存中的变量、本地盘中的数据等。这些存储都是可用的,但是这些状态信息在多次调用之间是没有持久化方面的保障的,也不能假设一次调用中保存的状态会在另一次调用中生效。我们经常会说,FaaS Function 是无状态的,不过更精确的描述是:FaaS Function 中需要进行持久化的状态数据都需要保存在 FaaS Function 实例之外。
毫无疑问,FaaS Function 天然就是无状态的,他提供的就是纯粹的从输入到输出的转换。虽然这不是什么新鲜事情,例如 12 Factor App 中也有一样的提议,但是这可能对一些应用架构产生巨大的影响。这些面向状态的 Function 需要用数据库或者分布式缓存(比如 Redis)以及网络文件/对象存储(比如 S3)来保存跨请求状态数据,或者要求请求中包含更多输入项目来进行状态的传递。
运行时长
典型的 FaaS Function 都要对每次调用的时长进行限制。目前 AWS Lambda Function 的限制是五分钟,超时之后就会被终止。微软和谷歌的产品也有类似限制。
这就意味着,有些长期运行的应用上提供的功能,可能并不能不加重构的适应 FaaS 环境,重构过程要把原来的单个任务拆分为多个协同工作的 FaaS Function。
启动延时和冷启动
Faas 平台在处理事件之前,首先需要初始化 Function 的实例,这是一个需要耗时的操作。即使是同一个 Function,他的初始化过程也可能受到众多因素的影响,产生数毫秒或数秒的差异。这看起来可是很糟糕,但是我们可以先放下这种不安,来看看 AWS Lambda 的例子。
Lambda Function 的初始化并不是热启动(复用一个之前的事件中用过的 Lambda Funtion 及其所在容器)过程,也不是冷启动(创建一个新的容器实例、启动 Function 主进程之类的工作)。而冷启动是个毋庸置疑的缓慢过程。
冷启动的延迟,受制于很多因素:用的什么语言,引用什么库,代码规模如何,Lambda 配置环境怎样,是否需要连接到 VPC 资源等等。其中很多方面都是受开发者控制的,所以降低冷启动延时通常都是可以实现的。
可以和冷启动耗时相提并论的另一个问题就是冷启动频度。假设一个函数在以每秒十个事件的速度运行,其中每个事件处理都需要消耗 50 毫秒,你可能每处理 10-20 万请求之后,会看到一次 Lambda 的冷启动。如果另一个函数每个小时处理一个事件,那么可能每个时间发生的时候你都会看到冷启动,这是因为 Amazon 会在几分钟之后释放不活动的 Lambda 实例。知道这一问题之后,你就会了解到针对你的案例,冷启动会产生什么影响,是否需要执行 “Keep alive” 之类的方法来防止冷启动。
冷启动看起来很让人担心吧?这取决于你的应用的类型和行为模式。我从前在 Intent Media 的团队有一个 Java 实现(常见语言中启动最慢的一个)的异步消息处理 Lambda App,每天要处理几千万的消息,完全没有操心过这个应用的启动延迟问题。这说明如果你正在写一个低延迟的交易应用,可能并不想要使用基于云的 FaaS 系统,这个决策跟实现语言的关系不大。
这种情况下,不论你是否认为对你的应用产生了影响,都还是应该按照生产环境的要求进行测试。这毕竟是一个持续创新的领域:如果你的案例无法通过测试,那么不妨过几个月再来一遍。
我的另一篇文章更深入的讨论了一下冷启动问题。
API 网关
之前我们提到过,无服务器技术有一个要素是 API 网关。API 网关是一个 HTTP 服务器,其中定义了路由和端点,每个路由都有相关联的用于处理路由的资源。在无服务器架构中,这些处理单元通常就是 FaaS Function。
当 API 网关收到请求之后,会在配置中查找对应的路由条目,在 FaaS 路由的情况下,会使用原有的请求来调用对应的 FaaS Function。通常状况下,API 网关可以把 HTTP 请求映射为一种更简单的输入提供给 FaaS Function,或把整个 HTTP 请求作为 JSON 对象传递出去。FaaS Function 会执行自有逻辑,并把结果反馈给 API 网关,API 网关就会把执行结果还原为 HTTP 响应,并将响应返回给调用者。
AWS 有自己的 API 网关(就叫 API Gateway),其他服务商也提供类似的能力。Amazon 的 API 网关是一个 BaaS 服务,作为一个外部服务,他需要用户的配置,但是其供给和运行就无需用户操心了。
除了纯粹的路由请求,API 网关可能还要执行认证、输入验证、返回码映射等功能。
FaaS 场景下的 API 网关有个功能就是,用无服务器的方式创建 HTTP 前端的微服务,并提供伸缩、管理以及其他的 FaaS Function 功能。
我第一次写这篇文章的时候,Amazon 的 API 网关还很不成熟。这些工具的进展是很迅速的。AWS API Gateway 这种组件还不是很”主流“,但是可以庆幸的是他们在持续进步,会越来越好。
工具
刚才提到的成熟度问题也适用于 FaaS 自身。在 2016 年中,这些东西还很粗糙;而 2018,我们看到了可观的进步。我们希望这一趋势能够持续下去。
另外一个例子就是 FaaS 世界中的开发体验。Auth0 Webtask 是首先提高开发体验优先级的案例。接下来是微软在 Azure Functions 产品中也这样提出。Visual Studio 及其反馈功能,一直将开发者体验作为产品的要素,Azure Function 也不例外。本地除错、本地触发之类的功能非常独到。
另外一个还急需增强的功能就是监控,下文会继续讨论。
开源
好吧,目前我们主要讨论的都是商用工具和产品。这是我们使用这类服务的主流方法,但是这里同样也有开源项目的一席之地。
开源 FaaS 工具和框架,尤其是流行的无服务器框架,这些框架针对 AWS API 网关和 Lambda 而来,提供优于 AWS 提供的工具的使用体验。还有一种是提供跨服务商的抽象工具,这对部分用户来说是很有价值的。类似的工具包括 Claudia 以及 Zappa。还有一个例子就是 Apex,他的吸引力在于,让用户可以使用 Amazon 直接支持范围以外的语言开发 Lambda 应用。
几大服务商在开源方面也不甘落后。AWS 自己的开发工具:SAM(Serverless Application Model)也是开源的。
FaaS 的最大益处之一就是无需担心底层的计算资源(服务器、虚拟机甚至是容器)。但是如果用户偏要关注这些怎么办?例如有一些云供应商可能无法满足的安全需要,或者用户自己具备一些服务器。开源软件能够在这些场景下,让用户拥有自己的”有服务器的“ FaaS 平台么?
是的,即使是这个方面也有不少活动。开源 FaaS 方面的早期领导者之一是 IBM(OpenWhisk,现在已经是一个 Apache 项目了)以及(至少对我来说很诧异的一个)微软,他开放了 Azure Function 平台 方面的很多东西。很多其他的自建 FaaS实现,会用各种理由建立在 Kubernetes 这样的容器平台之上。场上还有 Galactic Fog、Fission 以及 OpenFaaS 等值的关注的参赛选手。这是一个既庞大又快速的领域,建议关注 CNCF 的无服务器工作组来保持信息同步。
什么不是无服务器
行文至此,我们已经了解到,无服务器架构就是两个概念的结合体:BaaS 和 FaaS。我们也对其能力进行了一点挖掘。无服务器架构的关键属性究竟是什么?为什么 S3 这么古老的技术我也称之为无服务器?可以阅读我的另一篇文章:Defining Serverless。
在我们开始进入微服务的得与失的讨论之前,我们可以在定义方面再多花一点时间,我们来定义一下,什么不是无服务器。
和 PaaS 的对比
无服务器架构中的 FaaS Function 和 Twelve-Factor applications 非常类似,他是不是只是一个 PaaS 的变体,像 Heroku 那样的?引用 Adrian Cockcroft 的答案:
If your PaaS can efficiently start instances in 20ms that run for half a second, then call it serverless.
需要运行半秒的 PaaS 应用,如果能够在 20 毫秒之中完成实例的启动,那么他就是无服务器。
换句话说,多数 PaaS 应用并不适合在事件触发的情况下完成快速的启动和停止,而 FaaS 平台必须满足这一条件。
如果我是一个优秀的十二军规应用开发人员,这些因素并不会影响我的架构和开发过程,但是对运维就大有影响了。既然我们都是 DevOps 工程师,我们对运维的考虑和对开发的考虑应该一样多,对吧?
运维方面,FaaS 和 PaaS 的一个关键差异就是伸缩。一般情况下,PaaS 环境中需要思考如何伸缩,例如在 Heroku 中,需要运行多少 Dynos?在 FaaS 应用中,这一过程是完全透明的。即使你的 PaaS 应用中设置了自动伸缩,也不会到请求一级(除非是有很特别的流量模型),所以 FaaS 应用在成本控制方面更加有效。
有了这样的好处,还需要 PaaS 么?这里有很多原因,但是工具可能是个大问题。有人使用 Cloud Foundry 这样的设施来在混合云上提供通用的开发体验;迄今为止,FaaS 还没能达到这一成熟程度。
和容器对比
使用无服务器 FaaS 的一个原因就是避免在操作系统层面来管理应用进程。Heroku 这样的 PaaS 服务也提供了这样的能力,上面我们说过 PaaS 和无服务器 FaaS 的区别。目前还有一个流程的进程抽象就是容器了,Docker 就是这类技术的代表。容器管理系统将进程从操作系统级别独立出来独立部署,Mesos 和 Kubernetes 都风光无限。除此之外,我们还看到云供应商都在提供容器平台,例如 Amazon 的 ECS 和 EKS、Google Container Engine,都和无服务器 FaaS 一样,让团队避免管理自己的服务器。所有工作都围绕容器展开,那么还需要有 FaaS 么?
原则上说,这一区别跟上面提到的 PaaS 是一样的,无服务器 FaaS 中的伸缩是自动管理的、透明的,并具备良好的粒度,这是决定于自动的资源供给和分配的。容器平台依旧需要在集群端对尺寸和形态进行管理。
另外我认为容器平台虽然在快速发展,但是目前还不够成熟和稳定。当然这并不是说 FaaS 就成熟了,选择你喜欢的就可以了。
另外还有重要的一点就是容器平台也有了自己的伸缩系统。Kubernetes 有了内置的 HPA,Amazon 的 Amazon Fargate 也承诺提供”无服务器容器“。
正如我们所见,两种系统之间在管理和伸缩方面的差距并不大,因此这种选择更多的是要看应用的类型。例如 FaaS 是事件驱动类型的首选,容器更适合处理同步请求的多端点服务。我认为短期之内很多应用和团队会同时使用两种架构,同时二者的融合会令人相当期待。
NoOps
无服务器可能意味着 “No sysadmin”,但是不等于 “No Ops”,这取决于你在无服务器这个兔子洞里钻了多深。
“Ops” 的含义不仅仅在于服务器管理。他还(至少)意味着监控、部署、安全、网络、支持还有一部分的产品排错和系统伸缩等。在无服务器应用中,这些问题一样存在,还是需要一些策略来完成这些事情。无服务器体系如此新锐,在这方面的难度会更高。
系统管理还是存在的,只是外包给了供应商而已,这事的本身没有什么对错。外包生意满街都是,做法决定结果。不管怎样都应该知道,抽象有可能会不足以完成任务,要知道在某个地方,还是有人类系统管理员正在给你的应用提供支持。
Charity Majors 在第一次无服务器会议上,做了很精彩的演讲。(可以阅读她的两篇文章:WTF is operations 以及 Operational Best Practices)。
存储过程即服务
来自 Camille Fournier 的 Twitter: I wonder if serverless services will become a thing like stored procedures, a good idea that quickly turns into massive technical debt。
我在想,无服务器应用会不会像存储过程一样——一个好主意迅速的变成了巨额的技术债。
我还见过一种 FaaS 的状况就是:“存储过程即服务”。我想这是因为,很多 FaaS Function 的例子都是和附着于数据库的代码碎片。如果我们做的事情都是这样的,那这个名字就很恰当了,但是这其实只是一个 FaaS 的子集,我觉得不应该把这种情况当做 FaaS 的全部。
让 FaaS 离开存储过程的老路是个值得考虑的问题。从存储过程获取的经验,放到 FaaS 的语境中也是很有价值的。存储过程是这样的:
- 通常需要用厂商指定的语言,至少是厂商指定的语言框架和扩展。
- 必须在数据库中执行,不易测试。
- 版本管理难于进行,很少被看做是一个常规应用。
并不是所有的存储过程都有所有这些问题,但是存储过程中至少会出现其中一个问题。我们看看在 FaaS 中会怎样:
在 FaaS 中,在我目前看来问题 1 并不存在,可以不必担心了。
对问题 2 来说,我们只和代码打交道,很明显单元测试方面和其他代码并无区别。集成测试很明显会有些不同,后面我们会继续讨论。
而问题 3,FaaS 只有代码,天生的支持版本控制。目前应用打包有点困扰,不过这方面也在逐渐成熟,例如 Amazon 的 无服务器应用模型(SAM) 以及早前提到的无服务器框架。2018 年初,Amazon 还启动了一个 无服务器应用仓库(SAR),给在 AWS Serverless 服务之上构建应用的组织提供应用和应用组件的分发支持(进一步了解 SAR,可以阅读我的文章:Examining the AWS Serverless Application Repository)。
收益
前面我们主要在定义和解释无服务器架构的概念。现在我们讨论一下,如此的应用设计,带来的好处和问题。没有严肃的评估和权衡,是不应该做出任何决策的。
接下来我们就看看无服务器的好处。
降低运维成本
最大程度的简化之后,可以说无服务器框架是一种外包方案——将服务器管理、数据库管理甚至应用逻辑外包出去。由于使用的是一个可能有很多其他用户都在使用的预定义服务,这样就产生一种规模效应:服务商正在运行上千类似的数据库实例,因此用户可以以相对更低的价格来获得数据库服务。
主要从两个方面降低了成本:首先是从共享基础设施(例如硬件、网络)方面;第二是人力成本方面:同等规模的情况下,外包的无服务器系统,相对于自行开发和支撑的应用话费要低。
这种获益,跟从前我们从 IaaS 和 PaaS 之中的获益并没有太大的差异。但是我们可以分别从 BaaS 和 FaaS 两方面来扩展这方面的讨论。
BaaS:降低开发成本
IaaS 和 PaaS 都是建立在服务器和操作系统的标准化基础上的。类似的,BaaS 的基础就是应用程序组件的标准化。
认证就是一个好例子。很多应用都开发了自己的认证功能,一般包含了注册、登录、密码管理以及和其他认证系统的集成等功能。总体上看,绝大多数应用的这种功能都是类似的,Auth0 这样的服务应运而生,我们可以直接把这样完备的功能集成到我们的应用中,不再需要自行开发。
另外一个例子就是 Firebase 这样的 BaaS 的数据库。有些移动应用团队有让客户端直接和服务端数据库打交道的需要。BaaS数据库消除了大部分数据库管理开销,迎合无服务器应用程序的需求,为不同类型用户提供了各自合理的授权机制。
因为大家各自的不同背景,可能会有人不以为然(后面也会提到一些问题),但毋庸置疑,不少成功企业都已经在几乎没有自己的服务端代码的情况下交付了成功的产品。在第一次无服务器会议上,Joe Emison 给出了一些例子。
FaaS:伸缩成本
前面写过,无服务器 FaaS 有个有趣的事情就是:水平伸缩是完全自动化的、弹性的,当然也是由服务商提供的。这个特点有很多好处,不过最最根本的好处就是:只为必要的用量来买单,例如 AWS Lambda (的计费能够)精确到 100 毫秒。随用户的流量规模和类型不同,这可能会带来巨大的经济效益。
例:偶发性请求
假设你有一个服务应用在运行,这个应用每分钟消耗 50 毫秒来处理一个请求,一个小时内的 CPU 消耗是 0.1%。如果这个应用部署在自己的独立主机上,很明显会是非常低效的。上千个类似的应用来共享这台机器才合理。
无服务器 FaaS 就能更有效的处理这种情况,从而帮助用户降低成本。例如上面提到的应用,每分钟花费 100 毫秒的 CPU 时间,0.15% 的占用率,用户只要为此付款即可。
如下几方面的好处:
- 一些微服务类的应用,可能只有很小的负载需求,提供了根据逻辑/域拆分组件的能力,如此细粒度的应用可能在运维成本上是让人难以承受的。
- 如果一个公司或者团队希望尝试一些新东西,如果使用 FaaS 的话,只需要非常小的运维成本。如果工作负载非常小,那么供应商的赠金可能就足够使用了。
例:间歇型流量
另一个例子,假设一个业务的流量曲线非常陡峭,可能基线流量是每秒 20 请求,但是每 5 分钟会受到一次持续 10 秒钟的每秒 200 请求(10 倍)的冲击。我们继续假设,你的服务器的最大性能可以满足你的性能基线要求,但是在流量高峰期间,也不想要降低性能表现,怎么办呢?
传统环境下,虽说高峰时段只占总量的不足 4%,还是需要增加到 10 倍的硬件数量,来应对流量高峰。自动伸缩可能在这里也不太合适,新服务器启动完成之后,可能高峰时段已经结束了。
无服务器的 FaaS 在这里就如鱼得水了。如果流量仅是数量不同,原则上你不需要做什么不同的事情,只需要支付高峰时段的额外计算费用即可。
显而易见,这里我们用的例子比较极端,从而凸显了节约成本的效果,但是重点在于,从伸缩的视角来看,除非你有非常稳定的流量模式,能够持续使用服务器的稳定负载,否则 FaaS 始终是一个节约成本的手段。
上文中一个值得注意的提醒:如果你的流量是一致的并且持续的以较好的利用率运行在服务器上,那可能看不到什么成本节约,FaaS 方案甚至可能要消耗更多的成本。建议进行计算,来评估和比较不同方案的差异。
推荐阅读 Gojko Adzic 和 Robert Chatley 的 Serveless Computing: Economic and Architectural Impact(无服务器计算:经济和架构)。
优化才是成本节约的根源
FaaS 的成本问题,还有一个有趣的方面就是:性能优化不仅仅提高了应用的性能,还会直接的对运维成本造成影响,具体的影响范围要看服务商的收费方案粒度和形式。例如一个应用原本需要一秒钟来处理事件。通过优化之后,处理事件降低到 200 毫秒,(AWS Lambda)上,会看到节省了 80% 的计算资源。
简化运维管理
下面的内容需要强调——无服务器方案中依旧存在一些运维工作,但是会有一些改善。
在无服务器 BaaS 中,很明显运维管理要比其他架构简单:支撑更少的组件,就相当于更少的工作。
而 FaaS 中,会有很多方面的影响。下面我会尝试深入阐述几个方面。
FaaS 的伸缩优势不仅在于基础设施的成本
刚刚讲过伸缩的问题,FaaS 方案的优势不仅在于节约计算成本,还因为自动伸缩的能力降低了运维管理的成本。
最好的情况下,如果你的伸缩过程是人工的——也就是说由工程师在服务器集群中增加或删除实例——在 FaaS 中就可以忘记这些,而是让供应商来完成这些任务。
即使是在非 FaaS 架构中实现了自动伸缩,还是需要设置和管理的;而在 FaaS 中这就不需要了。
类似的,因为伸缩是由供应商根据请求/事件来完成了,耗尽内存或者其他性能瓶颈发生之前,不用操心可以同时处理多少并发请求了,至少 FaaS 的部分不用。而非 FaaS 或者下游数据库的部分则要谨慎的根据负载情况进行性能方面的调整。
降低打包和部署的复杂度
FaaS Function 的打包和部署,相对于部署整个服务来说是比较简单的。只要把代码打个 Zip 包,然后上传就可以了。无需 Puppet/Chef,没有启动停止的 Shell 脚本,也不用考虑部署一个还是多个容器。如果是刚起步,甚至都不用打包任何东西——可以在服务商的控制台上直接编写代码(这种方式当然是不推荐的)。
这个过程的重要性无需多言,对有些团队来说——这个好处非常重要:完全 FaaS 系统是不需要系统管理的。
PaaS 方案有类似的部署有点,但是我们前面做过比较:相对于 PaaS 来说,FaaS 的伸缩能力具有巨大优势。
上市时间和持续实验
上面说的运维管理方面的优势,工程师们都很了解。但是对业务方面,这又意味着什么呢?
很明显的一点就是成本:前面也说过:运维方面时间消耗减少,就等于降低了人员需求。但是我认为还有个更大的好处就是上市时间。我们的团队和产品日益精益和敏捷,我们希望不断尝鲜并且快速更新现有系统。持续交付让我们可以在项目稳定的基础上实现快速迭代,从而降低了从创意到部署的门槛,可以用最小代价来进行新的尝试。
成本优势是无服务器技术的最容易表达的优势,时间的缩减尤其让我激动。他让持续实验成为一种可能,对于软件企业来说,这是真正的革命。
更环保的计算?
最近十几年,数据中心的规模和数量都有了巨大的增长,数据中心所需的能源和物料消耗也都极为庞大。谷歌、苹果都在尝试将数据中心建设在可再生能源的附近,从而减少对化石能源的消耗。
空转的服务器在消耗能量——这是我们需要更多更大数据中心的主要原因之一:
一年中,商业和企业数据中心中的典型服务器所输出的计算能力一般在其最大输出能力的 5% 到 15% 之间。 –福布斯
这非常低效,造成巨大的环境问题。
基于云的基础设施架构在一定程度上环节了这种问题——公司已经不再需要为低概率需求购买并长期闲置服务器,而是按需够买。然而这一问题可能因此进一步恶化:服务器易于获得却难于管理,造成大量的服务器因疏于管理而闲置。
不管我们的基础设施用的是自有服务器、IaaS 还是 PaaS 方案,我们始终需要根据应用来做经年累月的决策活动。容量问题需要谨慎对待,所以过度规划其实是个必然情况。而无服务器架构中,我们不需要自行完成这一过程了——服务商需要根据我们的业务量实时的满足我们的性能需求。FaaS 供应商自身则需要通过对所有客户的数据的聚合,来完成自己的容量规划。
这种区别的结果就是跨数据中心的资源的高效利用,相对于传统的容量管理方案,显著的降低了环境的不良影响。
缺陷
所以亲爱的读者们,好话已经说尽,然而世事无绝对,接下来我们就要面对现实,看看无服务器体系的为难之处了。
无服务器架构的可爱之处是显而易见的,但是这其中还是有一些妥协的需要。有些问题是与生俱来的,通过过程控制无法完全杜绝,需要始终保持警惕;另有一些是和当前的实现有关的,随着时间的推移,最终应该会得到解决。
先天不足
服务商控制
任何的外包活动,都会把一些系统移交给第三方供应商进行控制。这种失控有时候会爆发出来,例如系统宕机、意外的限制、成本变化、功能缺失、API 强制升级等等等等。前面提到的 Charity Majors,在 OPERATIONAL BEST PRACTICES #SERVERLESS 的妥协部分对这个问题做了详细阐述:
供应商如果够聪明的话,会在用法上加入很强的限制条款,这样才能更方便的实现可靠的目标。用户所享有的弹性和选项就是混乱和脆弱的来源。当不同客户的喜好发生冲突的时候,很明显供应商会选择多数的一方来进行满足。
多租户问题
多租户指的是多个不同的客户(或者租户)在同一服务器上或同样的托管应用中运行多个软件实例的情况,(多租户策略)是实现我们前面提到的规模效益的关键点。服务商会努力让客户感觉自己是系统的唯一用户,一般来说,好的供应商也都成功的完成了这一工作。但是世事无绝对,有时候有些多租户方案会出现一些安全问题(一个客户能访问到别的客户的数据)、健壮性问题(一个客户的软件故障导致了其他用户的故障)以及性能问题(高负载客户拖慢了其他用户的应用)。
这些问题不是无服务器系统的专利——所有多租户系统都有这种可能。AWS Lambda 现在很成熟,我们大概不会看到她出现这种问题,但是不管使用的是 AWS 还是其他供应商,都应该注意这些 Issue,防止部分服务出现问题。
供应商锁定
很有可能出现的情况就是,正在使用某个厂商的一个无服务器功能,在其他厂商的实现可能会是不同的。如果想要更换成其他厂商,可能需要更新运维工具(部署、监控等),还可能需要变更你的代码(满足不同的 FaaS 界面),更有甚者,架构设计可能也无法幸免于难,需要适应不同供应商的实现方式。
即使系统中存在能够平滑迁移的部分,恐怕还是会被其他的架构组件所影响。如果你使用的是 AWS Lambda,用来响应一个 AWS Kinesis 消息总线的事件。AWS Lambda、Google Cloud Function 以及 Microsoft Azure Function 之间的差异并不大,但是很难把后面两个厂商的东西迁移到 AWS Kinesis stream 上。这说明了,从一个方案到另一个方案的迁移或者说适配,必须把整个基础设施同步迁移才可能完成。
很多人对此感觉惊讶——如果想要换个供应商,可能要做很多事情。这是因为有些用户采用了一种称为“多云”的方式,这种方式把云厂商进行抽象和隔离,从而统一进行开发和运维。一般来说这比单云方式要昂贵。所以虽然供应商锁定是个显而易见的弱势,我仍然推荐选择一个喜欢的供应商,尽可能的使用它的能力。我的另一篇文章 On Serverless, Multi-Cloud, and Vendor Lock In 中对这个问题进行了更多的探讨。
安全问题
在采用无服务器方法的过程中,会遇到很多安全方面的挑战。下面有一个非常简略的列表——还有很多这方面的问题需要读者自行注意。
- 每个无服务器供应商都给你的系统中增加了不同的安全实现。这可能增大了恶意用户的接触机会,可能也会提高被攻击的可能性。
- 如果直接从移动客户端使用 BaaS 数据库,那么就失去了传统应用访问数据库的那种服务端的安全隔离机制。这不是个致命问题,但是对应用的设计和开发过程需要多加注意。
- 组织在采用无服务器方法的过程中,读者可能会受到很大冲击。每个 Function 可能有额外的问题需要解决。例如 AWS 的 Lambda 中的每个 Function 都需要配置一个很容易搞错的 IAM Policy。这是个无法忽视的难题。AWS 的生产环境中的 IAM 管理需要仔细考量。
客户平台之间的重复逻辑
在一个完全的 BaaS 架构中是没有服务端逻辑的——所有都在客户端。如果这是你的第一个客户端应用,那没什么问题,但是很快你需要做下一个平台了,那就需要为你的逻辑重新做一个实现——如果是传统架构的话,本来不需要如此重复。假设在这种系统中试用了一个 BaaS 数据库。所有的客户端应用(可能包括 Web、原生 iOS、以及原生的 Android)都需要和这个数据库打交道,必须学会数据库和应用之间的沟通方式。
还有,如果想要迁移到一个新的数据库,可能要把所有客户端的代码都做出相应的迁移操作。
服务端优化的缺失
一个完全的 BaaS 架构中,就没有在服务端为客户端进行优化的可能性了。Backend for Frontend 模式中,对系统服务器底层进行了抽象和屏蔽,这样客户端就可以更快(在移动应用中更省电)的完成操作。但是全 BaaS 架构中就没有这个便利了。
这一条和前面一条缺陷的起因是一样的,所有逻辑都在客户端实现,而后端则是由服务商提供的。如果使用一些轻量级的服务端框架用来支撑部分逻辑,可能会降低这种问题造成的影响。
在 FaaS 中,不存在服务端状态
在说完 BaaS 的缺陷之后,我们来谈谈 FaaS,之前我提到过:
FaaS Function 在本地(服务器、绑定实例)状态方面有着严格的限制,这里说的状态包括内存中的变量、本地盘中的数据等。这些存储都是可用的,但是这些状态信息在多次调用之间是没有持久化方面的保障的,也不能假设一次调用中保存的状态会在另一次调用中生效。
这一假设的原因是在 FaaS 中我们通常没有办法控制在服务端中我们的容器/服务器的启动或者停止。
另外我还说过,一个解决本地状态的变通方法就是十二军规中的第六条:
12 军规应用的进程是无状态的,不分享任何东西。所有的需要持久化的数据必须存储在一个有状态的后端服务中,这一服务通常是数据库。
Heroku 推荐这种方式,但是在 Heroku 中可以做些变通——这是因为用户能够控制 Heroku Dynos 的启动和停止。如果是 FaaS,就无法绕过了。
所以在 FaaS 中,状态既然不能保存在内存里,那该如何处置?上面提到的方法是使用数据库,例如一个快速的 NoSQL 数据库或者进程外缓存(Redis 之类),或者外部的对象/文件存储(比如说 S3)都是可选的解决方法。但是这些方法都比内存或者本机持久化要慢得多。还是应该慎重考虑,你的应用是否适合如此操作。
这个情况引发的另外一个忧虑就是内存内缓存。很多应用都会把外部读取的大型数据源的数据保存在内存缓存中。你可能会使用 Ehcache 之类的技术来访问引用数据表。又或者在使用 HTTP 服务的时候,因为 HTTP 头指定了缓存,这种情况下你的 HTTP 客户端也会使用本地缓存。
FaaS 不允许使用本地缓存,这种情况适用于频繁使用的 Function。例如 AWS Lambda,我们预期一个 Function 实例持续运行几个小时,没几分钟至少使用一次。这意味着我们可以使用 Lambda 提供的(可配置的)3GB 内存或者 500 GB 的本地 “/tmp”,对一些缓存来说这可能不太够用。否则你可能不该假设进程内缓存的存在,转向使用一些低延迟的外部缓存,例如 Redis 或者 Memcached。当然这需要额外的工作,可能无法满足对应用效率的需要。
实现的缺陷
前面描述了一些 Serverless 的先天问题。我们看看一些缓解这些问题的变通方案。注意只是缓解,不是解决。
剩下的缺点主要是现有的技术限制,随着厂商和社区的持续努力,这些问题会逐渐解决。事实上和本文的第一个版本比较来说,这个列表的已经缩短了许多。
配置
我在编写本文的第一版的时候,AWS 在 Lambda 中提供了很少的配置能力。现在我欣喜的看到,这个问题已经解决了。如果你使用其他的平台,这方面的能力一定要检查一下。
给自己一个 DoS(拒绝服务攻击)
“买者自负责任”,这一原则在 FaaS 交易中非常重要。AWS Lambda 对 Function 有一个指定时间内并行数量的限制。如果这个限制是 1000,那么就说明允许这个 Function 同时运行 1000 个实例。如果需要超过这个限制,那么就可能得到异常、排队或者运行缓慢等后果。
这里的问题就是这个限制在整个 AWS 账号内都有效的。有些组织在生产和测试环境上使用同一个账号。这样也就意味着如果组织内有人做了一个压力测试——试着执行 1000 个并发的 Lambda Function,就成了一个针对生产环境的 DoS 了。
就算开发和生产用的是不同的 AWS 账号,生产环境中一个超载的 Lambda(例如处理客户的批量上传)可能会让其他的实时 API 变得无法访问。
Amazon 在这方面使用保留并行的方式提供了一些保护。这种办法让用户可以限制一个 Lambda Function 的并行数量,这样就不会影响到账号中的其他功能。然而这个功能缺省并不会启动的,它需要谨慎的管理才能使用。
执行时长
本文最初提到过,AWS Lambda 函数如果运行时间超过 5 分钟,就会退出,这一规定已经执行了几年,目前没有迹象表明 AWS 会修改这一限制。
启动延迟
前面提到过冷启动,还引用了我在这方面的一篇文章。AWS 在这方面做出了一些改进,但是问题依然存在,尤其是触发一个 JVM 实现的函数、或者是需要 VPC 资源的情况下。这方面的改进超出了本文范畴,就不再赘述了。
AWS Lambda 已经提到很多了,我想其他的供应商同样会有一些不够漂亮的实现。
测试
基于前面提到的理由,Serverless 应用的单元测试是相对简单的:所有代码都“只是代码”,多数情况下,这里没有什么必须包含的库或者必须实现的接口。
Serverless 应用的集成测试就不简单了。BaaS 的世界里,要依赖外部的系统,而不再是自己的数据库。所以集成测试也要用外部系统么?如果答案是肯定的,那么这些外部系统有提供测试的能力么?能够轻松的管理状态么?外部服务商会为压力测试提供不同的计费策略么?
如果要抛开外部服务独立进行集成测试,那么模拟环境的仿真程度如何?如果供应商没有提供模拟环境,用户可能自己开发么?
FaaS 方面虽然会好一些,但是也有同样的问题。现在已经有办法在本地运行 Lambda 以及 Azure 的 Faas 函数。然而本地环境是不可能完全模拟云端的;依赖本地的 FaaS 环境我是很不推荐的。实际上我认为应该用正式环境运行自动的集成测试,至少是作为部署管线的一部分,本地开发仅应用于开发的交互和除错。本地测试环境在持续的改进过程中,例如 SAM CLI 就为 Lambda HTTP API 应用提供快速反馈的能力。
在执行云端的集成测试之前,还应该注意前面说过的跨账号执行限制。为了更好的完成这一任务,可能需要把测试活动和生产账号进行隔离,或者更细粒度的账号管理也不算过分。
在云端进行测试,而不是在自己的笔记本上,看起来很震撼。但是时代在变化,我们在云端获得的这一能力,事实上 Google 的工程师已经使用了超过 10 年了。Amazon 甚至还希望在云端运行 IDE,我还没有完成这一跳,不过很可能会是个方向。
Debug
FaaS 上的除错是个有趣的领域。这里目前的进展主要是在本地运行 FaaS 函数,上面也说过这一点。微软提为本地运行的函数提供了很棒的由远程触发的除错支持。Amazon 提供了类似的能力,但是还不能用生产事件触发。
对实际运行在云端生产环境的函数是个不同的事情。目前 Lambda 没有这方面的支持,我对此非常期待。
部署、打包和版本
这是一个正在飞速进步的领域。AWS 在这方面做出很多创新,后续章节会进一步展开。
服务发现
微服务世界中服务发现是个常见名词:服务如何调用合适版本的其它服务。在 Serverless 世界中就很少会讨论这一问题。最开始我比较担心,目前已经释然了。很多 Serverless 的应用都是事件驱动的,服务的消费者通常要自行完成注册。基于 API 的 FaaS 通常会在 API 网关之后运行,相关的部署、流量控制也都是由网关完成的。我们甚至可以在 API 网关之前再加一层(例如使用 AWS CloudFront),从而获得跨区域的服务弹性。
这方面的内容我放在这里的原因是,我认为这个问题尚未解决,但是最终结果还是相当乐观的。
监控
监控对 FaaS 来说是比较麻烦的,根本原因就在于容器的不稳定性。很多云供应商提供了一系列的监控支持,我们也看到了很多传统的第三方厂商提供了这方面的能力。然而所有的数据来源都是供应商的基础数据。有时候这是好事,但是仅就 AWS Lambda 来说,他们的数据还很基础。我们真正需要的是对第三方厂商开放 API,以此获得更多支持。
API 网关以及超级 API 网关
ThoughtWorks 在技术雷达中讨论过超级网关(over-ambitious API gateways)。参考链接中是通用的 API 网关,很明显可以用于 FaaS 函数的 HTTP 前端。API 网关在这里的问题是这类产品在自身的配置中包含了很多的应用级别的逻辑。这些逻辑一般来说是难于进行测试、版本管理的,甚至定义本身也具有很大难度。当然,通常情况下其定义过程还是优于在应用代码中实现的。
这样一来就又一个权衡。如果我们将 API 网关视为 BaaS,它提供的能力是否节省了我们的投入?如果我们按照每次请求来支付 API 网关的费用,而不是按 CPU 使用率,那么最大限度地利用 API 网关的功能是否更具成本效益?
我的建议是,对于 API 网关的功能应该审慎考虑,只有在网关产品的长期运行真正能够在部署、监控以及测试方面节省开销的情况下才可以大量使用。如果其配置过程无法使用版本源码或者部署脚本的话,就绝对不要使用。
因为难于定义,Amazon 的 API 网关过去需要使用一些古怪的配置来为 Lambda 进行 HTTP 请求和响应的映射。由于 Lambda Proxy Integration 的出现,这一情况大有改观,但还是需要理解一些细微之处。在开源项目 Serverless Framework、Claudia.js,以及 Amazon 的 Serverless Application Model 中,这些元素自身也得以简化。
运维的差异
上面说过,Serverless 并非 “No Ops”,在监控、伸缩、安全以及网络方面都有很多事情要做。然而开始之后,很容易忽略运维工作。这里一个风险就是失去对安全问题的感知。可能你的应用上线运行后,不小心就出现在黑客新闻中,然后就出现数十倍的流量,然后就陷入 DoS 之中,却无法自拔。
这里就需要教育来跟进。Serverless 系统团队需要及早考虑运维方案,供应商和社区都提供了这方面的支持。包括压力测试、混沌工程在内的方法都应该及早学习。
Serverless 的未来
行文至此,再来讨论一下我认为的 Serverless 技术一些方面在未来的可能发展。
克服缺点
Serverless 还是个新事物。前面提到的问题已经很多,而且一定不是全部。Serverless 最重要的事情就是克服这些问题,至少要大幅改善,尤其是实现方面的问题。
工具
因为这一技术还很稚嫩,工具是个大问题。过去两年中,开发、应用和配置方面都有了很大进步,例如 Amazon 的 Serverless Application Model。尽管 Amazon 和 Google 可以在微软和 Auth0 身上得到更多灵感,但是起步阶段的体验依旧让人无法满意。
我很高兴的看到,云供应商正在使用更高级的发布方法。传统系统中,经常需要编写自己的过程来处理流量转移场景,例如蓝绿部署以及金丝雀发布,亚马逊为 Lambda 和 API 网关都提供了自动流量转移支持。这样的概念对 Serverless 系统更加重要,这种系统中的组件太多——100 个 Lambda 组成的系统进行原子发布是很不现实的。实际上 Nat Pryce 和我提到过一种 “Mixing desk” 方法——将一组组件逐步的从流量中引入或引出。
分布式监控可能是最大的改善。我们看到 Amazon 的 x-Ray 以及大量的第三方产品,但是这个问题显然还没有得到妥善解决。
远程调试同样是个普遍需要。微软 Azure Function 提供了这方面的支持,但是 Lambda 还没有。在远端运行的函数中植入断点是一个非常强大的功能。
最后我期望看到在“大运维”方面的工具——如何更加有效的维护成百上千的 FaaS 函数、配置服务等。例如有的组织可能会需要看到某些服务进程不再需要(常见的安全需求),对跨服务成本需要有更好的分组和可见性(对有成本要求的自治团队来说至关重要)等等。
状态管理
对大部分应用来说,缺乏对服务器内状态的持久化支持是可以接受的;但这并非全部。例如大规模缓存或者会话状态场景都有这样的需求。
比如说一个高吞吐应用可能希望能让函数实例长期存活,从而更好的使用进程内缓存完成任务。在自动伸缩的传统应用中,这一场景也很常见。
如果有一种非常低延迟的进程外数据库访问(例如 Redis),可能会是一个稍好的解决方案。Amazon 已经在 Elasticache 产品 提供了一个托管 Redis 方案,并且使用 Placement Groups 提供 EC2 服务器实例的部署协调。
更多的,我认为我们会看到不同种类的混合(Serverless 和非 Serverless)应用架构来解决外部状态的问题。例如对低延迟应用,可能会使用一个传统的长期运行的服务进程来处理初始请求,在请求和本地环境中搜集所有需要进行处理的上下文,然后将包含上下文的所有请求信息发送给 FaaS 函数 Farm,这样就无需在 Serverless 中寻求数据了。
平台改进
Serverless 的一些缺陷来自于平台的实现。执行时长、启动延迟以及跨函数限制都属此类。这种问题可能需要新的解决方案、或者额外的成本投入来解决。例如允许客户设置 FaaS 函数的两个实例持续可用,从而降低延迟,当然客户需要为此付出成本。微软 Azure Functions 提供的 Durable Functions 以及 App Service Plan-hosted functions 就支持这种做法。
当然平台的改进,不仅仅是为了修复现存问题,因此更加值得期待。
教育
Serverless 技术中很多服务商相关的缺陷都要通过教育来解决。这类平台的每个用户都要进行积极地思考,应用程序供应商的托管会对自身的生态会产生何种影响。我们需要思考一些问题,例如“我们是否需要考虑跨供应商的并行方案以备不时之需?”,或者“在部分失效的情况下,如何实现应用的优雅降级?”。
另一个需要教育的方面就是技术运维。很多团队的系统管理员都比过去少了,Serverless 更会加速这一趋势。但是系统管理员需要做的事情却不再只是配置 Unix 服务器和编写 Chef 脚本了——他们经常要在一线处理支持、网络、安全等事务。
Serverless 世界中,真正的 DevOps 文化显得尤为重要,这是因为有很多非系统管理的活动需要完成,通常都需要开发人员承担这一责任。这可能对于开发团队和技术领导来说并非司空见惯,因此教育和同运维的协作就至关重要了。
提高服务商的透明性
最后,在迁移方面:供应商应该让客户能够更透明和清晰的理解他们的平台,从而对平台能力进行估计以完成迁移。迁移过程颇具难度,但并非不可完成,不可靠的供应商是无法获得客户认可的。
模式的诞生
目前看来,何时以及如何使用 Serverless 架构的问题分析方法还处于起步阶段。现在团队只能向 Serverless 平台抛出所有想法,然后看看哪里走得动,哪里行不通。感谢先驱们的工作,我们开始看到一些推荐的实践方式逐步产生,这些知识无疑会持续的积累下去。
我们已经在应用架构方面看到了一些模式。例如什么规模之后 Serverless 会变得笨重?如果我们能够原子化的对 FaaS 函数进行分组部署,那么如何分组才是好的?我们在向微服务转型的过程中的方法是否适用,或者不同的架构是否会引向不同的方向?
Serverless 应用架构的一个讨论热点就是这一架构和事件思维的关系。AWS Lambda 的 Ajay Nair 在 2017 年 进行了一场演讲,这也是 CNCF Serverless 工作组的主要讨论方向之一。
更进一步,创建 FaaS 和传统(持续在线的)服务组件的混合架构的合理方式是什么?怎样才能更好地把 BaaS 引入现存的生态系统?或者反过来说,什么样的信号表明 BaaS 系统需要集成更多服务端代码?
我们还看到更多的使用模式方面的讨论。一个 FaaS 的标准实例就是媒体转换,不论何时,一个大型媒体文件被保存到 S3 存储中,就会自动运行一个进程来在其他位置创建一个更小的版本。然而我们现在看到很多数据处理管线、Web API 以及通用的胶水代码在使用 Serverless 方式。其中一些模式可以实现为通用组件,直接进行部署。根据这些早期想法,编写了 Amazon 的 Serverless 应用仓库。
最后,我们开始看到推荐的运维模式推进工具的发展。我们如何逻辑上对 FaaS、BaaS 以及传统服务器构成的混合架构进行日志集成?如何有效的调试 FaaS 函数?云供应商会提供很多答案和模式,我期待看到这方面的发展。
全球分布式架构
在宠物店例子中,我们看到单独的宠物店服务器如何拆分为不同的服务端组件,以及一些逻辑被转到客户端之中。本质上这个架构的焦点还是在于客户端和一些已知位置的远程服务。
Serverless 世界现在有了更加模糊的分布。就拿 Amazon 的 Lambda@Edge 来说,这是一个在 Amazon 的 CloudFront CDN 上运行 Lambda 的方式。在这种方式下,Lambda 函数会成为全球分布式架构——工程师的一个上传行为,意味着在全球超过 100 数据中心进行部署。这并不是我们习惯的设计方式,带来了更多的能力和限制。
另外 Lambda 函数能够在设备上运行,机器讯息模型能够在移动客户端上运行,服务端和客户端的界限日趋模糊。事实上我们看到了组件的局限性,Serverless 会再无边界。
超越 FaaS
我看到很多 FaaS 应用,主要是把现存的代码和设计思路进行 FaaS 改造:转换成为一系列的无状态函数。这很棒,但是我希望看到更多抽象或者语言,能够把 FaaS 变成一种底层实现,让开发人员在得到 FaaS 的好处的同时,不用实际的思考如何将应用设计成为不相干的函数群。
例如我不知道 Google 的 Dataflow 产品是否使用了 FaaS 方式的实现,但是我可以想象有人开发了一个产品或者开源项目做了类似的事情,使用了 FaaS 作为实现方式。这里要提到 Apache Spark,Spark 是一个用于大规模数据处理的的工具,提供了高度抽象,可以用 Amazon EMR 和 Hadoop 作为其底层平台。
测试
Serverless 系统中,还有更多集成和验收测试方面的工作可以做,这方面的很多事情是和云原生微服务系统是一致的。
这方面的激进想法包括在生产环境中测试以及用监控驱动开发:代码通过基本的单元测试之后,部署到一个流量子集中,和其他版本进行比较。这种方法可以和前面提到的流量迁移工具相互配合。这不是一个普适工具,但是会提高很多团队的生产效率。
可移植的实现
有两种方式来使用 Serverless,可以降低供应商绑定的风险。
厂商实现的抽象
Serverless Framework 最初是用来简化 Serverless 应用的运维任务,但是还提供了大量无视应用部署位置和数量的中立操作能力。假如能够在 AWS API Gateway + Lambda 和 Auth0 webtask 之间进行方便的切换,想必是非常令人惊喜的,当然这依赖于相关平台所提供的运维能力。
这方面的一个难点就是 FaaS 的接口没有一个统一的标准,但是这就是 CNCF Serverless Working Group 在 CloudEvents 项目的工作。
为多个平台共部署抽象的必要性尚值商榷,抽象的背后可能带来更多的复杂性。例如一个平台中的安全性很可能无法满足另外一个平台的需要。
可部署的实现
不是用第三方提供商的 Serverless 技术听起来可能有点奇怪,但是思考一下如下场景:
一个大的技术组织想要为移动应用开发团队提供一个类似 Firebase 的数据库服务,但是同时希望使用现存的数据库架构作为后端。
前面说过的有服务器的 FaaS 平台——项目中使用 FaaS 形式的架构,但是因为合规等方面的要求,必须在内部使用。
这两个场景下使用 Serverless 方法仍然是有收益的。可以参考一下 PaaS 的先例。最开始的 PaaS 都是基于云的(例如 Heroku),但是很快人们注意到了,在自有系统中运行 PaaS 也是有好处的——也称为 私有 PaaS(例如 Cloud Foundry)。
我能想象,和 PaaS 一样,最终我们会看到开源和和商业的 BaaS 以及 FaaS 实现,尤其是和 Kubernetes 这样的容器平台集成的产品。
社区
在很多城市已经有了颇具规模的 Serverless 社区及其相关的会议、见面会以及在线小组等。我认为这一社区会持续成长,最终与 Docker、Spring 这样的社区并驾齐驱。
结论
Serverless 这个古怪的名字,是一种架构方式,我们可以用这种架构,以更小投入来运行应用中的服务端系统。Serverless 包含两种技术:BaaS 可以在前端应用中紧密的集成第三方的远端服务;FaaS 把长期运行的服务端代码转换为短期的函数实例。
Serverless 不是万能药,所以要警惕那些声称 Serverless 将会替代所有现存架构的人。如果要跟进 Serverless 技术,尤其是 FaaS 之中,首先要知道,这其中有财宝——良好的伸缩和较低的成本;也有恶龙——调试和监控。
不因噎废食是对新技术的一个基本态度,Serverless 有很多积极因素,其中包括降低运营开发成本、简化运维管理以及减少环境影响等。但是我认为最大的好处是,缩短了创建新组件的反馈循环过程。我是精益方法的忠实粉丝,我认为 Serverless 技术符合这一理念,能够尽快的将新方案交付给用户,并且更早获取先期反馈,这无疑是具有很大价值的。