0 摘要
-
在这篇论文中,我们解决了在边缘网络中支持基于函数即服务(FaaS)模型的有状态工作流的问题。
-
特别是我们关注数据传输问题,由于某些边缘场景中通信链路速度有限,这可能成为性能瓶颈。为此,我们提出了三种不同的方案:
- 纯FaaS实现
- StateProp(应用状态在整个函数链中传播)
- 以及StateLocal(状态仅保留在运行函数的工作节点上,需要时进行检索)。
-
然后,我们将提出的方案扩展到应用程序建模为有向无环图(DAG)的更一般情况,这种建模方法涵盖了广泛的实际应用场景,例如物联网(IoT)领域。
-
我们的贡献通过原型实现进行了验证。在模拟条件下的实验表明,应用数据本地化原则显著减少了所需的网络流量并改善了端到端的延迟性能,尤其是在边缘节点上进行本地缓存和低链路速度的情况下。
1 介绍
- 这篇论文中,我们针对有状态函数工作流中的数据传输问题(包括参数/返回值和应用状态)
- 以第2节中介绍的实际应用案例为动机进行了研究,
- 在回顾现有技术(第3节)之后,
- 我们在我们之前的工作中总结了研究结果(第4节),提出了三种完全去中心化的执行模型:PureFaaS、StateProp和StateLocal
- 第5节中,这些模型扩展到将应用程序建模到DAG的更一般情况
- 第6节中比较他们的性能
- 第7节中得出结论
2 动机:实际应用案例
-
一个用于智慧城市的实时分析框架,与几个高影响的应用相关
- 自动检测异常交通情况
- 检测拥挤区域
- 保护脆弱人群
- 公共事件中的情绪识别等
-
这些应用程序有着相似的总体结构
- 从传感器获取数据开始,首先进行匿名化处理以删除个人数据,然后提取相关特征并用于触发基于机器学习(ML)的决策过程。
- 此时普通的Serverless不能在这里采用,原因
-
- 无状态执行是不够的:一些组件需要对程序状态进行读写访问
-
- FaaS支持函数链,但这里工作流程为DAG
-
-
这些应用的状态可以保存在3个位置
-
- 本地客户端
-
- 边缘节点
-
- 云端
-
-
而对于这些可保存状态的文职,随着传感器获得数据大小的增加,3个位置时延的增加有所不同
- 云端时延最高,且随着状态的增大而显著增大
- 状态在边缘或者客户端维护时,没有明显增加,但事实需要更高的网络流量成本
-
综上:由DAG状态函数组成的数据密集型应用程序(例如实时智慧城市分析)的性能在很大程度上取决于状态的存储位置,而这也是本文研究的主题。
-
DAG状态应用程序并不是特殊情况,依据阿里巴巴数据中心的生产系统中收集的真实云应用程序的跟踪信息:
- 由DAG组成的占21.7%
- 单个任务28.6%
- 链占49.7%
3 相关工作
- 云中的无服务器平台依赖于底层容器编排系统,这些系统处理自动缩放并负责保持一致的性能。然而,当在边缘使用这些编排工具时,设备是异构且集群化的,这会导致低效的性能。
- 支持有状态应用程序是云中无服务器计算定位文件中确定的关键研究挑战之一
- 目前,边缘网络中的有状态FaaS在很大程度上尚未被探索,这是我们的动机,探索不同的方法来处理函数调用的链和DAG的参数和状态分配。在实际部署中,可以将复杂的状态管理系统与我们在本文中提出的执行模型结合使用。
- 之后再补充
4 有状态的函数链
- 图中共有四个边缘节点,表示为ni,每个节点都托管一个无服务器平台,可以通过一组工作节点执行一个或多个类型的Lambda函数,表示为λi。例如,n1可以执行Lambda函数λ1和λ4,但不能执行λ2和λ3。
- 考虑上图的示例用户应用程序
- 客户端需要将输出提供给λ1
- λ1需要应用状态SA,其输出out1需要提供λ2
- 以此类推,最终输出out4返回客户端
- 在云中,有状态函数通过访问外部服务(例如内存数据库或存储服务)来实现无状态函数。然而这种方法在边缘上并不高效,由此提出三种方法,下列方法中假设函数分配为:[λ1,λ2,λ3,λ4] → [n1,n4,n2,n1]。
- PureFaaS中,函数链中的函数一个接一个地执行,并且每次调用时都会来回传输每个函数所需的状态,如上图示,只要满足以下两个条件,就可以在商业/开源的无服务器平台上轻松实现此策略
-
- 函数的签名(包括参数和返回值)支持客户端嵌入所需的状态
-
- 客户端事先知道每个下一个被调用的函数所需的状态。
-
- StateProp中,它利用了大多数无服务器平台提供的链式功能,如上图示,客户端将应用程序的完整状态嵌入到函数的参数和返回值中:不使用嵌入状态的函数将简单地将其传递,而其他函数将将接收到的修改后的状态作为函数参数嵌入,并最终返回给客户端。
- StateLocal将状态保留在边缘节点中,如上图所示,不是将状态嵌入到函数调用中,而是仅传递指针。
- 当一个Lambda函数需要状态时,它通过指针检索状态,然后成为状态的新所有者,从而修改链中后续函数调用中的状态指针。
- 这样,客户端最终将返回所有状态的更新指针列表,以便在后续应用程序执行中使用它们,或者在需要时从边缘节点中撤回状态。
5 扩展到DAG
5.1 DAG特定符号表示
- 一组任务之间的关系:如果任务λi必须在任务λj之前执行,则存在边λi → λj。这些先行关系定义了一个有向图,称为任务依赖图,最后形成DAG
- 任务也可能依赖某些状态:态依赖图是一个无向图,其中边λi → Sx表示任务λj需要访问状态Sx。任务依赖图和状态依赖图的并集生成任务-状态依赖图;如上图示
-
在应用程序的单次执行过程中,可能会出现多个任务需要在同一状态上操作的情况,就像上图中的示例一样。根据应用程序的内部逻辑,可能会出现以下情况
-
- 执行顺序无关紧要
-
- λ2必须在λ3之前执行
-
- λ3必须在λ2之前执行
-
-
服务器无状态平台需要从应用程序中了解依赖于共享状态的任务的执行时间顺序,以保持状态的因果一致性。为此,我们通过以下方式来捕捉这种时间顺序,将任务-状态依赖图进行扩展:
- 如果λi和λj都使用相同的状态,并且λi必须在λj之前执行以确保共享状态的因果一致性,我们添加一个虚拟边λi → λj,虚线表示,如上图示。
-
添加虚拟边会影响可以实现的并行性:没有状态依赖关系的情况下,在上图中的DAG可以执行为{λ1, λ2 | λ3, λ4}(其中|表示两个任务可以并行执行)
- 但在下图中需要虚拟边λ4 → λ2,但这会创建循环λ2 → λ3 → λ4,从而导致死锁:λ4无法执行,直到它接收到λ3的输出,而λ3无法运行,直到它接收到λ2的输出,而λ2在λ4执行完成之前无法访问状态S.
- 由于应用程序可以检测到这种情况,并且这反映了逻辑设计问题,因此我们假设我们感兴趣的应用程序仅限于具有非循环的扩展任务-状态依赖图。
5.2 PureFaaS模型扩展
- PureFaaS在链式函数调用和DAG中的使用方式保持不变,除了可以并行执行一些任务外。类似于拓扑排序,下图为示例
5.3 StateProp/StateLocal模型扩展
-
StateProp和StateLocal都依赖于工作节点在当前函数完成后调用下一个函数;它们在状态管理方面有所区别:StateProp沿着函数调用链传递状态,而StateLocal将状态保留在最后使用它的边缘节点内部。
-
首先,一个任务可能有多个输入,比如λ2和λ3都希望在各自的任务结束后执行λ4,因此“每个函数执行下一个函数”的概念并不像链式函数那样明确定义。我们通过引入异步调用的概念来解决这个问题:当一个函数终止时,它总是调用下一个函数(根据DAG的直接后继),但只有在所有输入都可用时才触发任务的执行。(这里相当于拓朴排序)
- 如果情况不是这样,则前一个函数的输出暂时存储在边缘节点上,函数会暂停执行
- 上图中的模式,增加了边缘节点上服务器无状态平台的复杂性,因为它们必须为每个不完整的操作维护一个临时状态。
- 但这种异步调用本身并不能解决问题:当λ2和λ3不同时调用n4节点的λ4时,将发生死锁
- 如果情况不是这样,则前一个函数的输出暂时存储在边缘节点上,函数会暂停执行
-
因此,为了支持StateProp/StateLocal,至少在DAG应用的单个执行期间,所有工作节点都需要知道函数与边缘节点之间的映射关系。这样,我们可以确保所有工作节点将在相同的边缘节点上调用后续任务的执行,这将带来以下影响
-
- 函数与边缘节点之间的映射信息必须沿着执行DAG传递,这会略微增加协议开销
-
- 必须有一个能够在DAG被调用时“解析”所有函数的过程(例如,可以由客户端完成),这可能会增加启动延迟
-
-
但仍不够支持StateProp/StateLocal,如果λ2和λ3都依赖于相同的状态S。无论相对顺序如何,都需要将由第一个要执行的函数(例如λ2)修改的更新状态传递给另一个函数(例如λ3)。但是两者之间没有调用路径,即λ3不是DAG中λ2的后代,这使得仅仅依靠状态传播变得不可能。因此,提出第二个修改
- 不再将状态嵌入函数参数(或其引用,对于StateLocal而言)
- 而是每个访问状态的函数将其直接发送给下一个将使用它的工作节点,根据状态依赖图和因果一致性约束,这两者已经是已知的。
- 因此,函数的工作节点必须准备好不仅接收异步调用并临时存储其参数,还要存储更新的状态,这些状态也是异步到达的。
- 上图是同PureFaaS解决的依赖图中StateProp/StateLocal的顺序图,两者之间唯一的区别在于对于StateLocal,状态必须从最后的所有者中检索
-
上表为所有方案的流量交换
-
总结:StateProp和StateLocal可以支持DAG应用程序,但需要进行以下主要修改:
- 工作节点必须支持异步函数调用
- 在单个DAG执行期间,函数和边缘节点之间的绑定必须为所有工作节点所知,而状态不能随参数一起传播
-
这些变化增加了在边缘节点上运行的软件的复杂性和协议开销,并加剧了可能存在的有关披露专有信息的担忧
6 表现评估
6.1 执行
-
在ServerlessOnEdge框架中实现这三种方案
- 该框架是一个分布式的调度无状态FaaS函数的框架
- 有论文团队开发和维护
- 在github开源
-
在该框架中
- 客户端通过e路由器调用请求函数
- 同时e路由器在无服务平台上充当中间人的角色,将无状态请求转发到根据负载和网络条件可用的多个目的地之一
- 实现了e计算机,它可以模拟具有特定配置的无服务器平台,包括计算速度、内存、容器数量等
- ServerlessOnEdge使用谷歌的gRPC进行客户端、e路由器和e计算机之间的通信。
-
PureFaaS的实现
-
- 客户端在每个函数调用时将所需的状态嵌入参数中
-
- e计算机将嵌入的状态作为函数返回值的一部分返回
-
- 如果满足先决条件,会调用多个函数(仅适用于DAG)
-
-
StateProp的实现
-
- 实现异步调用:立即返回空的确认消息,而真正的输出由链中的最后一个e计算机作为主动回应消息提供给客户端
-
- 在每个e计算机上安装了一个附带的e路由器,用于调度由其e计算机生成的函数调用作为函数链的一部分执行,这样系统中的每个e计算机可以知道链中下一个函数的目的地
-
-
上图展示了StateProp的实现
- 图中共有5个边缘节点(主机1到主机5),调用两个函数链
- 客户端在主机0上,主机0上e路由器被客户端用于调用链中的第一个函数(λ1,转发到主机1)
- 主机1上的e路由器接收到对λ4的下一次调用(转发到主机4)
- 主机4上的e计算机不需要经过其附带的e路由器,因为它可以将最终响应发送给主机0上的客户端
-
StateLocal要求与StateProp相同,但还需要需他的一些升级
-
- 系统消息必须支持远程状态,即不直接嵌入函数调用/响应中的状态,而是通过它们的名称和端点间接引用
-
- 状态由称为状态服务器的新组件进行管理,它们是与每个e计算机和客户端共存的简单内存键值存储
-
- 消息流与StateProp完全相同,但在每个函数调用时,e计算机检索所需的远程状态,然后将其复制到本地状态服务器中;为了做到这一点,状态依赖性也嵌入在函数调用请求消息中。
-
6.2 结果
- 进行了两批实验,分别是函数链和有向无环图(DAG)
函数链:
- 实验条件:
- 客户端执行长度为L(3或6)的函数链
- 其中每个函数随机选择自λ1、…、λ5,并可能重复。
- 我们假设应用程序有S个状态(3或6),其中第i个状态的大小为(1+i)×10 kB(以0为基的索引);
- 每个状态依赖于随机选择的随机基数从0(无依赖关系)到L(链中的所有函数都依赖于该状态)。
- 输入参数和返回值的大小被假设为相同,并且等于A(10 kB或100 kB)。一组函数
- 如上图示,将在各个场景中使用不同执行模型获得的平均端到端延迟进行了比较,其中网络设备之间的链路速率从1 Mb/s增加到100 Mb/s。
- 在左上角的图中
- PureFaaS和StateProp几乎重叠:这是因为状态和参数的大小相对较小;
- 直到链路速率低于20 Mb/s,StateLocal具有更低的平均延迟,这要归功于其更智能的仅在需要时传输状态的方法
- 然而,随着链路速率的提高,优势逐渐减小,直到延迟高于50 Mb/s和100 Mb/s链路速率下PureFaaS/StateProp的延迟:在如此高的连接速率下,数据传输变得与(或高于)建立TCP连接来检索/更新状态的时间相当
- 在相反的情况,即右下角的图中
- StateProp和StateLocal的性能相当
- 除了在高链路速率下:这是因为链条比其他情景中的链条要短,数据传输由输入/输出参数主导,这两种方案的处理是一样的
- 右上图和左下图是中间状况
- StateLocal在所有较慢的链路速率下都实现了更好的性能
- 而PureFaaS始终位于StateProp的顶部。
- 在左上角的图中
- 总结:
- 对于函数链,考虑到实际的协议开销,根据状态大小和网络速度,存在将状态保持在边缘节点(StateLocal)和将其嵌入到函数调用中之间的权衡。
- StateProp的性能始终优于PureFaaS。
- 对比函数链时,三种模型中的平均网络流量,如上图,此时链路速度100Mb/s,时StateLocal表现最差的时候
- PureFaaS生成的流量始终大于StateProp生成的流量,而后者始终大于StateLocal生成的流量
- 对于函数链,即使在高网络速度下,StateLocal的开销(以所需的流量速率为衡量)明显低于其他方案,但这并不总是转化为更低的端到端延迟。
DAG
- 考虑由一系列阶段组成的应用程序,每个阶段都有一个分支函数,该函数生成多个无状态调用,然后是一个有状态的收集任务。这种结构在ML应用程序中非常典型
- 我们使用了3个阶段,每个阶段有5个分支。函数是从边缘节点中随机选择的,状态依赖关系也是随机的,使用与链式结构相同的方法。
- 在上图中,展示了平均延迟随链路速率从1 Mb/s增加到100 Mb/s的变化(对数-对数图),以及仅针对链路速率为100 Mb/s的网络流量
- 我们发现PureFaaS在所有情况下都优于StateProp,且在参数大小较小时优势更为突出。这是因为调用的总函数数量远远超过链式情况,这会严重影响将所有状态嵌入到调用和响应中。
- StateLocal不需要支付这样的代价,它只传递状态的引用,并在除了非常高的链路速率之外的所有情况下表现最好,这是由于状态检索/更新操作的开销
- 总结:对于具有大量函数的DAG,只有在函数调用和响应中携带引用时,状态传播才有效,即StateLocal更有效
7 结论
- 在本文中,我们探讨了在分布在边缘节点上的无服务器平台上支持有状态应用程序的问题。
- 我们重点关注在链式和DAG工作流中沿函数调用传递状态的问题,并提出了三种不同特性的替代方案。
- 我们开发了一个原型实现来验证我们的方法的可行性,并使用真实的协议开销来衡量性能。
- 结果表明,在函数调用链中传播状态可以显著减少通信开销。这可以降低端到端的应用程序延迟,尤其是在连接有限的情况下。
- 然而,对于大型DAG工作流,再嵌入状态以进行传播就不再有效了:在这些情况下,必须在边缘节点上本地存储状态并传递它们的引用。
专业术语
FaaS & Serverless
FaaS是一种云计算模型,其中开发者可以编写和部署独立的函数(代码块),并通过云服务提供商的平台执行这些函数。FaaS允许开发者根据需要执行函数,而无需管理底层的服务器和基础设施。每个函数都是独立的、无状态的,以事件驱动的方式触发执行。开发者只需关注函数的实现,而不用担心服务器的管理和扩展性。
Serverless是一种更广泛的概念,它是构建在FaaS模型之上的。Serverless强调开发者无需关心服务器和基础设施,只需专注于编写代码和业务逻辑。在Serverless架构中,云服务提供商负责自动扩展和管理底层的资源。开发者只需支付根据函数执行时间和资源消耗计算的费用,而无需事先预留或管理服务器。
FaaS和Serverless提供了更灵活、可扩展的开发和部署方式,使开发者能够更专注于核心业务逻辑而不必担心基础设施的细节。它们在构建微服务、事件驱动应用和处理短暂任务等场景中得到广泛应用,为开发者提供了高度可伸缩、成本效益高的解决方案。
无状态FaaS
- 无状态FaaS指的是函数即服务(FaaS)模型中的一种特性,即函数本身不存储任何状态信息。在无状态FaaS中,每个函数都是独立的、无状态的,它们被设计成接收输入参数并生成输出结果,而不会在函数之间保留任何状态信息。
- 这种设计有助于实现函数的高度可伸缩性和灵活性。由于函数本身不存储状态,它们可以独立地扩展和部署,而无需关心底层的服务器和基础设施。此外,无状态函数对于并行执行和容错性也更具优势,因为它们之间没有任何依赖或共享状态。
- 然而,无状态FaaS也意味着对于那些需要在函数之间共享和保持状态的应用程序来说,需要依赖外部服务(例如存储或数据库)来管理和存储状态信息。因此,无状态FaaS适用于那些可以通过函数的输入和输出进行完全描述的简单任务或短期计算,而对于复杂的应用程序或需要持久状态的任务,则可能需要其他机制来处理状态管理和共享。
K8s
- Kubernetes(通常简称为K8s)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。它提供了一组强大的功能和工具,帮助用户简化容器化应用程序的部署、扩展和运维工作。
- 以下是Kubernetes的一些主要特性和概念:
- 容器编排:Kubernetes可以管理和编排容器化的应用程序,使其能够高效地运行在集群中的多个节点上。它可以自动在集群中的节点之间调度容器,并确保应用程序的高可用性和负载均衡。
- 自动伸缩:Kubernetes允许根据应用程序的负载情况自动扩展或缩减应用程序的副本数量。这样可以根据实际需求来调整资源的使用,以保证应用程序的性能和可用性。
- 服务发现和负载均衡:Kubernetes提供了内建的服务发现机制,可以为应用程序提供稳定的网络地址。同时,它还可以通过负载均衡来分发流量,确保应用程序能够平均地处理请求。
- 配置和存储管理:Kubernetes提供了灵活的配置管理机制,可以将应用程序的配置信息与容器分离,使得配置的修改和更新更加方便。此外,Kubernetes还支持多种存储选项,包括持久化存储和临时存储。
- 自愈和自修复:Kubernetes具备自愈和自修复的能力,当应用程序出现故障或节点失效时,它可以自动重新启动容器、替换故障节点,并确保应用程序的正常运行。
- 扩展性和可插拔性:Kubernetes的设计具有良好的扩展性和可插拔性,可以根据需要添加新的功能和组件。它提供了丰富的API和扩展点,允许开发人员根据自己的需求进行定制和扩展。
- 总的来说,Kubernetes为容器化应用程序的部署、管理和运维提供了一套完整的解决方案。它可以帮助用户简化复杂的容器编排任务,并提供高可用性、弹性伸缩和自动化管理等功能,使得应用程序能够更加高效和可靠地运行在分布式环境中。