无服务器架构
无服务器架构是将第三方“后端即服务”(BaaS)服务和/或在“函数即服务”(FaaS)平台上运行的自定义代码包含在内的应用程序设计。通过使用这些想法以及单页面应用程序等相关想法,此类架构消除了对传统始终在线服务器组件的大部分需求。无服务器架构可能受益于显着降低的运营成本、复杂性和工程周期,但代价是更多地依赖于供应商依赖关系和相对不成熟的支持服务。
2018 年 5 月 22 日
无服务器计算,或者更简单地说,无服务器,是软件架构领域中的热门话题。“三大”云供应商——亚马逊、谷歌和微软——都在无服务器方面投入巨资,我们已经看到了大量关于该主题的书籍、开源项目、会议和软件供应商。但是,什么是无服务器,为什么它值得(或不值得)考虑?我希望本文能为您解答这些问题。
首先,我们将了解无服务器的“是什么”。我们将在后面讨论这种方法的优缺点。
什么是无服务器?
与软件中的许多趋势一样,关于无服务器是什么,没有一个明确的观点。首先,它涵盖了两个不同但重叠的领域
- 无服务器最初用于描述显著或完全包含第三方云托管应用程序和服务的应用程序,以管理服务器端逻辑和状态。这些通常是“富客户端”应用程序——例如单页面 Web 应用程序或移动应用程序——它们使用庞大的云可访问数据库生态系统(例如 Parse、Firebase)、身份验证服务(例如 Auth0、AWS Cognito)等等。这些类型的服务以前被称为“(移动)后端即服务”,在本文的其余部分,我将使用“BaaS”作为简写。
- 无服务器还可以指服务器端逻辑仍然由应用程序开发人员编写的应用程序,但与传统架构不同,它运行在无状态计算容器中,这些容器是事件触发的,短暂的(可能只持续一次调用),并且完全由第三方管理。您可以将其视为“函数即服务”或“FaaS”。(注意:此名称的原始来源——@marak 的一条推文——已不再公开可用。) AWS Lambda 是目前最流行的函数即服务平台实现之一,但还有许多其他平台。
在本文中,我们将主要关注 FaaS。它不仅是无服务器领域中更新且推动大量炒作的领域,而且它与我们通常对技术架构的思考方式有很大不同。
BaaS 和 FaaS 在其操作属性(例如,无资源管理)方面是相关的,并且经常一起使用。大型云供应商都有包含 BaaS 和 FaaS 产品的“无服务器产品组合”——例如,这是亚马逊的无服务器 产品页面。谷歌的 Firebase BaaS 数据库通过 Google Cloud Functions for Firebase 提供显式的 FaaS 支持。
较小的公司也以类似的方式将这两个领域联系起来。Auth0 从一个 BaaS 产品开始,该产品实现了用户管理的许多方面,随后创建了配套的 FaaS 服务 Webtask。该公司通过 Extend 将这一理念更进一步,它使其他 SaaS 和 BaaS 公司能够轻松地将 FaaS 功能添加到现有产品中,从而创建统一的无服务器产品。
几个例子
UI 驱动的应用程序
让我们考虑一个传统的具有服务器端逻辑的三层客户端系统。一个很好的例子是典型的电子商务应用程序——我敢说是一个在线宠物商店?
传统上,架构将类似于下面的图表。假设它在服务器端使用 Java 或 Javascript 实现,在客户端使用 HTML + Javascript 组件
使用这种架构,客户端可以相对不智能,系统中的大部分逻辑——身份验证、页面导航、搜索、交易——由服务器应用程序实现。
使用无服务器架构,它最终可能看起来更像这样
这是一个非常简化的视图,但即使在这里,我们也看到了许多重大变化
- 我们删除了原始应用程序中的身份验证逻辑,并用第三方 BaaS 服务(例如 Auth0)替换了它。
- 使用另一个 BaaS 示例,我们允许客户端直接访问数据库的子集(用于产品列表),该数据库本身完全由第三方托管(例如 Google Firebase)。与访问数据库的服务器资源相比,我们可能对以这种方式访问数据库的客户端有不同的安全配置文件。
- 前两点暗示了第三点,这一点非常重要:原始应用程序中的一些逻辑现在位于客户端——例如,跟踪用户会话、了解应用程序的 UX 结构、从数据库读取并将数据转换为可用的视图等等。客户端正在成为 单页面应用程序。
- 如果,例如,某些 UX 相关的功能计算量很大或需要访问大量数据,我们可能希望将其保留在服务器中。在我们的宠物商店中,“搜索”就是一个例子。与其像原始架构中那样拥有始终在线的服务器,不如实现一个 FaaS 函数,该函数通过 API 网关(稍后介绍)响应 HTTP 请求。客户端和服务器“搜索”函数都从同一个数据库读取产品数据。
- 最后,我们可以用另一个单独的 FaaS 函数替换我们的“购买”功能,选择将其保留在服务器端以确保安全性,而不是在客户端重新实现它。它也由 API 网关提供支持。将不同的逻辑需求分解为单独部署的组件是使用 FaaS 时非常常见的方法。
如果我们选择使用 AWS Lambda 作为我们的 FaaS 平台,我们可以将原始宠物商店服务器中的搜索代码移植到新的宠物商店搜索函数中,而无需完全重写,因为 Lambda 支持 Java 和 Javascript——我们最初的实现语言。
退一步说,这个例子展示了关于无服务器架构的另一个非常重要的点。在原始版本中,所有流程、控制和安全都由中央服务器应用程序管理。在无服务器版本中,没有中央仲裁者来处理这些问题。相反,我们看到编排优先于编排,每个组件都扮演着更具架构意识的角色——这也是微服务方法中常见的理念。
这种方法有很多好处。正如 Sam Newman 在他的构建微服务一书中所指出的,以这种方式构建的系统通常“更灵活,更容易更改”,无论是整体还是通过对组件的独立更新;职责划分得更好;还有一些令人着迷的成本优势,Gojko Adzic 在 这篇精彩的演讲中 讨论了这一点。
当然,这种设计是一种权衡:它需要更好的分布式监控(稍后会详细介绍),而且我们更多地依赖底层平台的安全功能。更重要的是,与我们最初的单体应用程序相比,我们需要理解的活动部件更多。灵活性和成本效益是否值得多个后端组件带来的额外复杂性,这在很大程度上取决于具体情况。
消息驱动的应用程序
另一个例子是后端数据处理服务。
假设您正在编写一个以用户为中心的应用程序,该应用程序需要快速响应 UI 请求,并且需要捕获所有正在发生的各种用户活动,以便进行后续处理。想想在线广告系统:当用户点击广告时,您希望非常快地将他们重定向到该广告的目标页面。同时,您需要收集点击事件,以便向广告商收费。(这个例子并非假设——我在Intent Media的前团队就存在这种需求,他们以无服务器的方式实现了它。)
传统上,架构可能如下所示。“广告服务器”同步响应用户(未显示),并将“点击消息”发布到一个通道。然后,该消息由异步处理“点击处理器”应用程序,该应用程序更新数据库,例如,减少广告商的预算。
在无服务器世界中,它看起来如下所示
您能看到区别吗?与我们的第一个示例相比,架构的变化要小得多——这就是为什么异步消息处理是无服务器技术的非常流行的用例。我们用 FaaS 函数替换了长期运行的消息消费者应用程序。此函数在供应商提供的事件驱动上下文中运行。请注意,云平台供应商同时提供消息代理和 FaaS 环境——这两个系统紧密相连。
FaaS 环境还可以通过实例化函数代码的多个副本,并行处理多个消息。根据我们编写原始过程的方式,这可能是我们需要考虑的一个新概念。
解开“函数即服务”
我们已经多次提到 FaaS,但现在是深入了解它的真正含义的时候了。为此,让我们看看 Amazon 的 FaaS 产品 Lambda 的开篇描述。我在其中添加了一些标记,稍后会进行扩展。
AWS Lambda 允许您在不配置或管理服务器的情况下运行代码。(1) ... 使用 Lambda,您可以为几乎任何类型的应用程序或后端服务运行代码(2) - 所有这些都无需管理。只需上传您的代码,Lambda 会处理运行(3) 和扩展(4) 代码所需的一切,并提供高可用性。您可以将代码设置为自动触发其他 AWS 服务(5),或直接从任何 Web 或移动应用程序(6) 调用它。
- 从根本上说,FaaS 是关于在不管理自己的服务器系统或自己的长期运行的服务器应用程序的情况下运行后端代码。第二句话——长期运行的服务器应用程序——是与其他现代架构趋势(如容器和 PaaS(平台即服务))相比的一个关键区别。
- FaaS 产品不需要对特定框架或库进行编码。FaaS 函数在语言和环境方面是常规应用程序。例如,AWS Lambda 函数可以用 Javascript、Python、Go、任何 JVM 语言(Java、Clojure、Scala 等)或任何 .NET 语言“一流”地实现。但是,您的 Lambda 函数也可以执行与部署工件捆绑在一起的另一个进程,因此您实际上可以使用任何可以编译为 Unix 进程的语言(请参阅本文后面的 Apex)。
- 部署与传统系统有很大不同,因为我们没有自己的服务器应用程序来运行。在 FaaS 环境中,我们将函数的代码上传到 FaaS 提供商,提供商会完成配置资源、实例化虚拟机、管理进程等所有其他必要操作。
- 水平扩展完全自动、弹性和由提供商管理。如果您的系统需要并行处理 100 个请求,提供商将处理这些请求,而无需您进行任何额外配置。“计算容器”执行您的函数是短暂的,FaaS 提供商根据运行时需要创建和销毁它们。最重要的是,使用 FaaS,供应商处理所有底层资源配置和分配——用户根本不需要管理任何集群或虚拟机。
- FaaS 中的函数通常由提供商定义的事件类型触发。在 Amazon AWS 中,此类刺激包括 S3(文件/对象)更新、时间(计划任务)和添加到消息总线的消息(例如,Kinesis)。
- 大多数提供商还允许函数作为对入站 HTTP 请求的响应而触发;在 AWS 中,通常通过使用 API 网关来启用此功能。我们在宠物商店示例中使用 API 网关来实现我们的“搜索”和“购买”功能。函数也可以通过平台提供的 API 直接调用,无论是外部调用还是在同一个云环境中调用,但这是一种相对不常见的用法。
如果我们回到之前点击处理的例子,FaaS 用不需要配置服务器的东西替换了点击处理服务器(可能是物理机器,但肯定是一个特定的应用程序),也不需要一直运行的应用程序。
但是,FaaS 函数在架构上存在重大限制,尤其是在状态和执行持续时间方面。我们很快就会谈到这一点。
让我们再次考虑我们的点击处理示例。迁移到 FaaS 时,唯一需要更改的代码是“main 方法”(启动)代码,因为它被删除了,并且可能是特定代码,即顶级消息处理程序(“消息监听器接口”实现),但这可能只是方法签名的更改。其余代码(例如,写入数据库的代码)在 FaaS 世界中没有区别。
让我们回到我们的点击处理器。假设我们今天生意兴隆,客户点击的广告数量是平时的十倍。对于传统架构,我们的点击处理应用程序能够处理吗?例如,我们是否开发了应用程序以能够一次处理多个消息?如果可以,一个运行的应用程序实例是否足以处理负载?如果我们能够运行多个进程,自动扩展是自动的还是需要手动重新配置?使用 FaaS 方法,所有这些问题都已得到解答——您需要提前编写函数以假设水平扩展的并行性,但从那时起,FaaS 提供商会自动处理所有扩展需求。
状态
FaaS 函数在本地(机器/实例绑定)状态方面存在重大限制——即您存储在内存变量中的数据,或您写入本地磁盘的数据。您确实可以使用此类存储,但您无法保证此类状态在多次调用之间保持持久,更重要的是,您不应该假设来自一个函数调用的状态可用于同一函数的另一个调用。因此,FaaS 函数通常被描述为无状态,但更准确地说,任何需要持久的 FaaS 函数状态都需要外部化到 FaaS 函数实例之外。
对于天生无状态的 FaaS 函数——即那些提供其输入到输出的纯函数转换的函数——这无关紧要。但对于其他函数,这可能会对应用程序架构产生重大影响,尽管这并非独一无二的——“十二要素应用程序”概念具有完全相同的限制。此类面向状态的函数通常会使用数据库、跨应用程序缓存(如 Redis)或网络文件/对象存储(如 S3)来存储跨请求的状态,或提供处理请求所需的进一步输入。
执行时长
FaaS 函数在每次调用允许运行的时间长度方面通常有限制。目前,AWS Lambda 函数响应事件的“超时”最多为五分钟,然后就会被终止。Microsoft Azure 和 Google Cloud Functions 也有类似的限制。
这意味着某些类型的长期运行任务不适合 FaaS 函数,除非重新架构——您可能需要创建几个不同的协调 FaaS 函数,而在传统环境中,您可能只有一个长期运行的任务来执行协调和执行。
启动延迟和“冷启动”
FaaS 平台需要一些时间才能在每次事件之前初始化函数的实例。此启动延迟可能会有很大差异,即使对于一个特定的函数也是如此,具体取决于许多因素,并且可能在几毫秒到几秒之间。这听起来很糟糕,但让我们更具体一些,以 AWS Lambda 为例。
Lambda 函数的初始化将是“热启动”——重用 Lambda 函数及其主机容器的实例,来自之前的事件——或“冷启动”——创建新的容器实例,启动函数主机进程等。毫不奇怪,在考虑启动延迟时,是这些冷启动引起了最大的关注。
冷启动延迟取决于许多变量:您使用的语言、您使用的库数量、您拥有的代码量、Lambda 函数环境本身的配置、您是否需要连接到VPC 资源等。许多这些方面都在开发人员的控制之下,因此通常可以减少作为冷启动一部分而产生的启动延迟。
冷启动持续时间与冷启动频率一样可变。例如,如果一个函数每秒处理 10 个事件,每个事件需要 50 毫秒才能处理,那么您可能只会在 Lambda 的大约 100,000-200,000 个事件中看到一次冷启动。另一方面,如果您每小时处理一个事件,那么您可能会在每次事件中看到一次冷启动,因为 Amazon 会在几分钟后关闭未使用的 Lambda 实例。了解这一点将有助于您了解冷启动是否会对您的整体性能产生影响,以及您是否可能希望执行函数实例的“保持活动”以避免它们被淘汰。
冷启动是否令人担忧?这取决于您的应用程序的风格和流量形状。我在 Intent Media 的前团队有一个用 Java(通常是启动时间最慢的语言)实现的异步消息处理 Lambda 应用程序,每天处理数亿条消息,他们对该组件的启动延迟没有任何担忧。也就是说,如果您正在编写一个低延迟的交易应用程序,那么您现在可能不希望使用云托管的 FaaS 系统,无论您使用什么语言来实现。
无论您是否认为您的应用程序可能会遇到此类问题,您都应该使用类似生产环境的负载测试性能。如果您的用例现在不起作用,您可能想在几个月后再次尝试,因为这是 FaaS 供应商持续改进的主要领域。
有关冷启动的更多详细信息,请参阅我关于该主题的文章。
API 网关
我们之前提到的无服务器的一个方面是“API 网关”。API 网关是一个 HTTP 服务器,其中路由和端点在配置中定义,每个路由都与一个资源相关联,以处理该路由。在无服务器架构中,此类处理程序通常是 FaaS 函数。
当 API 网关收到请求时,它会找到与请求匹配的路由配置,并且在 FaaS 支持的路由的情况下,会使用原始请求的表示形式调用相关的 FaaS 函数。通常,API 网关允许将 HTTP 请求参数映射到 FaaS 函数的更简洁的输入,或者允许将整个 HTTP 请求传递,通常以 JSON 对象的形式。FaaS 函数将执行其逻辑并将结果返回给 API 网关,API 网关反过来将此结果转换为 HTTP 响应,并将其传递回原始调用者。
Amazon Web Services 有自己的 API 网关(有点令人困惑地命名为“API Gateway”),其他供应商也提供类似的功能。Amazon 的 API Gateway 本身就是一个 BaaS(是的,BaaS!)服务,因为它是一个您配置的外部服务,但您不需要自己运行或配置它。
除了纯粹路由请求之外,API 网关还可以执行身份验证、输入验证、响应代码映射等等。(如果你在考虑这是否真的是个好主意时,你的蜘蛛感应在发作,那就先别急!我们稍后会进一步考虑这个问题。)
使用带有 FaaS 函数的 API 网关的一个用例是在无服务器的方式下创建 HTTP 前端的微服务,并利用 FaaS 函数带来的所有扩展、管理和其他优势。
当我第一次写这篇文章的时候,至少亚马逊的 API 网关的工具还很不成熟。从那以后,这些工具已经有了很大的改进。像 AWS API 网关这样的组件还没有完全“主流化”,但希望它们比以前更容易使用,并且会不断改进。
工具
上面关于工具成熟度的评论也适用于无服务器 FaaS。2016 年的时候情况很糟糕;到了 2018 年,我们已经看到了显著的改进,并且我们预计工具会变得更好。
在 FaaS 世界中,有两个值得一提的“开发者 UX”的例子。首先是 Auth0 Webtask,它在工具中非常重视开发者 UX。其次是微软,他们的 Azure Functions 产品。微软一直将 Visual Studio 及其紧密的反馈循环放在其开发者产品的首位,Azure Functions 也不例外。它提供的功能,可以在给定来自云触发的事件的输入的情况下,在本地调试函数,这非常特殊。
仍然需要显著改进的一个领域是监控。我将在后面讨论这个问题。
开源
到目前为止,我主要讨论的是专有供应商产品和工具。大多数无服务器应用程序都使用这些服务,但在这个世界里也有开源项目。
无服务器中最常见的开源用途是用于 FaaS 工具和框架,尤其是流行的 Serverless Framework,它旨在使使用 AWS API Gateway 和 Lambda 比使用 AWS 提供的工具更容易。它还提供了一定程度的跨供应商工具抽象,一些用户发现它很有价值。类似的工具示例包括 Claudia 和 Zappa。另一个例子是 Apex,它特别有趣,因为它允许你使用除亚马逊直接支持的语言之外的其他语言开发 Lambda 函数。
不过,大型供应商自己并没有落后于开源工具的潮流。AWS 自己的部署工具 SAM——Serverless Application Model——也是 开源的。
专有 FaaS 的主要优势之一是不必担心底层计算基础设施(机器、VM,甚至容器)。但如果你想关心这些东西呢?也许你有一些云供应商无法满足的安全需求,或者你已经购买了一些服务器机架,不想扔掉。开源可以在这些场景中提供帮助,让你运行自己的“有服务器”的 FaaS 平台吗?
可以,而且在这个领域已经有很多活动。开源 FaaS 的早期领导者之一是 IBM(使用 OpenWhisk,现在是 Apache 项目),令人惊讶的是——至少对我来说——微软,它开源了其 Azure Functions 平台的大部分内容。许多其他自托管 FaaS 实现都使用底层容器平台,通常是 Kubernetes,这在很多方面都很有意义。在这个领域,值得探索的项目包括 Galactic Fog、Fission 和 OpenFaaS。这是一个庞大、快速发展的领域,我建议看看 Cloud Native Computing Federation (CNCF) Serverless Working Group 所做的工作来跟踪它。
无服务器不是什么?
到目前为止,在这篇文章中,我将无服务器描述为两个概念的结合:后端即服务和函数即服务。我还深入研究了后者的功能。为了更准确地了解我认为无服务器服务的关键属性(以及为什么我认为即使像 S3 这样的更老的服务也是无服务器的),我建议你阅读我的另一篇文章:Defining Serverless。
在我们开始研究非常重要的优势和劣势领域之前,我想再花一点时间来定义一下。让我们定义一下无服务器不是什么。
与 PaaS 的比较
鉴于无服务器 FaaS 函数与 十二要素应用程序 非常相似,它们是否只是另一种形式的 "平台即服务" (PaaS),比如 Heroku?为了简短地回答这个问题,我引用 Adrian Cockcroft 的话:
如果你的 PaaS 可以高效地在 20 毫秒内启动运行半秒的实例,那么就称之为无服务器。
换句话说,大多数 PaaS 应用程序并不适合在响应事件时启动和关闭整个应用程序,而 FaaS 平台恰恰做到了这一点。
如果我是一个优秀的十二要素应用程序开发者,这并不一定会影响我的应用程序的编程和架构方式,但它确实会对我的应用程序的运行方式产生重大影响。既然我们都是精通 DevOps 的工程师,我们不仅要考虑开发,还要考虑运维,对吧?
FaaS 和 PaaS 之间的关键运维差异是 *扩展*。通常情况下,使用 PaaS,你仍然需要考虑如何扩展——例如,使用 Heroku,你想运行多少个 Dynos?使用 FaaS 应用程序,这完全是透明的。即使你将 PaaS 应用程序设置为自动扩展,你也不会将它扩展到单个请求的级别(除非你的流量模式非常特殊),因此 FaaS 应用程序在成本方面效率更高。
鉴于这种优势,你为什么还要使用 PaaS 呢?有很多原因,但工具可能是最大的原因。有些人还使用像 Cloud Foundry 这样的 PaaS 平台,在混合的公有云和私有云中提供通用的开发体验;在撰写本文时,还没有像它一样成熟的 FaaS 等效物。
与容器的比较
使用无服务器 FaaS 的原因之一是避免在操作系统级别管理应用程序进程。像 Heroku 这样的 PaaS 服务也提供了这种功能,我在上面描述了 PaaS 与无服务器 FaaS 的区别。另一种流行的进程抽象是容器,Docker 是这种技术的典型代表。容器托管系统,如 Mesos 和 Kubernetes,它们将单个应用程序与操作系统级别的部署隔离开来,越来越受欢迎。在这条道路上,我们看到了像 Amazon ECS 和 EKS 这样的云托管容器平台,以及 Google Container Engine,它们与无服务器 FaaS 一样,让团队完全避免管理自己的服务器主机。鉴于容器的势头,是否仍然值得考虑无服务器 FaaS 呢?
我为 PaaS 提出的论点主要仍然适用于容器——对于无服务器 FaaS,*扩展是自动管理的、透明的、细粒度的*,并且这与我之前提到的自动资源供应和分配有关。容器平台传统上仍然需要你管理集群的大小和形状。
我还要说,容器技术还没有成熟和稳定,尽管它越来越接近成熟。当然,这并不是说无服务器 FaaS 已经成熟,但选择你想要哪些粗糙的边缘仍然是当务之急。
还需要提一下,现在容器平台中也提供了自扩展容器集群。Kubernetes 在 "Horizontal Pod Autoscaling" 中内置了这个功能,像 AWS Fargate 这样的服务也承诺提供“无服务器容器”。
随着我们看到无服务器 FaaS 和托管容器之间的管理和扩展差距缩小,它们之间的选择可能最终会归结为风格和应用程序类型。例如,FaaS 可能被认为是事件驱动风格的更好选择,每个应用程序组件的事件类型很少,而容器可能被认为是同步请求驱动的组件的更好选择,这些组件有许多入口点。我预计在相当短的时间内,许多应用程序和团队将使用这两种架构方法,并且看到这种使用模式的出现将非常有趣。
#NoOps
无服务器并不意味着“没有运维”——尽管它可能意味着“没有系统管理员”,这取决于你深入无服务器兔子洞的程度。
“运维”不仅仅意味着服务器管理。它还意味着——至少——监控、部署、安全、网络、支持,以及通常一定程度的生产调试和系统扩展。这些问题在无服务器应用程序中仍然存在,你仍然需要一个策略来处理它们。在某些方面,无服务器世界中的运维更难,因为很多东西都是全新的。
系统管理员仍然存在——你只是将它外包给了无服务器。这并不一定是一件坏事(或好事)——我们外包了很多东西,它的好坏取决于你想要做什么。无论如何,在某个时刻,抽象层可能会泄漏,你需要知道在某个地方有人类系统管理员在支持你的应用程序。
Charity Majors 在第一个 Serverlessconf 上做了 一个关于这个主题的精彩演讲。(你也可以阅读她关于它的两篇博文:WTF is operations? 和 Operational Best Practices。)
存储过程即服务
我想知道无服务器服务是否会变成像存储过程一样的东西,一个好主意,但很快就会变成巨大的技术债务。
我看到的另一个主题是无服务器 FaaS 是“存储过程即服务”。我认为这是因为许多 FaaS 函数的例子(包括我在本文中使用的一些例子)都是与数据库紧密集成的小段代码。如果我们只能将 FaaS 用于此,我认为这个名字很有用,但因为它实际上只是 FaaS 功能的一个子集,我认为用这种方式思考 FaaS 并没有用。
话虽如此,但值得考虑的是,FaaS 是否会带来一些存储过程相同的问题,包括 Camille 在上面引用的推文中提到的技术债务问题。使用存储过程有很多经验教训值得在 FaaS 的背景下回顾,看看它们是否适用。请记住,存储过程
- 通常需要供应商特定的语言,或者至少需要供应商特定的框架/语言扩展
- 很难测试,因为它们需要在数据库的上下文中执行
- 很难进行版本控制或作为一等公民的应用程序来对待
虽然并非所有这些问题都一定会适用于所有存储过程的实现,但它们无疑是人们可能会遇到的问题。让我们看看它们是否可能适用于 FaaS
(1) 绝对不是我迄今为止见过的 FaaS 实现所关心的问题,所以我们可以立即从列表中删除它。
对于 (2),由于我们正在处理“仅仅是代码”,单元测试与任何其他代码一样容易。但是,集成测试是一个不同的(也是合法的)问题,我们将在后面讨论。
对于 (3),同样由于 FaaS 函数是“仅仅是代码”,版本控制是可以的。直到最近,应用程序打包也是一个问题,但我们开始看到这里出现了成熟度,比如亚马逊的 Serverless Application Model (SAM) 和我之前提到的 Serverless Framework。在 2018 年初,亚马逊甚至推出了一个“Serverless Application Repository” (SAR),为组织提供了一种方式来分发基于 AWS 无服务器服务的应用程序和应用程序组件。(在我的题为 Examining the AWS Serverless Application Repository 的文章中阅读有关 SAR 的更多信息。)
优势
到目前为止,我主要尝试坚持定义和解释无服务器架构的含义。现在,我将讨论这种设计和部署应用程序方式的一些优点和缺点。在做出使用无服务器的决定之前,您一定要认真考虑并权衡利弊。
让我们从彩虹和独角兽的国度开始,看看无服务器的优势。
降低运营成本
无服务器,简单来说,是一种外包解决方案。它允许您付费给其他人来管理服务器、数据库,甚至您可能自己管理的应用程序逻辑。由于您使用的是许多其他人也会使用的预定义服务,因此我们看到了规模经济效应:您为管理的数据库支付的费用更低,因为一家供应商正在运行数千个非常相似的数据库。
降低的成本对您来说是两个方面的总和。第一个是纯粹来自与其他人共享基础设施(例如,硬件、网络)的基础设施成本收益。第二个是劳动力成本收益:与您自己开发和托管的等效系统相比,您将能够在无服务器系统上花费更少的时间。
然而,这种优势与您从基础设施即服务 (IaaS) 或平台即服务 (PaaS) 中获得的优势并没有太大区别。但是,我们可以通过两种关键方式扩展这种优势,每种方式对应无服务器 BaaS 和 FaaS 中的一种。
BaaS:降低开发成本
IaaS 和 PaaS 基于服务器和操作系统管理可以商品化的前提。另一方面,无服务器后端即服务是整个应用程序组件商品化的结果。
身份验证就是一个很好的例子。许多应用程序编写自己的身份验证功能,其中通常包括注册、登录、密码管理以及与其他身份验证提供商的集成。总的来说,这种逻辑在大多数应用程序中非常相似,而像Auth0这样的服务已经创建出来,允许我们将现成的身份验证功能集成到我们的应用程序中,而无需我们自己开发。
在同一主题下是 BaaS 数据库,例如Firebase 的数据库服务。一些移动应用程序团队发现,让客户端直接与服务器端数据库通信是有意义的。BaaS 数据库消除了大部分数据库管理开销,并且通常提供机制来执行针对不同类型用户的适当授权,以符合无服务器应用程序的预期模式。
根据您的背景,这些想法可能会让您感到不安(可能是由于我们在缺点部分将要讨论的原因),但不可否认的是,许多成功的公司能够以几乎没有自己的服务器端代码来生产引人注目的产品。Joe Emison 在第一届无服务器大会上举了几个例子。
FaaS:扩展成本
无服务器 FaaS 的乐趣之一是——正如我在本文前面提到的那样——“水平扩展是完全自动的、弹性的,并且由提供商管理。” 这有很多好处,但在基本的基础设施方面,最大的好处是您只为所需的计算付费,在 AWS Lambda 的情况下,精确到 100 毫秒。根据您的流量规模和形状,这可能对您来说是一个巨大的经济优势。
示例:偶尔请求
假设您正在运行一个服务器应用程序,该应用程序每分钟只处理一个请求,每个请求的处理时间为 50 毫秒,您在一个小时内的平均 CPU 使用率为 0.1%。如果此应用程序部署到其自己的专用主机,那么这将是极其低效的。一千个其他类似的应用程序都可以共享那台机器。
无服务器 FaaS 捕捉到了这种低效,并将这种优势以降低成本的形式传递给您。使用上面的示例应用程序,您将只为每分钟 100 毫秒的计算付费,这占总时间的 0.15%。
这具有以下连锁效应
- 对于具有非常小负载要求的潜在微服务,它支持按逻辑/领域分解组件,即使这种细粒度的操作成本可能在其他情况下是禁止的。
- 这种成本优势是一个伟大的民主化者。如果公司或团队想要尝试一些新事物,他们使用 FaaS 来满足其计算需求时,与“试水”相关的运营成本非常低。事实上,如果您的总工作量相对较小(但并非完全微不足道),您可能根本不需要为任何计算付费,因为一些 FaaS 供应商提供了“免费层”。
示例:不稳定的流量
让我们看另一个例子。假设您的流量配置文件非常尖峰——也许您的基线流量是每秒 20 个请求,但每五分钟您会收到每秒 200 个请求(是通常数量的 10 倍),持续 10 秒。为了便于说明,我们还假设您的基线性能最大限度地利用了您首选的主机服务器类型,并且您不想在流量峰值阶段降低响应时间。您如何解决这个问题?
在传统环境中,您可能需要将硬件总数增加 10 倍,以处理峰值,即使峰值总持续时间占机器总运行时间的不到 4%。自动扩展在这里可能不是一个好的选择,因为服务器新实例启动需要很长时间——到您的新实例启动时,峰值阶段已经结束。
然而,使用无服务器 FaaS,这将不再是一个问题。您实际上与流量配置文件一致时没有任何不同,并且您只为峰值阶段的额外计算能力付费。
显然,我在这里故意选择了无服务器 FaaS 可以节省大量成本的示例,但重点是要表明,从扩展的角度来看,除非您的流量形状非常稳定,并且始终如一地利用服务器主机的全部容量,否则您可能会节省使用 FaaS 的成本。
关于以上内容的一个注意事项:如果您的流量是均匀的,并且会始终如一地充分利用正在运行的服务器,那么您可能看不到这种成本优势,并且您实际上可能会花费更多使用 FaaS。您应该做一些数学运算,并将当前提供商的成本与全天候运行服务器的等效成本进行比较,以查看成本是否可以接受。
有关 FaaS 成本效益的更多详细信息,我推荐 Gojko Adzic 和 Robert Chatley 撰写的论文“无服务器计算:经济和架构影响”。
优化是部分成本节省的根本
关于 FaaS 成本,还有一个有趣的方面需要提及:您对代码进行的任何性能优化不仅会提高应用程序的速度,而且会与运营成本的降低直接且立即相关,这取决于供应商计费方案的粒度。例如,假设一个应用程序最初需要一秒钟来处理一个事件。如果通过代码优化,将其缩短到 200 毫秒,它(在 AWS Lambda 上)将立即看到计算成本节省 80%,而无需进行任何基础设施更改。
更轻松的运营管理
下一部分带有一个巨大的星号——无服务器的一些运营方面仍然很困难,但现在我们仍然坚持我们的独角兽和彩虹朋友……
在无服务器 BaaS 一侧,为什么运营管理比其他架构更简单是相当明显的:支持更少的组件意味着更少的工作。
在 FaaS 一侧,有很多方面在起作用,我将深入探讨其中几个方面。
FaaS 扩展优势超越基础设施成本
虽然扩展在我们上一节中还记忆犹新,但值得注意的是,FaaS 的扩展功能不仅降低了计算成本,还降低了运营管理,因为扩展是自动的。
在最好的情况下,如果您的扩展过程是手动的——例如,一个人需要显式地向服务器数组添加和删除实例——使用 FaaS,您可以高兴地忘记它,让您的 FaaS 供应商为您扩展应用程序。
即使您已经开始在非 FaaS 架构中使用自动扩展,这仍然需要设置和维护。使用 FaaS,这项工作不再需要。
同样,由于扩展是由提供商在每个请求/事件上执行的,您不再需要考虑在耗尽内存或看到太多性能下降之前可以处理多少并发请求的问题——至少在您的 FaaS 托管组件内是这样。下游数据库和非 FaaS 组件将不得不根据其负载可能显着增加的情况进行重新考虑。
降低打包和部署复杂性
与部署整个服务器相比,打包和部署 FaaS 函数很简单。您所做的就是将所有代码打包到一个 zip 文件中,然后上传。没有 Puppet/Chef,没有启动/停止 shell 脚本,没有关于在一台机器上部署一个或多个容器的决定。如果您刚开始,您甚至不需要打包任何东西——您可能能够在供应商控制台中编写您的代码(显然,不建议在生产代码中这样做!)。
这个过程描述起来并不需要很长时间,但对于一些团队来说,这种优势可能非常巨大:完全无服务器解决方案不需要任何系统管理。
PaaS 解决方案具有类似的部署优势,但正如我们之前所看到的,在比较 PaaS 与 FaaS 时,扩展优势是 FaaS 独有的。
上市时间和持续实验
更轻松的运营管理是我们作为工程师所理解的优势,但这对我们的企业意味着什么?
显而易见的原因是成本:在运营上花费的时间更少,意味着需要的人员更少,正如我已经描述的那样。但在我看来,一个更重要的原因是上市时间。随着我们的团队和产品越来越倾向于精益和敏捷流程,我们希望不断尝试新事物并快速更新现有系统。虽然在持续交付的背景下,简单的重新部署允许稳定项目的快速迭代,但拥有良好的新想法到初始部署能力使我们能够以低摩擦和最低成本尝试新的实验。
FaaS 的新想法到初始部署的故事通常非常出色,特别是对于由供应商生态系统中成熟定义的事件触发的简单函数而言。例如,假设您的组织已经在使用AWS Kinesis,一个类似 Kafka 的消息系统,用于通过您的基础设施广播各种类型的实时事件。使用 AWS Lambda,您可以在几分钟内开发和部署一个针对该 Kinesis 流的新生产事件监听器——您可以在一天内尝试几个不同的实验!
虽然成本效益是无服务器最容易表达的改进,但正是这种提前期缩短让我最兴奋。它可以实现一种持续实验的产品开发思维,这对于我们如何在公司中交付软件来说是一场真正的革命。
“更环保”的计算?
在过去几十年中,世界上数据中心的数量和规模大幅增加。除了建造这些中心所需的物理资源外,相关的能源需求非常大,以至于苹果、谷歌等公司都在谈论将一些数据中心托管在可再生能源附近,以减少这些中心原本需要的化石燃料燃烧的影响。
处于闲置状态但已通电的服务器消耗了大量的这种能源——它们是我们需要如此之多、如此之大的数据中心的很大一部分原因
商业和企业数据中心中的典型服务器在一年中平均交付其最大计算能力的 5% 到 15%。
-- 福布斯
这极其低效,并造成了巨大的环境影响。
一方面,云基础设施可能已经帮助减少了这种影响,因为公司可以按需“购买”更多服务器,只有在绝对需要时才购买,而不是提前很长时间预置所有可能需要的服务器。然而,也可以争论说,如果许多服务器被闲置,而没有进行充分的容量管理,那么轻松预置服务器可能会使情况变得更糟。
无论我们使用的是自托管服务器、IaaS 还是 PaaS 基础设施解决方案,我们仍然需要手动做出关于应用程序容量的决策,而这些决策通常会持续数月甚至数年。通常情况下,我们会谨慎地管理容量,这是正确的做法,因此我们会过度配置,从而导致前面提到的低效率。采用无服务器方法,**我们不再需要自己做出这些容量决策**——我们让无服务器供应商根据我们的实际需求实时提供足够的计算能力。然后,供应商可以在其所有客户的总量上做出自己的容量决策。
这种差异应该会导致数据中心资源的利用效率大幅提高,从而与传统的容量管理方法相比,减少对环境的影响。
缺点
所以,亲爱的读者,我希望你在彩虹、独角兽和所有闪闪发光的东西的国度里玩得开心,因为我们即将被现实的冷水浇醒。
无服务器架构确实有很多优点,但它们也伴随着重大的权衡。其中一些权衡是概念本身固有的,无法通过进步完全解决,并且始终需要考虑。其他权衡则与当前的实现方式有关,随着时间的推移,我们可以预期这些问题会得到解决。
固有缺点
供应商控制
对于任何外包策略,你都会将系统的一部分控制权交给第三方供应商。这种缺乏控制可能会表现为系统停机、意外限制、成本变化、功能丢失、强制 API 升级等等。我之前提到的 Charity Majors 在这篇文章的权衡部分更详细地解释了这个问题:https://charity.wtf/2016/05/31/operational-best-practices-serverless/
[供应商服务],如果它足够聪明,会对你如何使用它施加严格的限制,这样他们更有可能实现其可靠性目标。当用户拥有灵活性选择时,就会造成混乱和不可靠。如果平台必须在你的幸福和数千其他客户的幸福之间做出选择,他们每次都会选择多数人而不是少数人——这是他们应该做的。
多租户问题
多租户是指在同一台机器上,甚至在同一个托管应用程序中,为多个不同的客户(或租户)运行多个软件实例的情况。这是一种实现我们之前提到的规模经济效益的策略。服务供应商尽其所能让客户感觉他们都是唯一使用其系统的人,通常优秀的服务供应商在这方面做得很好。但没有人是完美的,有时多租户解决方案可能会出现安全问题(一个客户能够看到另一个客户的数据)、健壮性问题(一个客户的软件中的错误导致另一个客户的软件出现故障)以及性能问题(一个高负载客户导致另一个客户变慢)。
这些问题并非无服务器系统独有,它们存在于许多其他使用多租户的服务产品中。AWS Lambda 现在已经足够成熟,我们预计不会再遇到这种问题,但对于任何不太成熟的服务,无论它来自 AWS 还是其他供应商,你都应该注意这些问题。
供应商锁定
你从一个供应商那里使用的无服务器功能很可能在另一个供应商那里会有不同的实现方式。如果你想更换供应商,你几乎肯定需要更新你的运维工具(部署、监控等),你可能还需要更改代码(例如,满足不同的 FaaS 接口),如果你想更换供应商,你几乎肯定需要更新你的运维工具(部署、监控等),你可能还需要更改代码(例如,满足不同的 FaaS 接口),如果你想更换供应商,你几乎肯定需要更新你的运维工具(部署、监控等),你可能还需要更改代码(例如,满足不同的 FaaS 接口),并且你甚至可能需要更改你的设计或架构,如果竞争供应商的实现方式存在差异。
即使你成功地轻松迁移了生态系统的一部分,你可能也会受到另一个架构组件的更大影响。例如,假设你正在使用 AWS Lambda 来响应 AWS Kinesis 消息总线上的事件。AWS Lambda、Google Cloud Functions 和 Microsoft Azure Functions 之间的差异可能相对较小,但你仍然无法将后两种供应商的实现直接连接到你的 AWS Kinesis 流。这意味着**将代码从一个解决方案迁移到另一个解决方案是不可能的,除非也迁移基础设施的其他部分**。
很多人对这个想法感到害怕——知道如果你今天选择的云供应商明天需要改变,你将有很多工作要做,这可不是什么好感觉。正因为如此,有些人采用“多云”方法,以一种与实际使用的云供应商无关的方式开发和运行应用程序。这通常比单云方法更昂贵——因此,虽然供应商锁定是一个合理的担忧,但我仍然建议选择一个你满意的供应商,并尽可能利用他们的功能。我在这篇文章中更详细地解释了原因:https://blog.symphonia.io/on-serverless-multi-cloud-and-vendor-lock-in-da930b3993f.
安全问题
采用无服务器方法会让你面临大量安全问题。以下只是一些需要考虑的非常简短的例子——一定要探索其他可能影响你的因素。
- 你使用的每个无服务器供应商都会增加你的生态系统所采用的不同安全实现的数量。这会增加你的恶意攻击面,并提高攻击成功的可能性。
- 如果你直接从移动平台使用 BaaS 数据库,你将失去传统应用程序中服务器端应用程序提供的保护屏障。虽然这不是一个致命问题,但它确实需要在设计和开发应用程序时格外小心。
- 随着你的组织采用 FaaS,你可能会在公司范围内经历 FaaS 函数的寒武纪大爆发。每个函数都提供了另一个出现问题的机会。例如,在 AWS Lambda 中,每个 Lambda 函数通常都与一个配置的 IAM 策略 相关联,而这些策略很容易配置错误。这不是一个简单的主题,也不能忽视它。IAM 管理需要仔细考虑,至少在生产 AWS 账户中是这样。
跨客户端平台的逻辑重复
在“完整”BaaS 架构中,不会在服务器端编写任何自定义逻辑——所有逻辑都在客户端。这可能适合你的第一个客户端平台,但一旦你需要下一个平台,你就需要重复实现一部分逻辑——在更传统的架构中,你不需要这种重复。例如,如果在这种系统中使用 BaaS 数据库,所有客户端应用程序(可能是 Web、原生 iOS 和原生 Android)现在都需要能够与你的供应商数据库通信,并且需要了解如何将数据库模式映射到应用程序逻辑。
此外,如果你想在任何时候迁移到新的数据库,你需要在所有不同的客户端上复制这种编码/协调更改。
失去服务器优化
在完整的 BaaS 架构中,没有机会优化服务器设计以提高客户端性能。‘前端后端’ 模式存在于服务器中,用于抽象整个系统的一些底层方面,部分原因是为了让客户端能够更快地执行操作,并在移动应用程序的情况下使用更少的电池电量。这种模式不适用于完整的 BaaS。
这种缺点和前面提到的缺点都存在于所有自定义逻辑都在客户端,并且唯一的后端服务是供应商提供的完整的 BaaS 架构中。这两种缺点的缓解措施是采用 FaaS 或其他类型的轻量级服务器端模式,将某些逻辑迁移到服务器。
无服务器 FaaS 没有服务器内状态
在介绍了几个特定于 BaaS 的缺点之后,让我们谈谈 FaaS。我之前说过
FaaS 函数在本地状态方面存在重大限制。不要假设一个函数的一次调用中的状态将可用于同一函数的另一次调用。
这种假设的原因是,在 FaaS 中,我们通常无法控制函数的宿主容器何时启动和停止。
我还说过,本地状态的替代方案是遵循十二要素应用程序的第 6 个因素,即接受这种限制
十二要素进程是无状态的,并且是无共享的。任何需要持久化的数据都必须存储在有状态的后端服务中,通常是数据库。
-- 十二要素应用程序
Heroku 建议这种思维方式,但你可以在他们的 PaaS 上运行时弯曲规则,因为你可以控制 Heroku Dynos 何时启动和停止。在 FaaS 中,没有弯曲规则的余地。
那么,如果不能将状态保存在内存中,FaaS 的状态应该放在哪里?上面的引用提到了使用数据库,在许多情况下,快速 NoSQL 数据库、进程外缓存(例如 Redis)或外部对象/文件存储(例如 S3)将是你的选择。但这些都比内存中或机器上的持久化速度慢得多。你需要考虑你的应用程序是否适合这种方式。
在这方面,另一个问题是内存缓存。许多从外部存储的大型数据集读取数据的应用程序会将该数据集的一部分保存在内存缓存中。你可能正在从数据库中的“参考数据”表中读取数据,并使用类似 Ehcache 的东西。或者,你可能正在从一个指定缓存头的 HTTP 服务中读取数据,在这种情况下,你的内存中 HTTP 客户端可以提供本地缓存。
FaaS 确实允许使用一些本地缓存,如果你的函数使用频率足够高,这可能会有用。例如,在 AWS Lambda 中,我们通常期望一个函数实例只要每隔几分钟使用一次,就会保留几个小时。这意味着我们可以使用 Lambda 提供给我们的(可配置的)3 GB RAM 或 512 MB 本地“/tmp”空间。对于某些缓存,这可能就足够了。否则,你需要不再假设进程内缓存,并且你需要使用低延迟的外部缓存,如 Redis 或 Memcached。但是,这需要额外的工作,并且根据你的用例,速度可能慢得令人望而却步。
实施缺点
前面描述的缺点很可能永远存在于无服务器中。我们将在缓解解决方案方面看到改进,但它们始终会存在。
然而,剩下的缺点纯粹归结于目前的现状。如果供应商或英雄社区愿意投入,这些缺点都可以消除。事实上,自从这篇文章的第一个版本发布以来,这个列表已经缩短了。
配置
当我写这篇文章的第一个版本时,AWS 在 Lambda 函数配置方面提供的功能很少。我很高兴地说,这个问题现在已经解决了,但如果你使用的是不太成熟的平台,这仍然是值得检查的事情。
拒绝服务攻击自己
以下是一个说明为什么在处理 FaaS 时,买者自负是一个关键短语的例子。AWS Lambda 限制了在给定时间可以运行的 Lambda 函数的并发执行次数。假设这个限制是一千次;这意味着在任何时候,你都允许执行一千个函数实例。如果你需要超过这个限制,你可能会开始出现异常、排队或整体速度变慢。
这里的问题是,这个限制是针对整个 AWS 账户的。一些组织使用同一个 AWS 账户来进行生产和测试。这意味着,如果你的组织中的某个人在某个地方执行了一种新的负载测试,并开始尝试执行一千个并发 Lambda 函数,你将无意中对你的生产应用程序进行 DoS 攻击。糟糕。
即使你使用不同的 AWS 账户进行生产和开发,一个过载的生产 Lambda(例如,处理来自客户的批量上传)也可能导致你单独的实时 Lambda 支持的生产 API 变得无响应。
Amazon 提供了一些保护措施,通过**保留并发**。保留并发允许你限制 Lambda 函数的并发性,以防止它破坏你的账户中的其他函数,同时确保无论账户中的其他函数在做什么,总有可用容量。但是,保留并发在账户中默认情况下是关闭的,需要仔细管理。
执行时长
在本文的前面,我提到过,如果 AWS Lambda 函数运行时间超过五分钟,就会被中止。这种情况已经持续了几年,AWS 也没有表现出改变的迹象。
启动延迟
我之前谈到了冷启动,并提到了我关于这个主题的文章。AWS 随着时间的推移在这方面有所改进,但仍然存在重大问题,特别是对于偶尔触发的 JVM 实现的函数和/或需要访问 VPC 资源的函数。预计这方面将继续改进。
好了,关于 AWS Lambda 的批评就到这里了。我相信其他供应商也有一些见不得人的秘密。
测试
对无服务器应用程序进行单元测试相当简单,原因是我之前提到的:你编写的任何代码都是“普通代码”,而且大多数情况下,你不需要使用很多自定义库或实现接口。
另一方面,对无服务器应用程序进行集成测试很困难。在 BaaS 世界中,你故意依赖外部提供的系统,而不是例如你自己的数据库。那么你的集成测试是否也应该使用外部系统?如果是,那么这些系统对测试场景的适应性如何?你能轻松地创建和销毁状态吗?你的供应商能为你提供不同的负载测试计费策略吗?
如果你想为集成测试模拟这些外部系统,供应商是否提供本地模拟?如果是,模拟的保真度如何?如果供应商不提供模拟,你将如何自己实现一个?
FaaS 领域也存在类似的问题,尽管这方面有所改进。现在可以为 Lambda 和 Microsoft Azure 在本地运行 FaaS 函数。但是,没有本地环境可以完全模拟云环境;仅仅依靠本地 FaaS 环境不是我推荐的策略。事实上,我更进一步建议,你用于运行自动化集成测试的规范环境(至少作为部署管道的一部分)应该是云,并且你应该主要使用本地测试环境进行交互式开发和调试。这些本地测试环境不断改进 - 例如,SAM CLI 为开发 Lambda 支持的 HTTP API 应用程序提供了快速反馈。
还记得我在几节之前提到的在云中运行集成测试时的跨帐户执行限制吗?你可能至少想要将这些测试与你的生产云帐户隔离开来,并且可能使用比这更细粒度的帐户。
将集成测试视为一项重要任务的部分原因是,我们与无服务器 FaaS 集成的单元(即每个函数)比其他架构小得多,因此我们比其他架构风格更依赖集成测试。
依赖基于云的测试环境而不是在我的笔记本电脑上本地运行所有内容对我来说是一个不小的冲击。但时代在变,我们从云中获得的能力与谷歌等公司的工程师十多年来拥有的能力类似。亚马逊现在甚至允许你在云中运行你的 IDE。我还没有完全迈出这一步,但它可能即将到来。
调试
使用 FaaS 进行调试是一个有趣的领域。这方面已经取得了进展,主要与在本地运行 FaaS 函数有关,与上面讨论的测试更新一致。正如我之前提到的,微软为本地运行但由远程事件触发的函数提供了出色的调试支持。亚马逊提供了类似的功能,但尚未由生产事件触发。
调试实际运行在生产云环境中的函数是另一回事。Lambda 至少还没有支持这种功能,尽管看到这种功能会很棒。
部署、打包和版本控制
这是一个正在积极改进的领域。AWS 在改进这方面取得了巨大进步,我将在稍后的“无服务器的未来”部分中进一步讨论它。
发现
“发现”是微服务领域经常讨论的话题:它是关于一个服务如何调用另一个服务的正确版本的问题。在无服务器世界中,关于发现的讨论很少。最初这让我很担心,但现在我不那么担心了。无服务器的许多用法本质上是事件驱动的,在这种情况下,事件的消费者通常会在一定程度上进行自我注册。对于 FaaS 的面向 API 的用法,我们通常在 API 网关后面使用它们。在这种情况下,我们在 API 网关前面使用 DNS,并在网关后面使用自动部署/流量转移。我们甚至可以在 API 网关前面使用更多层(例如,使用AWS CloudFront)来支持跨区域弹性。
我把这个想法放在“限制”中,因为我认为它还没有得到证实,但最终它可能没问题。
监控和可观察性
监控对于 FaaS 来说是一个棘手的领域,因为容器的短暂性。大多数云供应商都提供一定程度的监控支持,我们也看到了传统监控供应商在这方面的许多第三方工作。尽管如此,他们最终能做些什么,以及你能做些什么,都取决于供应商提供的基本数据。在某些情况下这可能没问题,但至少对于 AWS Lambda 来说,它非常基础。我们真正需要的是开放 API 和第三方服务提供更多帮助的能力。
API 网关定义和过于雄心勃勃的 API 网关
Thoughtworks 在其技术雷达出版物中讨论了过于雄心勃勃的 API 网关。虽然链接指的是一般的 API 网关(例如,用于那些面向传统部署的微服务的 API 网关),但它绝对适用于将 API 网关用作 HTTP 前端到 FaaS 函数。问题是,API 网关提供了在自己的配置/定义域中执行许多特定于应用程序的逻辑的机会。这种逻辑通常难以测试、版本控制,有时难以定义。通常,将这种逻辑保留在程序代码中(与应用程序的其余部分一样)要好得多。
不过,这里确实存在一种张力。如果我们将 API 网关视为 BaaS,那么考虑它提供给我们的所有选项是否有价值,以便节省我们的工作?如果我们按请求付费使用 API 网关,而不是按 CPU 利用率付费,那么最大限度地利用 API 网关的功能是否更具成本效益?
我的建议是谨慎使用增强的 API 网关功能,只有在它真的能从长远来看节省你的工作时才使用,包括它如何部署、监控和测试。绝对不要使用无法在源代码控制的配置文件或部署脚本中表达的 API 网关功能。
关于定义的难度,亚马逊的 API 网关过去常常迫使你创建一些棘手的配置来将 HTTP 请求和响应映射到/从 Lambda 函数。使用Lambda 代理集成,其中大部分已经变得更加简单,但你仍然需要了解一些偶尔会出现的棘手细微差别。使用开源项目(如Serverless Framework 和Claudia.js)或亚马逊的Serverless Application Model,这些元素本身变得更容易。
延迟操作
我之前提到过,无服务器不是“无运维”——从监控、架构扩展、安全和网络的角度来看,仍然有很多事情要做。但是,当你刚开始的时候很容易忽略运维(“看,妈妈,没有操作系统!”)。这里的危险是陷入一种虚假的安全感。也许你的应用程序已经启动并运行,但它意外地出现在 Hacker News 上,突然你的流量增加了 10 倍,哎呀!你意外地遭受了拒绝服务攻击,并且不知道如何处理它。
这里的解决方法是教育。使用无服务器系统的团队需要尽早考虑运维活动,而供应商和社区有责任提供教学,帮助他们理解这意味着什么。诸如预先负载测试和混沌工程之类的领域也将帮助团队自我学习。
无服务器的未来
我们即将结束这次关于无服务器架构世界的旅程。最后,我将讨论一些我认为无服务器世界在未来几个月和几年内可能会发展的一些领域。
缓解缺点
无服务器仍然是一个相当新的世界。因此,前面关于缺点的部分很长,我甚至没有涵盖所有我能涵盖的内容。无服务器最重要的发展将是减轻固有的缺点,消除或至少改进实现缺点。
工具
工具仍然是无服务器的一个问题,这是因为许多技术和方法都是新的。在过去两年中,部署/应用程序捆绑和配置都有所改进,Serverless 框架和亚马逊的 Serverless Application Model 带领了这一趋势。但是,“前 10 分钟”的体验仍然不像它本可以的那样普遍令人惊叹,尽管亚马逊和谷歌可以向微软和 Auth0 学习更多灵感。
我一直在兴奋地看到云供应商积极解决的一个领域是更高级别的发布方法。在传统系统中,团队通常需要编写自己的流程来处理“流量转移”的想法,例如蓝绿部署和金丝雀发布。考虑到这一点,亚马逊支持自动流量转移,适用于 Lambda 和 API 网关。这些概念在无服务器系统中甚至更有用,因为如此多的单独部署的组件构成了一个系统——一次性原子发布 100 个 Lambda 函数是不可能的。事实上,Nat Pryce 向我描述了一个“调音台”方法的想法,在这种方法中,我们可以逐渐将组组件纳入和移出流量流。
分布式监控可能是最需要改进的领域。我们已经看到了亚马逊的X-Ray 和各种第三方产品在这方面的早期工作,但这绝对不是一个已解决的问题。
远程调试也是我想看到更广泛应用的东西。Microsoft Azure Functions 支持这一点,但 Lambda 不支持。能够在远程运行的函数中设置断点是一个非常强大的功能。
最后,我希望看到“元操作”工具的改进——如何更有效地管理数百或数千个 FaaS 函数、配置的服务等等。例如,组织需要能够看到某些服务实例何时不再使用(至少出于安全目的),他们需要更好地对跨服务成本进行分组和可视化(特别是对于具有成本责任的自治团队),等等。
状态管理
对于许多应用程序来说,FaaS 缺乏持久性的服务器内状态是可以的,但对于许多其他应用程序来说,这是一个障碍——无论是对于大型缓存集还是对会话状态的快速访问。
对于高吞吐量应用程序,一种可能的解决方法是供应商让函数实例在事件之间保持更长时间的活动状态,并让常规的进程内缓存方法发挥作用。这不会在 100% 的时间内起作用,因为缓存不会为每个事件预热,但这与传统部署的应用程序使用自动扩展时已经存在的问题相同。
一个更好的解决方案可能是对进程外数据的非常低的延迟访问,例如能够以非常低的网络开销查询 Redis 数据库。考虑到亚马逊已经在其Elasticache 产品中提供了托管的 Redis 解决方案,并且他们已经允许使用放置组 相对地将 EC2(服务器)实例共置,这似乎并不算太难。
不过,我认为更有可能的是,我们将看到不同类型的混合(无服务器和非无服务器)应用程序架构被采用,以考虑外部状态约束。例如,对于低延迟应用程序,你可能会看到一种方法,即一个常规的、长时间运行的服务器处理初始请求,从其本地和外部状态收集处理该请求所需的所有上下文,然后将一个完全上下文化的请求传递给一个不需要从外部查找数据的 FaaS 函数池。
平台改进
目前,无服务器 FaaS 的某些缺点归结于平台的实现方式。执行时长、启动延迟和跨函数限制是三个明显的缺点。这些问题很可能通过新的解决方案得到解决,或者通过可能产生额外成本的变通方法解决。例如,我认为可以通过允许客户请求始终以低延迟提供两个 FaaS 函数实例来缓解启动延迟问题,客户需要为这种可用性付费。Microsoft Azure Functions 在 持久函数 和 App Service 计划托管函数 中包含了这种想法的元素。
当然,我们会看到平台改进,不仅仅是修复当前的缺陷,这些改进也将令人兴奋。
教育
许多与无服务器相关的特定于供应商的固有缺点正在通过教育得到缓解。所有使用此类平台的人都需要积极思考,将他们生态系统中的很大一部分托管在一个或多个应用程序供应商意味着什么。我们需要思考诸如“如果一个供应商不可用,我们是否要考虑来自不同供应商的并行解决方案?”和“应用程序如何在部分中断的情况下优雅地降级?”之类的问题。
另一个需要教育的领域是技术运营。许多团队现在拥有的系统管理员比以前少,无服务器将加速这种变化。但是系统管理员不仅仅配置 Unix 盒子和 Chef 脚本——他们通常是支持、网络、安全等方面的第一线人员。
在无服务器世界中,真正的 DevOps 文化 变得更加重要,因为那些其他非系统管理员活动仍然需要完成,而且通常是开发人员现在负责这些活动。这些活动可能对许多开发人员和技术负责人来说并不自然,因此教育和与运营人员的密切合作至关重要。
提高透明度,并从供应商那里获得更清晰的预期
最后,关于缓解措施:供应商必须在我们越来越依赖他们的平台来提供更多托管功能时,更加明确地说明我们对他们平台的期望。虽然迁移平台很困难,但并非不可能,不可靠的供应商将看到他们的客户将业务转移到其他地方。
模式的出现
我们对如何以及何时使用无服务器架构的理解仍处于起步阶段。现在,团队正在将各种想法抛向无服务器平台,看看哪些想法行得通。感谢先驱者!我们开始看到推荐实践模式的出现,这些知识只会不断增长。
我们看到的一些模式存在于应用程序架构中。例如,FaaS 函数可以有多大,才能避免变得笨拙?假设我们可以原子地部署一组 FaaS 函数,那么创建此类分组的好方法是什么?它们是否与我们目前将逻辑分组到微服务的方式密切相关,或者架构上的差异是否将我们推向了不同的方向?
无服务器应用程序架构中一个特别有趣的活跃讨论领域是它如何与事件思维交互。AWS Lambda 产品负责人 Ajay Nair 在 2017 年 发表了一篇精彩的演讲,这是 CNCF 无服务器工作组讨论的主要领域之一。
进一步扩展,在 FaaS 和传统的“始终在线”持久服务器组件之间创建混合架构的好方法是什么?将 BaaS 引入现有生态系统的好方法是什么?反过来,完全或大部分 BaaS 系统需要开始采用或使用更多自定义服务器端代码的预警信号是什么?
我们还看到更多使用模式的讨论。FaaS 的一个标准示例是媒体转换,例如,每当将大型媒体文件存储到 S3 存储桶时,就会自动运行一个进程,在另一个存储桶中创建较小的版本。但是,我们现在还看到无服务器在数据处理管道、高度可扩展的 Web API 以及作为操作中的通用“粘合”代码方面得到了广泛应用。其中一些模式可以作为通用组件实现,直接部署到组织中;我写过关于亚马逊的无服务器应用程序存储库,它包含了这种想法的早期形式。
最后,随着工具的改进,我们开始看到推荐的操作模式。我们如何为 FaaS、BaaS 和传统服务器的混合架构逻辑地聚合日志?我们如何最有效地调试 FaaS 函数?这些问题的答案——以及新出现的模式——很多来自云供应商本身,我预计该领域的活动将增长。
全球分布式架构
在我之前给出的宠物商店示例中,我们看到单个宠物商店服务器被分解成几个服务器端组件,以及一些逻辑,这些逻辑一直移动到客户端——但从根本上说,这仍然是一个专注于客户端或已知位置的远程服务的架构。
我们现在在无服务器世界中开始看到的是责任的模糊分布。一个例子是亚马逊的 Lambda@Edge 产品:一种在亚马逊的 CloudFront 内容交付网络中运行 Lambda 函数的方法。使用 Lambda@Edge,Lambda 函数现在在全球范围内分布——工程师的一次上传活动将意味着该函数将部署到 全球 100 多个数据中心。这不是我们习惯的设计,它带来了许多约束和功能。
此外,Lambda 函数可以在 设备上运行,机器学习模型可以在移动客户端上运行,在你意识到之前,“客户端”和“服务器端”的分裂似乎不再有意义。事实上,我们现在看到组件位置的频谱,从人类用户开始扩展。无服务器将变得无区域。
超越“FaaS 化”
到目前为止,我所见过的 FaaS 的大多数用法都是关于将现有代码和设计理念“FaaS 化”:将它们转换为一组无状态函数。这很强大,但我预计我们会开始看到更多抽象,甚至可能是语言,使用 FaaS 作为底层实现,让开发人员在不实际将应用程序视为一组离散函数的情况下,获得 FaaS 的好处。
例如,我不知道 Google 是否在它的 Dataflow 产品中使用了 FaaS 实现,但我可以想象有人会创建一个产品或开源项目来做类似的事情,并使用 FaaS 作为实现。这里的一个比较是类似于 Apache Spark。Spark 是一个用于大规模数据处理的工具,它提供了非常高级的抽象,可以使用 Amazon EMR 和 Hadoop 作为其底层平台。
测试
我认为在无服务器系统的集成和验收测试方面还有很多工作要做,但其中很多工作与以更传统方式开发的“云原生”微服务系统相同。
这里的一个激进想法是拥抱像 生产环境测试 和 监控驱动开发 这样的理念;一旦代码通过了基本的单元测试验证,就将其部署到一小部分流量中,看看它与之前的版本相比如何。这可以与我之前提到的流量切换工具相结合。这并不适用于所有情况,但对于许多团队来说,它可能是一个非常有效的工具。
可移植的实现
团队可以使用无服务器,同时减少对特定云供应商的依赖,方法有两种。
供应商实现上的抽象
Serverless Framework 主要存在于简化无服务器应用程序的操作任务,但也提供了一定程度的关于此类应用程序在哪里以及如何部署的中立性。例如,能够轻松地在 AWS API Gateway + Lambda 和 Auth0 webtask 之间切换,即使是现在,也取决于每个平台的操作能力,这将是一件很棒的事情。
这方面的一个棘手问题是在没有某种标准化的情况下对抽象的 FaaS 编码接口进行建模,但这正是 CNCF 无服务器工作组在 CloudEvents 上所做的工作。
一旦操作的复杂性显现出来,为多个平台提供部署抽象的价值就值得怀疑了。例如,在不同的云中,安全设置总是可能不同。
可部署的实现
建议我们在不使用第三方提供商的情况下使用无服务器技术,这听起来可能很奇怪,但请考虑以下想法
- 也许我们是一个大型技术组织,我们希望开始为我们所有的移动应用程序开发团队提供类似于 Firebase 的数据库体验,但我们希望使用我们现有的数据库架构作为后端。
- 我之前谈到过“有服务器”的 FaaS 平台——能够将 FaaS 风格的架构用于我们的一些项目,但由于合规性、法律等原因,必须在本地运行我们的应用程序。
在这两种情况下,在没有供应商托管的情况下,使用无服务器方法仍然有很多好处。这里有一个先例——考虑平台即服务 (PaaS)。最初流行的 PaaS 都是基于云的(例如,Heroku),但很快,人们就看到了在自己的系统上运行 PaaS 环境的好处——所谓的“私有”PaaS(例如,Cloud Foundry,正如我在本文前面提到的)。
我可以想象,就像私有 PaaS 实现一样,我们会看到 BaaS 和 FaaS 概念的开源和商业实现变得流行起来,尤其是那些与 Kubernetes 等容器平台集成的实现。
社区
已经有一个相当大的无服务器社区,拥有多个会议,在许多城市举办聚会,以及各种在线小组。我预计这种情况会继续发展,可能与 Docker 和 Spring 等社区的发展轨迹相同。
结论
无服务器,尽管名字令人困惑,但它是一种架构风格,在这种风格中,我们依赖于运行我们自己的服务器端系统作为我们应用程序的一部分,而这比平时要少。我们通过两种技术来实现这一点:BaaS,我们直接将第三方远程应用程序服务紧密集成到我们应用程序的前端,以及 FaaS,它将服务器端代码从长时间运行的组件移动到短暂的函数实例。
无服务器不是解决所有问题的正确方法,因此请警惕那些说它将取代你所有现有架构的人。如果你现在开始使用无服务器系统,尤其是在 FaaS 领域,要小心。虽然有丰富的财富——可扩展性和节省的部署工作——可以掠夺,但也有一些巨龙——调试和监控——潜伏在下一个角落。
然而,这些财富不应该被轻易忽视,因为无服务器架构有许多积极的方面,包括降低运营和开发成本、简化运营管理以及减少环境影响。但我认为,最重要的益处是创建新应用程序组件的反馈循环缩短了。我非常喜欢“精益”方法,主要是因为我认为尽早将技术展示给最终用户以获得早期反馈,以及无服务器带来的上市时间缩短,非常符合这种理念。
无服务器服务以及我们对如何使用它们的理解,今天(2018 年 5 月)正处于“略显尴尬的青春期”。在未来几年,该领域将取得许多进步,看看无服务器如何融入我们的架构工具箱将是一件很有趣的事情。
致谢
感谢以下人员对本文的贡献:Obie Fernandez、Martin Fowler、Paul Hammant、Badri Janakiraman、Kief Morris、Nat Pryce、Ben Rady、Carlos Nunez、John Chapin、Robert Bagge、Karel Sague Alfonso、Premanand Chandrasekaran、Augusto Marietti、Roberto Sarrionandia、Donna Malayeri。
感谢 Badri Janakiraman 和 Ant Stanley 为关于术语起源的侧边栏提供意见。
感谢我以前在 Intent Media 的团队成员,他们以适当的怀疑态度和热情来应对这项新技术:John Chapin、Pete Gieser、Sebastián Rojas 和 Philippe René。
感谢 Sid Orlando 进行校对。
最后,感谢我在无服务器社区的朋友和同事,尤其是那些我在本文中链接到他们内容的人。
重大修订
2018年5月22日: 整篇文章的实质性更新。阅读此处以了解此更新的详细信息。
2016年8月4日: 添加了“未来”和“结论”
2016年7月25日: 添加了“起源”侧边栏和“与容器的比较”部分
2016年7月18日: 添加了“缺点”
2016年7月13日: 添加了“优点”
2016年6月17日: 添加了“什么是无服务器的?”
2016年6月16日: 添加了“解开‘函数即服务’”
2016年6月15日: 发布第一部分 - 一些例子