【云原生】Serverless 技术架构

Serverless 技术架构

一、什么是Serverless?

1、Serverless技术简介

image-20230628181023269

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 架构所具备的优势有:

  • 更快的开发速度:相较于IaaS和PaaS,基于Serverless技术的FaaS平台通过将传统应用运行过程中的客户业务代码逻辑部分抽象提炼出来(形成 用户函数代码 + BaaS的格局),开发者只需要按照云平台标准开发函数集合,开发者不需要关心底层使用什么框架——只需要开发业务逻辑;更不知道有多少个实例在运行、运行在哪里,这些都使得开发者能将更多的精力专注于业务代码。
  • 内建自动化部署:Serverless 架构通常通过在平台上提供的 API 以及各种工具,实现内建的自动化部署。一般来说,这些工具可以监听源代码仓库的变更,并在代码更新时自动部署到生产环境。在部署过程中,工具会把代码打包成一个或多个函数,并将其上传到 FaaS 平台,然后将其配置为适当的 URL 和事件触发器,以响应请求。这样,每次请求都会触发对应的函数执行,从而实现了代码的实时部署。
  • **减少资源开销:**函数在没有请求时,函数实例为0。当有请求时,云平台会快速拉起一个实例以承载流量。更重要的:函数可以按照请求计费,即调用一次就付费一次,而传统服务则是按照实例规格计费。

Serverless 架构的一些短板

  • **状态管理:**要想实现自由的缩放,无状态是必须的,而对于有状态的服务,使用serverless这就丧失了灵活性,有状态服务需要与存储交互就不可避免的增加了延迟和复杂性。
  • **计算环境高度标准化:**无服务计算应用运行所依托的底层服务器以及运行环境对用户是透明的,用户无法选择,也无法优化。无服务计算程序运行的环境是高度标准化的,有些依赖于特定运行环境、特定服务器版本、甚至特定硬件资源的依赖较难确保兼容性。
  • 本地测试:目前在使用Serverless架构的开发过程中还缺少比较成熟的调试和开发工具。
  • 冷启动耗时

2、Serverless关键技术

(1)事件驱动

​ **Serverless 的"运行才计算“的特性,便意味着他是一种 “严格” 的事件驱动式计算。**事件驱动程序设计这种设计模型是在交互程序(Interactive program)的情况下孕育而生的。这也意味着,系统在编程模型上有着巨大的改变。在我们编写 GUI 程序,如桌面程序、Web 前端应用,我们都通过监听用户对按钮、链接等组件操作,才开始相应的处理逻辑。这和 Serverless 是相似的,只在用户使用的时候,才会对应用户的行为进行响应。

(2)容器技术

​ 容器技术在 Serverless 架构中通常是通过 Docker 实现的。云服务提供商会将每个函数的代码打包到一个独立的 Docker 容器中。在容器内部,可以确保代码可以在隔离的环境中运行,并且不会影响其他函数或应用程序的运行。

​ 当请求到达时,云服务提供商会在容器内部执行函数,并在完成后销毁容器。这种做法有助于确保每个函数的执行是短暂的,并且不会占用过多资源,从而避免了资源浪费。

​ 通过使用容器技术,Serverless 架构可以确保函数在隔离的环境中运行,并且可以快速扩展,以满足增长的需求。同时,容器技术还可以提高应用程序的安全性,因为可以限制函数的权限,以防止函数访问不该访问的数据。

3、与常见技术架构对比

(1)传统架构 -VS- Serverless

​ 一个传统 Java Web应用的技术架构可能如下图所示:

image-20230206154800930

​ 这样的架构可以让前端十分轻便,不需要做什么应用逻辑,只是负责渲染用户界面,将请求通过HTTP发送给后端,而所有的数据操作都是有由后端的Java程序来完成的。这样的架构开发起来比较容易,但是维护起来确十分复杂,前端开发、后端的开发都需要十分专业的人员、环境的配置,还要有人专门维护数据库、应用的更新和升级。

image-20230206154903012

​ 而在serverless架构中,我们不再需要在服务器端代码中存储任何会话状态,而是直接将它们存储在NoSQL中,这样将使应用程序无状态,有助于弹性扩展。前端可以直接利用BaaS而减少后端的编码需求,这样架构的本质上是减少了应用程序开发的人力成本,降低了自己维护基础设施的风险,而且利用云的能力更便于扩展和快速迭代。

(2)MicroService -VS- Serverless

​ 微服务(MicroService)是软件架构领域业另一个热门的话题。如果说微服务是以专注于单一责任与功能的小型功能块为基础,利用模组化的方式组合出复杂的大型应用程序,那么我们还可以进一步认为Serverless架构可以提供一种更加“代码碎片化”的软件架构范式,我们称之为Function as a Services(FaaS)。而所谓的“函数”(Function)提供的是相比微服务更加细小的程序单元。例如,可以通过微服务代表为某个客户执行所有CRUD操作所需的代码,而FaaS中的“函数”可以代表客户所要执行的每个操作:创建、读取、更新,以及删除。当触发“创建账户”事件后,将通过AWS Lambda函数的方式执行相应的“函数”。从这一层意思来说,我们可以简单地将Serverless架构与FaaS概念等同起来。

​ Serverless 天生就与微服务架构是相辅相成的。一个 Serverless 应用拥有自己的网关、数据库、接口,你可还以使用自己喜欢的语言(受限于服务提供者)来开发服务。换句话来说,在这种情形下,一个 Serverless 可能是一个完美的微服务实例。

image-20230206134756696
(3)Serverful Cloud -VS- Serverless Cloud

传统云平台与Serverless平台三个比较关键的区别是:

  1. **解耦的计算和存储。**存储和计算分别扩展,并独立调配和定价。通常,存储由单独的云服务提供,计算是无状态的。
  2. **执行代码而不管理资源分配。**用户提供一段代码而不是请求资源,云自动提供资源来执行该代码。
  3. 按使用的资源而不是分配的资源的比例支付。计费是根据与执行相关的某个维度(如执行时间)进行的,而不是根据基础云平台的某一维度(如分配的VM的大小和数量)进行的。

​ 传统云平台与Serverless平台的其他技术细节的一些对比(以AWS为例):

image-20230207174034204

4、Serverless业界工程实践

(1)AWS Lambda :AWS Lambda云计算服务介绍_如何使用AWS Lambda-AWS云服务 (amazon.com)

二、FaaS

1、IaaS -> CaaS -> PaaS -> FaaS

image-20230207111003095

  • **IaaS (Infrastructure as a Service): 基础设施即服务。**是指提供基础设施(如计算、存储、网络等)作为服务,用户可以在此基础上搭建自己的应用环境。

  • **CaaS (Containers as a Service): 容器即服务。**是指将容器技术提供给用户使用,用户可以通过容器运行自己的应用。

  • **PaaS (Platform as a Service): 平台即服务。**是指提供开发平台和应用管理工具,用户只需要关注应用的开发和运行,不需要关注底层的基础设施。

  • **Serverless:无服务器架构。**是指不需要为了运行应用程序而显式管理和配置服务器资源,所有的底层管理和维护都由平台提供方负责。用户只需要上传自己的代码,等待代码被自动运行即可。 Serverless = FaaS + BaaS

  • FaaS (Function as a Service) 函数即服务: 作为一种云计算服务,它允许开发人员在云提供商处部署单个函数,而不是整个应用程序。云提供商在请求到达时自动执行函数,并在请求完成后自动销毁。而“FaaS”中 最关键的 "F"也就是函数是如何定义的呢?可以参考下面OpenFaaS项目创始人 Alex 对CloudFunction的定义:

    image-20230215180140379

2、FaaS关键技术

(1)弹性伸缩 (K8s 作为编排底座)

​ 调度是指将用户的应用选择合适的服务器进行部署,弹性伸缩是根据业务需求和策略自动调整计算能力(即实例数量)的服务。如函数实例当前为1,当函数的访问量上来后,弹性伸缩系统根据策略将函数实例数扩容到N,用来承载更多的请求;当系统容量够用,则适当的缩容部分实例。在传统的开发模式中,扩容和缩容都是相对繁琐流程。扩容需要申请服务器资源、环境配置、部署服务、绑定网关等;缩容则需要停止服务、从网关剔除等。

而这个过程恰好契合Kubernetes的核心能力,Kubernetes解决了基础设施的问题、解决了服务部署调度的问题,通过调整Deployment的副本数能解决服务实例调整的问题、通过接入HPA帮助我们实现实例的自动伸缩问题。因此,开源社区的Serverless/FaaS产品基本是基于k8s实现的,也正是因为k8s的发展,促进了开源社区的Serverless/FaaS产品的发展。

image-20230628180924633

关于弹性伸缩,除了Kubernetes的HPA外,社区内还有诸如事件驱动架构弹性框架Keda、以及KPA(Knative实现)等。如Keda,其解决的核心问题是在事件驱动架构下的弹性伸缩(事件触发扩缩容)与实例从0 -> 1、1->0的问题,Keda整体架构如下:

image-20230207163102070

(2)函数Runtime & 函数加载机制

​ 函数Runtime是可用于加载&运行函数的某个版本的编程语言或框架。比如Python 、Java、Golang运行时,他们各自负责加载以及运行语言相关的函数。对于函数如何加载到Runtime中,社区中有两个方向:

  • 将函数和Runtime编译成镜像。如OpenFaas在创建函数时会将其编译成镜像,然后使用Deployment部署。

    image-20230207153534039

  • Runtime在运行时将函数代码下载到本地,并使用编程语言提供的动态加载机制将函数加载到内存中。如Fission便采用该方法

    image-20230207154026312

(3)冷启动

冷启动是指函数从启动到能够提供服务的准备阶段。区别于传统的弹性伸缩服务,在FaaS中,如果函数没有请求调用,弹性伸缩 系统可以实例伸缩为0。当新的请求进来后,弹性伸缩系统则将实例数从0扩容至1。为了提供更好的用户体验,冷启动必须要在毫秒内,否则请求调用在感知上是有延迟的。冷启动时间主要由以下三个部分组成:

  • 云平台调度和启动资源以运行Cloud Functions。
  • 配置对应开发语言 Runtime(例如操作系统、语言运行时、依赖库等)下载并运行Cloud Function代码。
  • 执行特定应用程序启动任务,如加载和初始化数据结构和库。

​ 针对上面的Runtime的两种种实现方式,如果我们要对冷启动进行优化,优化方向是不一样的。对于OpenFaas采取的模式,都很难做到ms级,因此其官方对于敏感的函数,也是不推荐将实例数缩容到0的。在镜像部署函数的模式下,只能尽量减少Pod的启动时间,其优化方向如下:

  • 提高镜像拉取速度
  • 镜像预拉取
  • 减少镜像体积
  • 尽量使用异步函数

​ 但在Kubernetes作为技术底座 + 镜像部署函数的模式中,无论怎么优化,冷启动都还是比较大的。

如果不使用Kuberntes,目前工业界还有两个可选的方向,一个是基于KVM的轻量级虚拟化技术,如Firecracker;另一个则是Wasm(WebAssembly )AWS的Lambda底层技术就是使用了Firecracker,他的冷启动时间都是ms级的。Wasm则是一个比较新的技术,其本质是将各语言程序编译成一个特定的二进制格式,由Wasm Runtime来执行。早期Wasm二进制只能运行于浏览器,但是现在后端也有很多Wasm Runtime实现。目前在公开资料中,字节跳动有在FaaS中使用Wasm Runtime。

​ 若采取函数动态加载的形式,以Fission为例,Fission会为支持的每种编程语言创建一个Pod池,当某个函数需要调用时,则从语言相关的池子中选中一个Pod,在触发Pod中的Runtime容器下载函数并动态加载到内存中。这里的解决冷启动的核心思路是创建一个公共的warm Pod池。

(4)函数的调用/触发方式
  • 基于Web函数架构:其本质是通过HTTP方式来调用函数。这里核心在于Runtime需要提供一个Web框架和Web Server,请求进入Runtime后则调用用户函数。一般像公有云的产品都会在函数前绑定一个API网关,用于外部访问。开源社区的实现则一般都会有一个类似API网关的proxy来转发请求。如Fission有一个Router组件;OpenFaas则有网关。
  • 基于事件驱动架构:不管是社区还是公有云的产品,都有触发器的概念。所谓的触发器其实就是事件源,事件源可以由一个统一的Sidecar容器接收然后以HTTP调用函数;也可以由每个函数Runtime直接接收后调用函数。
  • **用户自定义的触发方式:**如OpenFaaS的名为Connector的组件可以让开发者自定义函数的触发方式。
(5)函数间调用、函数灰度以及函数治理

​ 目前社区的在这方面积累还是比较弱。其实,对于函数调用、灰度以及函数治理,我们可以抽象下,其本质不就是一个微服务架构吗?微服务架构下解决这些问题,尤其是在Kuberntes作为技术底座时,自然就想到了Service Mesh(如Istio),流量进出都走mesh sidecar,自然就实现了函数灰度、治理、调用等能力了。

image-20230207162824873

3、云原生 FaaS 框架

(1) OpenFunction

​ OpenFunction 是一个现代化的云原生 FaaS(函数即服务)框架,它引入了很多非常优秀的开源技术栈,包括 Knative、Tekton、Shipwright、Dapr、KEDA 等,这些技术栈为打造新一代开源函数计算平台提供了无限可能:

  • Shipwright 可以在函数构建的过程中让用户自由选择和切换镜像构建的工具,并对其进行抽象,提供了统一的 API;
  • Knative 提供了优秀的同步函数运行时,具有强大的自动伸缩能力;
  • KEDA 可以基于更多类型的指标来自动伸缩,更加灵活;
  • Dapr 可以将不同应用的通用能力进行抽象,减轻开发分布式应用的工作量。
image-20230207154715227
(2) OpenFaaS

​ OpenFaaS是一个发展比较久的FaaS开源项目,在GitHub上获得了比较多的关注,它在CloudFunction运行时处理请求的模式方面作了较多的探索。在本篇调研的第三节FaaS实践中着重介绍。

image-20230210160110697

三、FaaS实践 - OpenFaaS

1、概览

(1)是什么

​ OpenFaaS 是一个开源的 FaaS 框架,可以让用户在其上部署和运行自己的函数。OpenFaaS 使用 Docker 容器作为底层技术,可以运行在任何云环境或者私有云上。OpenFaaS 项目旨在将 Kubernetes 集群或者独立的虚拟机等低级基础设施转化为管理无服务器函数的高级平台。

(2)做了什么

image-20230210145258300

  • 函数调起WatchDog 是OpenFaaS用于调起和监控 Cloud Function 的关键实现,其充当运行函数和微服务的反向代理。它可以独立使用,也可以作为OpenFaaS容器的EntryPoint,并作为"Init Process" 在函数容器中启动。其内置了一个使用Golang实现的 HttpServer,提供并发请求、超时和运行状况检查等功能。其通过加载各种环境变量让开发者可以自定义其功能。
  • **自动伸缩:**OpenFaaS使用自主设计的 AutoScaler 进行函数的自动伸缩。
  • **冷启动:**OpenFaaS 在架构设计中有侧重,OpenFaaS 没有维护冷启动池然后按需加载业务代码这种机制,它希望保持只读模式的文件系统,容器载体被设计成不可变的单元来获得可预测的生命周期和获取最大程度的安全性。所以 OpenFaaS 在无流量情况下没有将函数实例数默认缩零,只是作为一个可选配置针对函数粒度进行开启,整体的冷启动数据流量期望通过最小的实例来进行承载,最大程度地规避拉起一个新实例所需要的资源调度、二进制拉取、进程启动、健康检测这些流程产生的巨大冷启动开销。
(3)怎么用

​ Overview - OpenFaaS

​ OpenFaaS 通过helm 在原生K8s部署

2、关键技术构成

OpenFaaS 技术概览图:

image-20230210161252264

上图是 OpenFaaS 的抽象服务流程,下面是各个节点的简单介绍:

Gateway: HTTP 网关,用于接收用户请求及内部指令。

NATS Streaming: 用于异步执行函数。

Prometheus/ AlertManager: 用于收集服务指标及扩缩容操作。

faas-netes: 针对 K8S 的 Provider,可以定制其他的 Provider 例如 Docker Swarm 等。

Docker Registry: 用于拉取函数镜像的仓库。

(1)WatchDog

WatchDog 是OpenFaaS用于调起和监控 Cloud Function 的关键实现,最新的of-watchdog充当运行函数或微服务的反向代理。它可以独立使用,也可以作为OpenFaaS容器的EntryPoint,并作为"Init Process" 在函数容器中启动。其内置了一个使用Golang实现的 HttpServer,提供并发请求、超时和运行状况检查等功能。其通过加载各种环境变量让开发者可以自定义其功能。 OpenFaaS先后推出了 ClassicWatchdog 以及 of - Watchdog两种 Watchdog模式。下面分别介绍:

  • 经典 WatchDog 工作模式:
image-20230210155449722

这种模式下,watchdog 启动了监听在 8080 端口的轻量级 HTTP 服务器,每个进来的请求都会:

  1. 读取请求头和请求体
  2. fork 或者 exec 包含实际函数的可执行文件
  3. 将请求头和请求体写入到函数进程的 stdin
  4. 等待函数进程的退出(或者超时)
  5. 读取函数进程的 stdoutstderr
  6. 在 HTTP 响应中将去读的字节发送回调用方

上述逻辑类似于传统的 通用网关接口(CGI)。一方面,每次函数调用都启动单独的进程看起来不够高效,而另一方面,它确实很方便,因为 任何使用 stdio流进行 I/O 处理的程序(包括 CLI 工具)都可以部署为 OpenFaaS 函数

提起隔离,我们有必要区分下函数调用

  1. OpenFaaS 中的不同函数始终分布在不同的容器中
  2. 一个函数可以有一个或多个容器 —— 取决于缩放选项
  3. 同一函数的独立调用可能会最终进入同一个容器
  4. 同一函数的独立调用将始终使用不同的进程进行
  • 反向代理 Watchdog 工作模式:
image-20230210161538305

​ 如果 经典 运行时类似于 CGI,那么这个运行时模式类似于后来的 FastCGI。运行时希望在 watchdog 后面有一个长期运行的 HTTP 服务器,而不是每次函数调用时创建新的进程。这本质上是 将 watchdog 组件变成反向代理:当容器启动时,反向代理 watchdog 也会创建一个监听在 8080 端口的轻量级 HTTP 服务器。然而,与 经典 watchdog 不同的是反向代理watchdog 只创建一次函数的进程,并将其当成(长期运行的)上游服务器。然后,函数调用转变成到该上游的 HTTP 请求。

然而,反向代理模式并不为了取代经典模式。经典模式的强项在于其函数的编写非常简单。这也是没有 HTTP 服务器的代码的唯一选择。比如使用 Cobol、bash 或者 PowerShell 脚本等等编写的函数。

何时该使用反向代理运行时模式:

  • 函数需要在两次调用之间保持状态:
    • 缓存
    • 持久连接(例如,保持从函数到数据库的连接打开)
    • 有状态函数
  • 每个函数启动一个进程可能开销很大,为每个调用带来了延迟
  • 你想运行一个(微)服务作为函数

根据 OpenFaaS 的创建者 Alex Ellis 的解释,FaaS,特别是 OpenFaaS,可以被视为在不依赖服务器抽象的情况下 部署微服务的简化方式。即 FaaS 是无服务器架构的规范示例。

因此,使用反向代理的方式,函数可以被看作是部署微服务的固执的方式。方便、快速、简单。但使用有状态函数时,要留意由于多个调用可能在同一个进程中结束而导致的警告:

  • 在一个进程中结束的并发调用可能会触发代码中的竞争条件(例如,一个带有全局变量的 Go 函数,而全局变量没有锁的保护)。
  • 在一个进程中结束的后续调用可能会导致交叉调用数据泄露(当然,就像传统微服务一样)。
  • 由于该进程在两次调用之间被复用,因此代码中的任何内存泄漏都不会被释放。

wathdog 与 反向代理 watchdog 二者工作模式对比:

image-20230209164843629

(2)APIGateway

image-20230210171540735

Openfaas 网关为自定义函数提供 RESTful 风格接口的外部路由,内置 UI、faas-cli 和 API 请求均会由 Openfaas 网关进行处理分发。

Openfaas 网关同时集成了 Prometheus 提供对函数和服务的监控能力,也可调取 faas-provider 的 API 实现对容器的管理。

Faas-provider 是一个使用 go 语言实现的符合 Openfaas provider Http REST API 标准的 SDK 工具集。

OpenFaaS API Gateway主要职责:

  • 对函数的 CRUD 操作
  • 代理函数调用服务
  • 管理函数容器的扩缩容
  • 容器密钥管理
  • 日志引流
(3)FaaS-Provider模型 / faas-nets

​ 参考阅读:The power of interfaces in OpenFaaS (alexellis.io)

(3)Auto-Scaling

​ 参考阅读:Autoscaling - OpenFaaS 、Scale to Zero and Back Again with OpenFaaS

(4)基于NATS 的异步函数调用

​ 下面两张图简要阐述了同步/异步调用 上传一份PDF文件函数 的调用过程,说明了如何通过NATS实现函数异步调用。

image-20230210174824986image-20230210174841234

3、其他

(1)开发者视角

image-20230210170127630
(本文部分图片来源于网络,侵删请联系。)

拓展阅读:

​ 对话阿里云叔同:如何看待 2022 年云原生的发展,2023 年有哪些值得关注的技术? - 掘金 (juejin.cn)

伯克利Serverless View (berkeley view)文章来源地址https://uudwc.com/A/Z4DzJ

原文地址:https://blog.csdn.net/qq_44683653/article/details/131442752

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请联系站长进行投诉反馈,一经查实,立即删除!

h
上一篇 2023年06月28日 21:35
智能佳—LoCoBot WX250 6自由度
下一篇 2023年06月28日 21:35