背景
2019 年 Berkeley 预测 Serverless 将取代 Serverful 计算 [1 ] ,成为云计算的计算新范式。Serverless 为应用程序开发提供了一种全新的系统架构,其凭借着弹性伸缩省事省心,按需付费更低成本、聚焦业务降低 OPS 这三大核心价值,将开发人员从繁重的手动资源管理和性能成本优化中解放出来,让工程师的生产力再次发生变革。
从上面的定义可以看出, Severless != No Server, 只是对于开发者来说,没有了 Server 去管理。而在云厂商提供的服务中,Serverless 架构应该是采用 FaaS(Function as a service,函数即服务)和 BaaS(后端服务)服务来解决问题的一种设计。
FaaS 服务的典型代表:AWS lambda、阿里云函数计算 FC、Azure Functions、Google Cloud Functions 等。BaaS 服务典型代表:AWS: S3、Dynamodb、SQS 等; 阿里云:OSS、 TableStore、MNS 等。
Serverless 计算
当然随着需求和技术的发展,业界出现了一些 FaaS 以外的其它形态的 Serverless 计算服务,比如 Google Cloud Run、AWS App Runner、阿里云 Serverless 应用引擎 SAE、 阿里云 Serverless Kubernetes ASK 等,这些服务也提供了弹性伸缩能力和按使用计费的收费模式,具备 Serverless 服务的形态,可以说进一步扩大了 Serverless 计算的阵营。
而在 Serverless 计算领域最典型的两种产品形态代表 FaaS 和 Google Cloud Run, 都不约而同采用了并发度(Concurrency)这个指标作为扩缩容策略。接下来我们重点剖析下不同产品形态下并发的语义以及为什么这些流行的 Serverless 计算产品为什么采用并发度作为扩缩容的策略。
什么是并发?
并发是现代计算的核心原则之一, 并发是指计算系统同时处理多个任务的能力。例如,如果您的计算机同时运行多个程序,则具有多个并发进程 / 线程可以共享 CPU 时间。如果单个应用程序进程同时处理多个网络请求,或者并行处理队列中的多个作业,则也可以认为该应用程序正在执行并发工作。
比如 “世界第一语言 PHP” 在 Web 领域的实践,使用就是进程池,如下图中的 FastCGI 进程管理器。发送到服务器的 Web 请求将被分配给进程池中的 CGI 进程。该 CGI 进程将处理该单个请求。如果同时收到多个请求,则将启动多个 CGI 进程并行处理它们。然而,每个进程一次只能处理一个请求。服务器能够通过对 CGI 进程进行上下文切换来处理并发请求。操作系统调度程序将跟踪所有 CGI 进程,并在需要时切换正在 CPU 上运行的 CGI 进程,以使每个 CGI 进程在需要时都能获得属于自己的、公平的 CPU 时间份额。
如今,有更多用于并发的工具, 这包括现代编程语言内置的强大异步并发机制,以及帮助简化并发的云计算服务。让我们看看一些云计算服务如何设计和使用并发。
单实例单并发
云厂商的 FaaS 服务的并发扩缩容原理基本大同小异, 我们以 AWS Lambda 官方文档 [3 ] 为参考:
当首次调用一个函数时,FaaS 服务会创建一个函数实例,并运行处理程序方法以处理事件。完成后,函数会在一段时间内保持可用状态,以处理后续的事件。如果在函数忙碌时有其他事件到达,FaaS 会创建更多的函数实例来同时处理这些请求。
从文档中我们可以看出,每个函数实例一次只能处理一个事件请求(即 one concurrent request per instance,也称为单实例单并发)。在处理事件请求时,函数被认为是繁忙的,因此任何并发事件都必须转到另一个函数实例。每次必须创建函数的新实例时,都会出现短暂的 “冷启动”(Cold Start) 延迟。这个冷启动的持续时间取决于您的代码大小和使用的运行时 Runtime。下图 [4 ] 显示了当有多个并发请求需要进行并行处理时,FaaS 如何实时扩展函数实例的数量:
Tips: 只有绿色部分是毫秒计费, 黄色和空白部分均不会计费, 真正 100% 为计算资源付费。
FaaS scaling and concurrency
这使得 FaaS 的并发模型在某些方面类似于那些老式的 PHP 进程管理器。在这两种情况下:1). PHP 进程管理器通过并行启动更多进程来实现并发。单个进程一次只能处理一个事件请求。2). FaaS 通过并行启动更多的执行环境容器实例来实现并发, 单个实例一次只能处理一个事件请求。但使用 PHP 进程管理器那样的进程级别的并发有两个经典难题需要解决:
进程之间的安全隔离:您必须在操作系统分配 CPU 时间和系统资源给进程时做出正确的决策。一个进程可能会消耗过多的资源,影响在同一台机器上运行的其他进程的性能。
自动扩缩容:以 PHP 应用程序为例,您必须管理每个服务器上的 PHP CGI 进程数量,并且必须对运行这些进程的服务器数量进行手动扩缩容。
FaaS 能很好解决上述两个难题,FaaS 明显有一些现代化的特点,以函数计算执行环境容器的安全隔离为例 [5 ] :
阿里云 FC 计算节点安全隔离
虚拟化级别安全隔离
神龙裸金属计算节点可运行来自不同用户的函数实例,使用阿里云安全沙箱 [13 ] 提供函数级别虚拟化及容器隔离,ECS 虚拟机只允许运行同用户的函数实例,借助 ECS 隔离提供用户级别虚拟化隔离,使用 Runc [14 ] 等容器技术实现函数级别的容器隔离。
函数实例网络访问受限,用户决定网络外访权限
函数实例配置私有 IP 地址,用户不可直接访问,且实例间网络不可达,网络隔离使用 open vSwitch、iptables 和 routing tables 实现。
函数实例资源受限函数 CPU / 内存设置的配额
函数计算负责函数实例沙箱容器的漏洞修复及安全升级
使用 FaaS 这种事件驱动的全托管计算服务,您将自动获得隔离的执行环境实例,FaaS 服务自动管理执行环境实例的数量和容量。您所要做的事情就是提供您的代码到 FaaS 服务,并向 FaaS 服务发送事件以触发该代码执行即可。
FaaS 简略概览
从上面对 FaaS 并发扩缩容的讨论中,相信大家很快 get 到单个实例一个并发的能力对 CPU 密集型的逻辑非常友好。而现代的许多工作负载都充满了 I/O 操作,如果我们采用 FaaS 经典的 one concurrent request per instance 模式,会有如下痛点问题:
严重的资源浪费
IO-intensive workload [ 11]
蓝色方框表示程序正在工作时的时间,红色方框表示等待 IO 操作完成所花费的时间。由于 IO 请求可能比 CPU 指令花费的时间长几个数量级,因此您的程序可能会花费大部分时间等待, 实例资源浪费严重。并且随着并并发数目的变大,浪费的资源也呈线性增长,如下面红色部分即为浪费的计算资源:
FaaS IO-intensive workload
2. 可能会对共享资源造成意想不到的后果
数据库是一个典型的例子。当使用传统的关系型数据库(如 mysql)时,数据库有一个最大并发连接数。传统常驻型服务器通常使用 “数据库连接池” 进行优化。“数据库连接池” 限制了单个服务器实例对数据库的最大并发连接数,同时允许并发的请求能有效地共享 “数据库连接池” 的连接。然而,如果每个实例只能处理一个请求并维持与数据库的开放连接,则请求的数量与到数据库的连接数之间存在一对一的关系。结果是在负载高峰期间,数据库可能会因过多连接而打满,并最终拒绝新连接。如果一个数据库实例的最大连接数为 100,使用 FaaS, 示意图如下:
FaaS with DB
单实例多并发
因此,就 FaaS 领域的 one concurrent request per instance 的痛点问题,Google Cloud Run 提供了 multi concurrent requests per instance 的能力 [6 ] ,这就很好解决我们上文讨论的单实例单并发扩缩容模型的痛点:
Google Cloud Run 单个实例默认最大并发度 (即单个实例的并发请求数上限) 为 80,最大可调整到 1000。
IO 等待期间不再是资源浪费
Google Cloud Run IO-Intensive workload
对共享资源造成影响可预期:提高数据库连接吞吐
Google Cloud Run With DB
如果每个实例配置了数据库连接池大小为 10,那么每个实例可以允许 10 个并行请求到数据库。由于每个实例可能会接收高达 80 个并发请求,“数据库连接池” 将在等待数据库连接被释放并返回到池中时,自动阻止传入的请求。通过使用 10 个数据库连接响应 80 个请求,理论上可以在数据库达到其最大连接限制之前将数据库的吞吐量提高 10 倍。
有趣的是,一些 FaaS 厂商勇敢做出了 multi concurrent requests per instance 的尝试,比如阿里云函数计算设置实例并发度 [15 ] ,Google Cloud Functions 第 2 代也开始支持设置实例并发度 [16 ] 。旨在解决现代很重要的 IO 密集型工作负载问题。
为什么 Serverless 使用并发读扩缩容
FaaS 和 Google Cloud Run 采用实例并发度 (即实例的并发请求数上限) 这个指标进行扩缩容,而不是采用 CPU 指标等 HPA 策略,是因为在 Serverless 领域,实例并发度是 “基于请求处理 / 事件驱动进行扩缩容” 表达最好的一个方式。
FaaS 和 Google Cloud Run 都有实例缩至为 0 和有请求进来可以拉起一个新实例的能力,在实例 0-1 过程中无法使用 CPU 或内存等指标进行扩容。
更好地匹配请求处理:并发度能够更好地匹配实际请求的数量,因此可以更好地利用计算资源,同时确保请求能够快速得到响应。以阿里云函数计算和 K8S 做一个资源匹配请求速度的对比 [7 ] :
更好的资源利用率:实例并发度策略可以更好地利用计算资源,可以在请求高峰期间快速扩容,而在请求较少时保持最小的实例数量,从而减少资源浪费。FaaS 和 Google Cloud Run 允许用户运行任何语言的代码,并自动扩展以匹配流量:并发度总数 = 同时处理请求的实例数量 * 每个实例的最大并发请求数上限
当然,引入的并发度的概念也给习惯了 CPU 指标等扩缩容的开发者带来的新的疑惑, 对于 IO 密集型的应用,基于 CPU 指标的 HPA 扩容策略很简单就可以提高应用程序的可用性、性能和可靠性,并使资源更高效地利用。反而单个实例的最大并发度的合理值怎么去设置是一个比较头疼的问题?这个问题,业界通常都是建议您根据自己的负载情况做压测迭代出合适的并发度值。阿里云函数计算为此做了一个业界最前沿的探索,提供了自动化推荐能力:从青铜到王者,揭秘 Serverless 自动化函数最佳配置 [8 ] [17 ] , 并由此展望智能动态并发度:在这种模式下,用户不需要通过手动配置参数,而是在函数运行时动态调整,根据实例 CPU 负载的健康指标自动调整到最佳值。
结论
基于上文对并发度的讨论,对于单实例单并发(云产品代表 FaaS)和 单实例多并发(云产品代表 Google Cloud Run) 这两种形态的 Serverless 产品, 我应该选择哪个产品来托管我的应用程序呢?以下是一些情景是我个人会选择哪种产品的建议:
但最终还是需要根据您具体的业务需求做取舍,选择最合适的产品和方案。注:FaaS 中的函数计算 FC 和 Google Cloud Functions V2 也支持单实例多并发。
上述表格中的建议是基于阿里云函数计算应用中心 [9] 中的用户对于应用的偏好部署次数【见下图】以及客户落地案例【见参考 [12]】来佐证的, 尤其对于每个请求必须相互隔离或者 CPU 密集型任务, FaaS 具有无与伦比的优势:
对于存量应用,将 CPU 密集型任务从应用中抽离出来,提升服务的稳定性,这个文章 PDF Generation With AWS Lambda [10 ] [18 ] 深入讨论了这种实践的收益。
对于新业务 CPU/GPU 密集型应用, 如音视频处理以及最近大火的大模型 AIGC (AI generated content ) 应用, 是 FaaS 天然契合的场景 。
在 AI 场景中请求和后端资源的调度比传统的微服务场景的要求会更高,主要原因是 AI 场景的请求对资源的消耗特别大。比如一个 Stable Diffusion 使用 A10 GPU 卡部署,一块 A10 卡 (ecs.gn7i-c8g1.2xlarge) 启动 Stable Diffusion 服务一次只能处理个位数的文本绘图请求。一旦同时进来请求过多,就会出现计算资源竞争从而导致请求超时的情况。而 FaaS 的 "one concurrent request per instance" 天然契合这个场景, 简直就是绝配。
函数计算 FC 应用中心文件处理应用部署情况图
函数计算 FC 应用中心音视频处理应用部署情况图
函数计算 FC 应用中心 AI 应用部署情况图
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !