Serverless(无服务器架构)指的是由开发者实现的服务端逻辑运行在无状态的计算容器中,它由事件触发, 完全被第三方管理,其业务层面的状态则被开发者使用的数据库和存储资源所记录。
Serverless使得开发者无需直接处理服务器(无论是物理机,虚拟机,容器等)。无主机的优势会让使用者在服务器维护方面的操作开销大大减少,无需为升级服务器而忧心,无主机还意味着在应用程序中需要监控的度量指标也会不同。这是因为使用的大多数底层服务不会再发布 CPU、内存、磁盘大小等传统度量指标了。这让不再需要再特别关心架构的底层操作细节。
AWS云架构战略副总裁Adrian Cockcroft曾经针对Serverless的界定给出了一个简单的方法:“如果你的PaaS能够有效地在20毫秒内启动实例并运行半秒,那么就可以称之为Serverless”。而在 A Berkeley View on Serverless Computing 论文中的描述:Serverless = FaaS + BaaS 。
参考上面关于Serverless架构的技术逻辑示意图,可以看到Serverless架构大量依赖第三方服务(也叫做后端即服务,即“BaaS”)或暂存容器中运行的自定义代码(函数即服务,即“FaaS”)的应用程序,因此FaaS和BaaS可以视作Serverless的两个具体实现。函数是无服务器架构中抽象语言运行时的最小单位。在这种“按需使用”的架构中,并不看重运行一个函数需要多少 CPU 或 RAM 或任何其他资源,而是更看重运行函数所需的时间,也只为这些函数的运行时间付费。
下面简要分析Serverless所具备的优势,以及目前存在的一些短板:
Serverless 架构所具备的优势有:
Serverless 架构的一些短板:
Serverless 的"运行才计算“的特性,便意味着他是一种 “严格” 的事件驱动式计算。 事件驱动程序设计这种设计模型是在交互程序(Interactive program)的情况下孕育而生的。这也意味着,系统在编程模型上有着巨大的改变。在我们编写 GUI 程序,如桌面程序、Web 前端应用,我们都通过监听用户对按钮、链接等组件操作,才开始相应的处理逻辑。这和 Serverless 是相似的,只在用户使用的时候,才会对应用户的行为进行响应。
容器技术在 Serverless 架构中通常是通过 Docker 实现的。云服务提供商会将每个函数的代码打包到一个独立的 Docker 容器中。在容器内部,可以确保代码可以在隔离的环境中运行,并且不会影响其他函数或应用程序的运行。
当请求到达时,云服务提供商会在容器内部执行函数,并在完成后销毁容器。这种做法有助于确保每个函数的执行是短暂的,并且不会占用过多资源,从而避免了资源浪费。
通过使用容器技术,Serverless 架构可以确保函数在隔离的环境中运行,并且可以快速扩展,以满足增长的需求。同时,容器技术还可以提高应用程序的安全性,因为可以限制函数的权限,以防止函数访问不该访问的数据。
一个传统 Java Web应用的技术架构可能如下图所示:
这样的架构可以让前端十分轻便,不需要做什么应用逻辑,只是负责渲染用户界面,将请求通过HTTP发送给后端,而所有的数据操作都是有由后端的Java程序来完成的。这样的架构开发起来比较容易,但是维护起来确十分复杂,前端开发、后端的开发都需要十分专业的人员、环境的配置,还要有人专门维护数据库、应用的更新和升级。
而在serverless架构中,我们不再需要在服务器端代码中存储任何会话状态,而是直接将它们存储在NoSQL中,这样将使应用程序无状态,有助于弹性扩展。前端可以直接利用BaaS而减少后端的编码需求,这样架构的本质上是减少了应用程序开发的人力成本,降低了自己维护基础设施的风险,而且利用云的能力更便于扩展和快速迭代。
微服务(MicroService)是软件架构领域业另一个热门的话题。如果说微服务是以专注于单一责任与功能的小型功能块为基础,利用模组化的方式组合出复杂的大型应用程序,那么我们还可以进一步认为Serverless架构可以提供一种更加“代码碎片化”的软件架构范式,我们称之为Function as a Services(FaaS)。而所谓的“函数”(Function)提供的是相比微服务更加细小的程序单元。例如,可以通过微服务代表为某个客户执行所有CRUD操作所需的代码,而FaaS中的“函数”可以代表客户所要执行的每个操作:创建、读取、更新,以及删除。当触发“创建账户”事件后,将通过AWS Lambda函数的方式执行相应的“函数”。从这一层意思来说,我们可以简单地将Serverless架构与FaaS概念等同起来。
Serverless 天生就与微服务架构是相辅相成的。一个 Serverless 应用拥有自己的网关、数据库、接口,你可还以使用自己喜欢的语言(受限于服务提供者)来开发服务。换句话来说,在这种情形下,一个 Serverless 可能是一个完美的微服务实例。
传统云平台与Serverless平台三个比较关键的区别是:
传统云平台与Serverless平台的其他技术细节的一些对比(以AWS为例):
IaaS (Infrastructure as a Service): 基础设施即服务。 是指提供基础设施(如计算、存储、网络等)作为服务,用户可以在此基础上搭建自己的应用环境。
CaaS (Containers as a Service): 容器即服务。 是指将容器技术提供给用户使用,用户可以通过容器运行自己的应用。
PaaS (Platform as a Service): 平台即服务。 是指提供开发平台和应用管理工具,用户只需要关注应用的开发和运行,不需要关注底层的基础设施。
Serverless:无服务器架构。 是指不需要为了运行应用程序而显式管理和配置服务器资源,所有的底层管理和维护都由平台提供方负责。用户只需要上传自己的代码,等待代码被自动运行即可。 Serverless = FaaS + BaaS。BaaS (backend as a service)
FaaS (Function as a Service) 函数即服务: 作为一种云计算服务,它允许开发人员在云提供商处部署单个函数,而不是整个应用程序。云提供商在请求到达时自动执行函数,并在请求完成后自动销毁。而“FaaS”中 最关键的 "F"也就是函数是如何定义的呢?可以参考下面OpenFaaS项目发起人 Alex 对CloudFunction的定义:
下面简单探讨一些 FaaS 中不得不面对的关键技术解决方案:
弹性伸缩是函数计算成本优化中的关键一环,弹性伸缩针对的主要对象是函数实例(一些faas架构还包含一同启动的MQ触发器实例)。弹性伸缩的主要流程包含 : 指标感应(触发弹性伸缩) -> 调度伸缩策略的计算 -> 最终扩缩生效,以上步骤和k8s 的 HPA 十分类似,但是实现机制针对函数计算的特性有所改变,尽管如此,k8s目前所具备的能力也能较为有效地满足FaaS的弹性伸缩需求,因此,开源社区的Serverless/FaaS产品基本是基于k8s实现的,也正是因为k8s的发展,促进了开源社区的Serverless/FaaS产品的发展。
以下是 开源项目OpenFaaS基于k8s实现的架构图:
关于弹性伸缩,除了Kubernetes的HPA外,社区内还有诸如事件驱动架构弹性框架KEDA、以及KPA(Knative实现)等。KEDA,其解决的核心问题是在事件驱动架构下的弹性伸缩(事件触发扩缩容)与实例从0 -> 1、1->0的问题,KEDA充当Kubernetes Mertrics Server工作,允许用户使用专门的Kubernete自定义资源定义来定义自动缩放规则。
KEDA可以在云和边缘上运行,并且可与与Kubernetes组件(如Horizontal Pod Autoscaler)本地集成,并且没有外部依赖关系。,KEDA整体架构如下:
FaaS 运行时应该为函数的执行提供资源和安全隔离的语言运行环境,并为函数传递调用事件,上下文信息和响应信息等。函数运行时应该为需要具备统一的接口规范来支持多语言的接入,以及专门的监控管理组件(类似随函数实例一同启动的Agent)来维护函数实例的生命周期。
以 业界成熟的 FaaS 平台AWS的Lambda为例,其函数运行时架构就类似下图所示:右侧虚线框括起来的即为一个函数实例,包含一个RuntimeAgent( 即API Endpoints) 用于处理函数调用过程中对函数进程的监控和管理,并收集执行数据上传函数实例外 HostAgent(Lambda Service)用于分析和记录;Processes则包含了执行用户业务逻辑的函数进程以及由FaaS平台提供的Runtime环境。
Lambda通过Runtime 以及 Runtime API来支持多种语言。Runtime API与Runtime之间通过API规范进行调用,而用户函数只需实现Runtime的特定接口函数即可完成初始化、加载和调用等操作。此外,Runtime提供了一个特定于语言的环境,用于在Lambda和函数之间传递调用事件、上下文信息和响应。用户可以使用Lambda平台提供的Runtime,也可以构建自己的运行时。
每个主要编程语言版本都有一个单独的运行时,具有唯一的运行时标识符,如python3.10或nodejs18.x。要将函数配置为使用新的主要语言版本,需要更改运行时标识符。由于AWS Lambda无法保证主要版本之间的向后兼容性,因此这是一项客户自主的操作。
对于定义为容器镜像的函数,可以在创建容器镜像时选择运行时和Linux发行版。若要更改运行时,需要更新函数的配置,并创建一个新的容器镜像。运行时与一个Amazon Linux发行版配对。底层执行环境提供了额外的库和环境变量,这样可以从函数代码中访问这些库和变量。
在k8s集群中,整个 Execution Enviroment 函数实例则可以作为一个Pod 运行在特定集群中,与k8s其他组件组件或用户自定义组件进行交互。
以上可以看到Lambda 通过统一打包用户函数以及函数运行时为镜像的方式实现了函数加载,此外,也可以通过挂载目录或者共享内存的方式实现用户函数代码加载,在此不做展开。
冷启动是指用户函数实例从调度到启动到能够提供服务的准备阶段。我们来看看在传统k8s集群中启动一个包含用户函数以及函数运行时的镜像容器需要包含几个步骤:
其中镜像拉取时间是不可控的,从几秒钟到几十秒甚至数分钟都有可能,在函数代码包含在镜像中的情况下,由于函数镜像的异构化,且由于存在大量的函数镜像 故而导致难以复用。其次,kubelet创建容器的时间一般也长达数秒,最后容器启动后,容器内部的函数进程启动时间(包括运行时环境启动、依赖准备等)也需要耗费时间。
综上,函数冷启动时延是FaaS中的一个关键问题,针对冷启动优化目前业界大概有以下方法:
针对镜像拉取时延,可以采取镜像代码分离的方法,镜像本身是分层的,故所以可将用户函数代码 与 函数运行时、RuntimeAgent等环境依赖层分离,节点提前将不变化的那部分内容提前拉取,节省时间。
针对拉起容器,以及拉起容器后容器内部的函数运行时、RuntimeAgent等环境以及组件启动带来的时间损耗,可以通过函数实例预热,即维护一个冷启动资源池的方式来降低时延,FaaS平台可以预先维护一批不包含业务函数的基础容器组成一个冷启动资源池,待到冷启动过程中,再与某个用户函数实例绑定,通过**共享存储卷 **或 直接在线拉取代码 等方式加载用户函数实例,并开始提供服务。冷启动池的容量、扩缩容等配置则由FaaS平台进行统一的监控和管理。(或者采用更轻量高效的容器运行时:一个是基于KVM的轻量级虚拟化技术,如Firecracker;另一个则是Wasm。AWS的Lambda底层技术就是使用了Firecracker,他的冷启动时间都是ms级的)
针对健康检查,OpenFaaS 曾尝试过在K8s健康检查前,通过 RuntimeAgent(watchdog) 提前开始接受并处理外界流量的方式来降低时延,但这种方法有许多问题,暂不推荐。
针对容器内部用户业务函数进程实例启动的耗时,可以参考 OpenFaaS 的在维护一个缓存函数实例的情况下,在容器内Fork a Processe Per Request的方式。
此外,一般FaaS要求能做到函数实例实例流量为0的情况下缩容至0(K8s 原生的 HPA暂时做不到这一点,但KEDA可以),但一些faas平台并未对此做强制限制,比如OpenFaaS开源版默认运行一个函数实例来承载初始流量,其他的一些平台则采取了维护冷启动资源池的方式来避初始化过长的问题。
基于Web函数架构:其本质是通过HTTP方式来调用函数。这里核心在于函数实例的Runtime环境需要提供一个Web框架和Web Server,请求进入Runtime后则调用用户函数。每一个函数默认配置一个HTTP触发器并为其分配一个固定的独立域名,只要请求这个域名就能够以HTTP触发函数执行,函数只要实现对应接口即可处理请求,最后再通过FaaS平台提供的API网关对函数实例接口请求进行集中的处理。
基于事件驱动架构:不管是社区还是公有云的产品,都有触发器的概念。所谓的触发器其实就是事件源,事件源可以由一个统一的Sidecar容器接收然后以HTTP调用函数;也可以由每个函数Runtime直接接收后调用函数。
基于消息队列MQ: MQ消费是业务场景中常见的一种业务类型,无论是 离线的数据计算,还是在线的实时消息,都会选择MQ作为中间件来对业务进行异步处理和解耦,因此MQ消息队列可以作为整个FaaS平台最大的流量入口。
用户自定义的触发方式: 如OpenFaaS的名为Connector的组件可以让开发者自定义函数的触发方式。
下面是我个人整理的一些FaaS涉及技术点,可能不是很全,仅供参考。
OpenFunction 是一个现代化的云原生 FaaS(函数即服务)框架,它引入了很多非常优秀的开源技术栈,包括 Knative、Tekton、Shipwright、Dapr、KEDA 等,这些技术栈为打造新一代开源函数计算平台提供了无限可能:
OpenFaaS是一个发展比较久的FaaS开源项目,在GitHub上获得了比较多的关注,它在CloudFunction运行时处理请求的模式方面作了较多的探索。在本篇的第三节FaaS实践中着重介绍。
Knative 是一个开源的、基于 Kubernetes 的平台,旨在简化和加速容器化应用程序的开发、部署和管理过程。它提供了一组构建块和工具,帮助开发人员将现代化的、云原生的应用程序部署到 Kubernetes 集群上,实现无服务器架构(Serverless)和自动化的容器编排。
OpenFaaS 是一个开源的 FaaS 框架,可以让用户在其上部署和运行自己的函数。OpenFaaS 使用 Docker 容器作为底层技术,可以运行在任何云环境或者私有云上。OpenFaaS 项目旨在将 Kubernetes 集群或者独立的虚拟机等低级基础设施转化为管理无服务器函数的高级平台。
Overview - OpenFaaS
OpenFaaS 通过helm 在原生K8s部署
OpenFaaS 技术概览图:
上图是 OpenFaaS 的抽象服务流程,下面是各个节点的简单介绍:
Gateway: HTTP 网关,用于接收用户请求及内部指令。
NATS Streaming: 用于异步执行函数。
Prometheus/ AlertManager: 用于收集服务指标及扩缩容操作。
faas-netes: 针对 K8S 的 Provider,可以定制其他的 Provider 例如 Docker Swarm 等。
Docker Registry: 用于拉取函数镜像的仓库。
WatchDog 是OpenFaaS用于调起和监控 Cloud Function 的关键实现,最新的of-watchdog充当运行函数或微服务的反向代理。它可以独立使用,也可以作为OpenFaaS容器的EntryPoint,并作为"Init Process" 在函数容器中启动。其内置了一个使用Golang实现的 HttpServer,提供并发请求、超时和运行状况检查等功能。其通过加载各种环境变量让开发者可以自定义其功能。 OpenFaaS先后推出了 ClassicWatchdog 以及 of - Watchdog两种 Watchdog模式。下面分别介绍:
这种模式下,watchdog 启动了监听在 8080 端口的轻量级 HTTP 服务器,每个进来的请求都会:
上述逻辑类似于传统的 通用网关接口(CGI)。一方面,每次函数调用都启动单独的进程看起来不够高效,而另一方面,它确实很方便,因为 任何使用 stdio流进行 I/O 处理的程序(包括 CLI 工具)都可以部署为 OpenFaaS 函数。
提起隔离,我们有必要区分下函数 和 调用:
如果 经典 运行时类似于 CGI,那么这个运行时模式类似于后来的 FastCGI。运行时希望在 watchdog 后面有一个长期运行的 HTTP 服务器,而不是每次函数调用时创建新的进程。这本质上是 将 watchdog 组件变成反向代理:当容器启动时,反向代理 watchdog 也会创建一个监听在 8080 端口的轻量级 HTTP 服务器。然而,与 经典 watchdog 不同的是反向代理watchdog 只创建一次函数的进程,并将其当成(长期运行的)上游服务器。然后,函数调用转变成到该上游的 HTTP 请求。
然而,反向代理模式并不为了取代经典模式。经典模式的强项在于其函数的编写非常简单。这也是没有 HTTP 服务器的代码的唯一选择。比如使用 Cobol、bash 或者 PowerShell 脚本等等编写的函数。
何时该使用反向代理运行时模式:
根据 OpenFaaS 的创建者 Alex Ellis 的解释,FaaS,特别是 OpenFaaS,可以被视为在不依赖服务器抽象的情况下 部署微服务的简化方式。即 FaaS 是无服务器架构的规范示例。
因此,使用反向代理的方式,函数可以被看作是部署微服务的固执的方式。方便、快速、简单。但使用有状态函数时,要留意由于多个调用可能在同一个进程中结束而导致的警告:
wathdog 与 反向代理 watchdog 二者工作模式对比:
Openfaas 网关为自定义函数提供 RESTful 风格接口的外部路由,内置 UI、faas-cli 和 API 请求均会由 Openfaas 网关进行处理分发。
Openfaas 网关同时集成了 Prometheus 提供对函数和服务的监控能力,也可调取 faas-provider 的 API 实现对容器的管理。
Faas-provider 是一个使用 go 语言实现的符合 Openfaas provider Http REST API 标准的 SDK 工具集。
OpenFaaS API Gateway主要职责:
参考阅读:The power of interfaces in OpenFaaS (alexellis.io)
参考阅读:Autoscaling - OpenFaaS 、Scale to Zero and Back Again with OpenFaaS
下面两张图简要阐述了同步/异步调用 上传一份PDF文件函数 的调用过程,说明了如何通过NATS实现函数异步调用。
拓展阅读:
- 对话阿里云叔同:如何看待 2022 年云原生的发展,2023 年有哪些值得关注的技术? - 掘金 (juejin.cn)
- 加州伯克利 Serverless View(berkeley view)