软件开发架构师

理解Serverless:构建全服务应用程序的技巧和资源-InfoQ

运维 20 2019-09-02 23:17

本文要点

  • Serverless 不仅仅是功能即服务(FaaS)。
  • 不要担心供应商锁定;接受供应商通过事件集成来提供的功能。
  • 开源工具有助于简化复杂应用程序的构建。
  • 使用基础设施即代码(Infrastructure as Code,IaC)的解决方案(如 CloudFormation)来定义 Serverless 应用程序并简化 DevOps。
  • 强大的监控解决方案可以通过精确的成本管理和评估工具提供函数和集成性能的可视化。

尽管在过去几年中, Serverless 技术已经得到了迅速普及,但是对于 Serverless 解决方案仍然存在许多误解和担忧。供应商锁定、工具、成本管理、冷启动、监控和开发生命周期都是 Serverless 技术相关的热门话题。本文旨在解决其中的一些问题,并分享相关的技巧和资源,以指导 Serverless 新手构建功能强大、灵活且经济高效的 Serverless 应用程序。

对 Serverless 技术的误解

其中一个主要的误解是,Serverless 等同于功能即服务 Functions as a Service ,FaaS),因此不值得为此作出特别激进的改变。虽然 AWS Lambda 无疑是 Serverless 崛起的明星之一,并且可以说是 Serverless 架构越来越受欢迎的因素之一,但与 FaaS 相比,Serverless 还有更多功能。

Serverless 的核心原则是:我们不必担心如何管理基础设施或者扩容缩容,只需为使用的内容付费既可。如果考虑这些因素,那么就有许多可用的服务了,比如 AWS DynamoDB、S3、SNS 或 SQS、Graphcooll、Auth0、Now、Netlify 或 Firebase(还有很多很多)。最终,Serverless 提供了强大的云计算功能,而无需承担管理基础设施或优化容量可伸缩性的负担和责任。这种抽象还意味着基础设施层的安全不再是我们需要关心的问题了,考虑到维护安全标准的难度和复杂性,这是一个非常大的利好。最后但也很重要的是,如果我们不使用它所提供的基础设施,那么就不必为此付费。

也可以认为 Serverless 是一种“思想状态”——一个人在设计解决方案时所采用的思维方式。避免使用需要维护任何基础设施的方式。通过 Serverless 的方式,我们正在试图将我们投入在项目中的工作时间重新分配到对用户有更直接影响和益处的事情上,比如健壮的业务逻辑、能吸引用户的界面及快速响应、可靠的 API 上。例如,如果我们可以通过支付 Algolia 来避免管理和维护一个自由文本搜索平台,那么这就是我们将要做的。以 Serverless 的方式来构建应用程序可以极大地缩短上市时间,因为我们不再需要担心如何管理复杂的基础设施了。这样可以规避管理基础设施产生的责任和成本,并能集中精力构建客户真正需要的应用程序和服务,这种方式,Patrick Debois 称之为“全服务”,这个术语已经得到了 Serverless 社区的认可。其次,函数应该被视为将服务绑定在一起的粘合剂,并且可概念化为部署单元(而不是部署整个库或 Web 应用程序),从而允许对应用程序的部署和变更进行非常细粒度的控制。如果不能以这种方式部署函数,那么这可能是一种代码坏味道,表明该函数承担了太多的职责,应该进行重构了。

在开发云应用程序时,有些人担心供应商锁定。对于 Serverless,这种担心仍然存在,我认为这是源于对 Serverless 真正含义的误解。例如,以我在 AWS 上构建 Serverless 应用程序的经验来看,采用 AWS Lambda 将其他 AWS 服务粘合在一起,是 Serverless 架构强大功能的一部分。这个例子很好地说明了整体是优于局部的。试图避免供应商锁定实际上会导致比我们想要解决的问题还要更严重的问题。使用容器时,在云供应商之间管理自己的抽象层可能会更容易些,但是当使用 Serverless 时,特别是考虑到 Serverless 的成本效益时,这种努力就不值得了。一定要考虑供应商是如何提供设施来暴露服务的;一些专用服务依赖于与其他供应商的强大集成点,并且提供了开箱即用的钩子。从 API 网关端点处指定需要调用的 Lambda 比将请求代理到现有容器或 EC2 实例中要更容易些。Graphcool 使用 Auth0 提供了简单的配置,这比使用自定义标识提供程序也更容易。

为我们的 Serverless 应用程序选择合适的供应商是一个架构决策问题。我们不应该假定某天再回过头来管理服务器,并以此前提来构建 Serverless 应用程序。选择云供应商与选择使用什么容器或什么数据库来构建应用程序,甚至与用什么语言来编写代码没有什么不同。

最好考虑下如下几点:

  • 你需要什么服务以及为什么需要这些服务。
  • 云服务供应商提供了哪些不同的服务,以及如何使用你所选择的 FAAS 实现将这些服务粘合在一起。
  • 支持哪些编程语言(是动态类型还是静态类型、是编译代码还是解释代码、基准测试、冷启动性能、开源生态系统等)。
  • 你的安全要求是什么(SLAS、2FA、OAuth、HTTPS、SSL 等)。
  • 如何管理 CI/CD 和软件开发周期。
  • 可以利用什么样的基础设施即代码(IaC)解决方案。

如果你正在对一个现有的应用程序进行扩展并添加了 Serverless 功能,那么这可能会限制你的选择,不过几乎所有 Serverless 技术都提供了某种形式的 API(通过 REST 端点或消息队列),这些 API 提供了一个简单的集成点来将应用程序扩展成可以独立开发的核心应用程序。选择使用那些提供了易理解的 API 并且具有可靠的文档和强大社区的服务,这样你就不会出错了。很多时候,当谈到 Serverless 技术时,集成的简单性可能是我们关注的关键指标,它可能也是自 2015 年 Lambda 发布以来,AWS 获得成功的最大贡献因素之一。

在什么情况下使用 serverless 是有益的

Serverless 几乎可以用在任何地方,我发现现在的使用情况还远远没有发挥出 Serverless 计算的好处。由于 Serverless 技术的存在,云计算的进入门槛现在非常低。如果开发人员有一个想法,但不知道如何管理云基础设施和优化成本,他们完全无需担心是否能找到一个有工程背景的人来帮助他们。如果一家初创公司正在试图构建一个平台,但又担心由此产生的成本,那么他们可以采用 Serverless 的方式轻松实现。

由于 Serverless 能节约成本且具有可伸缩性,它可以像适用于具有遍布全球数百万用户的 Web 应用程序那样,适用于内部 IT 系统。当谈到计费时,我们可以用美分而不是用欧元(或其他货币)来结算。这是非常强大的。就算是最基础的 AWS EC2 实例(t1.micro)持续开一个月,即使什么都不做(谁还没有忘记关闭的时候呢!),我们也需要花费 15 欧元。为了和 AWS EC2 进行比较,产生相同的成本水平,我们需要在同一时间段内运行一个 1 秒内可达到 300 万次的 512MB 的 Lambda 函数。同样地,如果你不使用这个函数,它就不会产生任何费用。

由于 Serverless 主要是基于事件的,所以可以将 Serverless 基础设施直接添加到传统系统中。例如,可以使用 AWS S3、Lambda 和 Kinesis 为传统零售系统搭建可以通过 API 网关端点接收数据的分析服务,或者使用 DynamoDB 流和 Algolia 来改进自由文本搜索系统。

绝大多数的 Serverless 平台都支持多种语言,最常用的有 Python、JavaScript、 C#、Java 和 GO。一般来说,各语言都没有对可使用的库进行限制,因此,我们可以随意选择使用我们最擅长的开源代码库。然而,保持低依赖性仍是个不错的主意,它可以确保函数尽可能地在最佳状态下执行,并能利用 Serverless 应用程序强大的可伸缩性。容器需要装载的包越多,冷启动时间就越长。

冷启动是指容器、运行时和函数处理程序在使用之前要先初始化。它可能会导致大约 3 秒的功能延迟,当你试图为缺乏耐心的用户提供响应时,这并不理想。不过,冷启动只发生在函数空闲了几分钟后的第一次调用时。许多人认为这是个小麻烦,可以不断检测该函数保持其活跃,或者干脆无视它。

虽然 AWS 已经发布了一个无服务器的 SQL 数据库 Serverless Aurora ,但是,SQL 数据库并不是一个理想的 Serverless 计算用例,因为它们是依赖连接来执行事务的,当 AWS Lambda 接收的吞吐量升高时,这些事务可能很快就会成为瓶颈。值得一提地是,虽然 Serverless Aurora 在不断改进,但是目前像 DynamoDB 这样的 NoSQL 解决方案才是更适用于 Serverless 计算的解决方案。不过,毫无疑问,这种情况在未来几个月将会发生变化。

Serverless 能使用的工具也有一些限制,特别是用于本地测试方面的工具。虽然存在 docker-lambda、DynamoDB Local 和 lLocalStack 等解决方案,但通常它们非常复杂,需要大量配置。不过,所有这些项目都在积极的开发中,这些工具达到我们都能接受的标准只是时间问题。

Serverless 对开发生命周期的影响

由于我们的基础设施始终都只是配置,所以可以使用脚本来指定和部署代码。它既可以是简单的 shell 脚本,也可以是诸如 AWS CloudFormation 之类的配置即代码的解决方案。我偏向使用 CloudFormation 的原因是:虽然它没有为每个区域都提供配置,但它能允许我们指定自定义的资源,并且这些自定义资源可以是简单的 Lambda 函数。这意味着,当 CloudFormation 不能满足我们的需求时,我们可以编写一个自定义资源(一个 Lambda 函数)来弥补这个不足。我们可以使用支持 Lambda 的自定义资源来做任何事情,甚至可以在 AWS 环境之外配置依赖项。

同样地,由于一切都是简单的配置,因此可以根据环境 / 区域 / 用户对部署脚本进行参数化,特别是当我们使用基础设施即代码(Infrastructure as Code ,IaC)解决方案(比如 CloudFormation)时。例如,我们可以为代码仓库中的每个分支部署基础设施的副本,以便在开发期间对它们进行完全隔离的测试,这可以极大地减少开发人员的反馈循环测试,开发人员通过反馈循环测试来判断其代码在线上环境中是否能够充分执行。管理者也无需担心部署这么多环境的成本问题,因为它们是按使用次数计费的。

DevOps 也不必担心那么多了,因为他们只需要确保开发人员的配置是正确的即可。他们不再需要管理实例、负载均衡或安全组了。当谈到 Serverless,“NoOps”这个术语经常被提及,但是,拥有基础设施配置方面的专业知识仍然是很重要的,尤其是拥有 IAM 配置和优化云资源方面的知识。

一些非常强大的可视化监控工具(如 Epsagon、Thundra、Dashbird 和 IOPipe)可以提供可视化跟踪 Serverless 应用程序的能力。它们可以提供诸如日志记录、跟踪、函数性能度量、架构瓶颈、成本分析和评估等信息。这不仅能为 DevOps 工程师、开发人员和架构师提供深入了解应用程序执行情况的能力,还能够帮忙管理层更好地了解实时的、秒级的资源成本和计费预测。如果使用托管的基础设施,这些是很难做到的。

由于不再需要考虑 Web 服务器、虚拟机或容器管理、服务器补丁、操作系统、Internet 网关、跳箱等因素,架构 Serverless 应用程序变得简单多了。不同职责的抽象使 Serverless 架构能够更专注于最重要的事情,即满足业务方和客户的需求。

虽然相关工具现在还不够完善(它们每天都在改进中),但是开发人员的体验已经非常好了,因为开发人员不仅能更专注于编写业务逻辑,也能以最好的方式将应用程序的复杂性转移到架构中的不同服务上去。Serverless 应用程序的编排是基于事件并由云供应商(如 SQS、S3 事件或 DynamoDB 流)抽象得出的,因此,开发人员需要做的仅仅是指定其业务逻辑将如何响应对应的事件,而无需担心以怎么的方式实现数据库、消息队列是最好的,或者在特定存储设备上应该怎么尽可能地优化操作数据等问题。

代码可以像使用任何其他应用程序开发过程一样在本地运行和调试。单元测试也没有改变。正如我前面提到的,使用可定制的堆栈配置来部署整个应用程序基础设施的能力,能够使开发人员快速获取关键的反馈信息,从而无需担心测试成本或在昂贵的托管环境中运行的影响。

用于构建 Serverless 应用程序的工具和技术

现在还没有构建 Serverless 应用程序的标准方式,用于构建 Serverless 的服务也是如此。在提供强大的 Serverless 解决方案方面,AWS 显然是一个领导者,但是,谷歌云 Zeit Firebase 也都值得一试。如果你正在使用 AWS,我推荐的方式是使用无服务器应用程序模型(Serverless Application Model,SAM)来构建应用程序,特别是使用 C#时,因为 Visual Studio 中的工具非常出色。Visual Studio 能做的任何事情,SAM CLI 也都能做到,因此,即使使用了不同的 IDE 或文本编辑器,也不必担心会错过任何东西。当然,SAM 也可以和其他语言一起使用。

当谈到和其他语言一起使用时,Serverless Framework 是一个优秀的开源工具,它能够使用非常强大的 YAML 配置文件来配置所有的内容。Serverless Framework 还能支持多云供应商,因此,如果我们正在寻找一个多云的解决方案,这个框架肯定能使我们的工作变得更轻松。它还有一个庞大的社区,社区里有很多插件,基本可以满足我们的需求。我曾经也为该社区做出过贡献,社区的团队成员都非常友好、乐于助人、热情好客。

对于本地测试,docker-lambda、Serverless Local、DynamoDB Local 和 LocalStack 都是很好的开源工具。但 Serverless 工具目前还处于初级阶段,因此,如果我们的场景比较复杂,通常需要做一些额外的工作才能使本地测试正常运行。然而,简单地将堆栈部署到我们的环境中并进行测试是非常便宜的。这样做的好处是不必担心在本地是否精确复制了云环境。

使用 AWS Lambda 层可以减小部署包,并能缩短加载时间。

确保我们使用了正确的编程语言。不同的语言有不同的优缺点。目前有很多基准测试,但当谈到 AWS Lambda 性能时,JavaScript、Python 和 C#(.NET Core 2.1+)明显是领先者。最近,AWS Lambda 引入了运行时 API,该 API 允许我们指定我们想要使用的语言和运行时,因此可以随意尝试了(C++、其他任何语言?)。

需要一直保持较小的部署包。部署包越小加载就越快。避免引入大型库,特别是当我们只使用大型库中的一两个功能时。如果我们正在使用 JavaScript 编程,请确保使用 Webpack 之类的构建工具来优化我们的构建,并且只引入我们所需要的内容。虽然 .NET Core 3.0 引入了 QuickJIT 和有助于提高性能的分层编译,但它对冷启动有很大的帮助。

Serverless 函数基于事件的特性可能会使业务逻辑在开始时难以协调。在这方面,消息队列和状态机是非常有用的。Lambda 函数可以相互调用,但也只在不需要等待响应(触发即忘记)的情况下才能这样做——我们也不希望在等待另一个函数完成后才计费。在分离业务逻辑领域、控制应用程序瓶颈和处理事务(使用 FIFO 队列时)时,消息队列非常有用。可以将 AWS lambda 函数作为死信队列分配给 SQS 队列,来跟踪失败事件以进行分析。当需要将函数连接在一起管理复杂的过程时,AWS 步骤函数(AWS Step Functions,状态机)非常有用。步骤函数不需要让 Lambda 函数调用另一个函数,也可以协调状态转换、在函数之间传递数据,并能管理全局函数状态。它还允许指定重试条件,或者在发生特定错误时执行特定操作,功能是非常强大的。

结论

过去几年中,Serverless 的发展速度十分惊人。对于这种范式的转换存在一些误解。从简化开发和 DevOps 到由于抽象的基础设施和可伸缩的管理而大大降低了运营成本,Serverless 解决方案在整个开发生命周期中都有很大的优势。尽管 Serverless 也有其局限性,但是有一些可靠的技术和设计模式可以用于构建强大的 Serverless 应用程序或将 Serverless 元素集成到现有架构中。

作者介绍

Christopher Paton是爱尔兰科克 Johnson Controls 公司的高级开发人员。他曾在英国和爱尔兰的多个行业(从广播媒体到公共部门组织)工作过。自从 2015 年 Lambda 发布以来,他一直在使用 Serverless 技术。他曾在 AWS 的 Serverless 用户组上发表演讲,并在科克主持了 Serverless 框架会议。他将在 RebelCon 发表题为“使用 Serverless 快速构建和部署”的演讲。

原文链接:

[Understanding Serverless: Tips and Resources for Building Servicefull Applications](

文章评论