找回密码
 会员注册
查看: 12|回复: 0

WebAssembly在ByteFaaS中的应用_UTF_8

[复制链接]

2万

主题

0

回帖

7万

积分

超级版主

积分
75133
发表于 2024-9-30 07:06:43 | 显示全部楼层 |阅读模式
1. 前言近年来,无论是 Serverless 还是 WebAssembly,都越来越受到开发者的广泛关注,作为字节跳动内部的函数计算平台,ByteFaaS 在 WebAssembly 方向上也有着不少的探索和实践。我们利用 WebAssembly 技术构建出了极致轻量化的函数运行时,并辅以全新设计的精简架构,打造出了云边一体的 Serverless 解决方案,拓展了 FaaS 的边界和应用场景,为业务带去了更多的可能。本文将先带领大家认识 ByteFaaS 平台,分别介绍经典 FaaS 和 FaaS Worker(轻量级函数)方向。随后通过 WebAssembly 运行时、精简架构、开发者支持三个方面,详细介绍 FaaS Worker 的设计与实现。最后在文章结尾,会对 WebAssembly 函数能够为用户所带来的收益进行大致总结。2. ByteFaaS 平台介绍ByteFaaS 是字节跳动内部的函数计算平台,遵循事件驱动和服务函数化的理念,在整体产品形态上与公有云厂商的 FaaS 产品相近,提供了包括服务函数化、自动扩缩容、多种事件源支持、多种开发语言支持、服务高可用等平台能力。ByteFaaS 平台为用户屏蔽了资源和运维细节,并提供日志查询、性能监控和报警等功能,极大降低了开发者的开发运维成本。截止 2022 年中旬,ByteFaaS 平台上线函数 10w+,日均活跃函数 1.9w+,日均发布函数次数 7000+,在线流量高峰约 70w QPS,消息触发器流量高峰 1.2 亿 QPS,调用量和计算资源规模在业界处于全球领先水平。有关 ByteFaaS 函数计算平台的更多信息,可参考 ByteFaaS 函数计算团队出品的图书《Serverless 核心技术和大规模实践》[1]。在函数运行时和数据面架构方面,ByteFaaS 大致包含两大类,一类是基于容器或虚拟机隔离技术的,我们称作经典 FaaS,另一类则是采用进程内隔离方式的,如 WebAssembly,称作轻量级函数。2.1 经典 FaaS通常在函数计算平台中,每个函数实例是一个虚拟机或者容器,函数进程运行在函数实例中,与此同时数据面架构也围绕着虚拟机和容器这两种隔离技术设计。经典 FaaS 运行时的优势是符合用户一直以来的开发思维,并且在运行时层面几乎没有限制,在其他环境(例如在物理机、PaaS 平台上)可以执行的代码,稍加改动甚至不做修改,就可以移植到 FaaS 平台上运行,整体迁移的成本比较低。尤其是在 ByteFaaS 推出了 FaaS Native 方案后,现有应用迁移至 FaaS 的成本可以忽略不计。不过经典 FaaS 运行时的劣势也比较明显,主要集中在如下几个方面。函数实例的启动时间相对比较长,虽然通过预热或快照等方式可以大幅度降低冷启动时延,达到百毫秒级别,但对于时延敏感的应用,依然需要通过预留实例等手段尽量绕过冷启动,用资源换取时间。单个函数实例需要独占一个虚拟机或容器实例,资源开销相对较高,进而导致成本较高。尤其是对于那些代码简单且流量小的函数,预留实例会造成资源浪费,不预留实例则会导致函数调用时延抖动。实例扩容可能带来额外的函数调用时延开销,突发流量下的函数时延不稳定。对于边缘等资源非常受限的场景,有限的资源总量无法支撑平台使用虚拟机或者容器运行函数实例。2.2 轻量级函数随着经典 FaaS 在实际业务中的落地实践增多,单一架构和技术路线无法很好地支撑所有应用场景,FaaS 需要针对业务特点进行定向优化,给出特定的解决方案。针对经典 FaaS 的固有问题,ByteFaaS 尝试采用进程内隔离的方式来解决,构建出了冷启动速度极快且资源开销极低的函数执行方案,称其为轻量级函数。下图对比了虚拟机隔离、容器隔离和进程内隔离这三类隔离方式。图 1. 虚拟机隔离、容器隔离和进程内隔离对比虚拟机和容器是经过长期生产验证的、非常优秀的隔离技术,它们兼容性好,可以支持几乎任何原生二进制可执行程序,但是由于隔离是在虚拟机级别和进程级别,函数实例中除了用户代码外,有相当一部分内容是重复的,操作系统和运行时部分被复制了多次,也因此带来了资源开销高和启动速度慢的问题。而采用进程内隔离时,用户代码可以运行在同一个函数运行时进程内,共享函数运行时,极大程度地复用了函数实例的公共部分。正是由于进程内隔离方案将函数公共部分最大限度地从函数实例中剥离出去,使得函数实例变得极度精简,从而能够实现亚毫秒级别的函数冷启动速度以及低至数百 KB 的函数实例内存开销,单个函数运行时进程内可同时运行成百上千个函数实例。ByteFaaS 采用 WebAssembly 技术打造了上述的轻量级函数运行时,也为轻量级函数运行时设计了新的轻量级数据面架构,实现了极致短的冷启动时间,函数高密部署,极致成本优化,并能有效应对流量突增陡降。同时也可将 FaaS 带去边缘等资源受限的应用场景。我们将 ByteFaaS 的轻量级函数运行时和架构统称为 ByteFaaS Worker。3. WebAssembly 函数运行时起初 WebAssembly 诞生于浏览器环境,作为 JavaScript 的补充,尝试解决大型 Web 应用执行速度慢的问题。后来随着 WebAssembly 标准的演进以及各种 Standalone WebAssembly Runtime 的开源,WebAssembly 也开始在服务端场景中崭露头角。ByteFaaS 利用 WebAssembly 技术实现了一种采用进程内隔离机制的轻量级函数运行时,将函数实例冷启动时间缩短至亚毫秒级,并提供了多种常用编程语言的 SDK,降低用户的学习接入成本,辅助用户快速开发 WebAssembly 函数。3.1 WebAssembly 简介WebAssembly 是一种可运行在现代网络浏览器中的新型代码,它的设计目的不是为了发明一种新的编程语言,而是为如 C、C++ 和 Rust 等语言提供一个高效的编译目标。它在安全的、可移植、轻量化、高效率的虚拟机沙箱中执行,并且可以在不同平台上实现接近本地的运行速度。如下图,各种语言的代码通过编译器编译成 WebAssembly 模块,编译好的 WebAssembly 模块可以在各平台上的 WebAssembly 虚拟机中运行。图 2. WebAssembly 支持多语言多架构WebAssembly 天然的轻量、安全、快速、可移植等特性与 FaaS 的需求非常契合,可以帮助 FaaS 实现极致的轻量化和极致的冷启动速度。而对于 ByteFaaS 用户而言,采用一种可以编译至 WebAssembly 的语言编写函数代码即可,不会引入过多的学习成本,理论上所有基于 LLVM 架构的高级语言都可以编译到 WebAssembly。3.2 Hostcall + WASI业界通常把执行 WebAssembly 字节码的沙箱环境称作 WebAssembly VM,运行 WebAssembly VM 的进程被称为 Host 进程,VM 中执行的代码被称作 Guest 程序。由于 WebAssembly 本身只是一种可执行格式,Guest 程序自身只能完成纯计算操作,想要拓展 Guest 的能力范围,就需要 Host 提供相应的接口出来供 Guest 调用,这些接口统称为 Hostcall。虽然 WebAssembly 最初诞生于浏览器环境,但是 WebAssembly 技术有着上述简介中的执行快速、安全等优点,渐渐地开发者尝试将它运用在非 Web 环境。而在非 Web 环境中应用 WebAssembly 就需要解决其和外界交互的问题,此时 WASI 就应运而生了。WASI 为 WebAssembly 程序用 Hostcall 的方式提供了一套系统 API,封装了诸多例如读写文件、读取环境变量、写标准输出等接口,并且支持运行时对 WASI 接口进行严格的权限控制,相当于为 WebAssembly 提供了类似 Linux Syscall 的接口能力。例如,想要在 WebAssembly 程序中读取环境变量,则可以调用 envrion_get() 和 environ_sizes_get() 接口从 Host 侧读取到当前执行环境的环境变量信息,具体如下://读取环境变量的值//缓存空间的大小需要和`environ_sizes_get`返回的大小对齐pubfnenviron_get(arg0:i32,arg1:i32)->i32;//返回对应环境变量的数据长度pubfnenviron_sizes_get(arg0:i32,arg1:i32)->i32;通常情况下,WASI 接口会被用户所使用语言的标准库所调用,在开发者编写代码时,使用语言标准库中相对应的接口即可,无需关系其背后所调用到的 Hostcall 接口。例如,使用 Rust 语言开发 WebAssembly 程序时,使用 std::env 包中的环境变量 API 即可实现环境变量的读取,具体如下:usestd::env;letkey="HOME";matchenv::var(key){Ok(val)=>println!("{}:{:}",key,val),Err(e)=>println!("couldn'tinterpret{}:{}",key,e),}在 ByteFaaS 平台上,WebAssembly 函数运行时支持标准的 WASI ABI,用户可使用任意语言编写代码,编译出符合 WASI 规范的 WebAssembly Module,即可在 FaaS 平台上运行。3.3 运行时能力拓展由于业务的复杂性,仅有 WASI 接口是不够的。比如仅仅基于 WASI 较难复用传统函数下常用的一些操作,比如在函数内发起 HTTP / RPC 请求、连接并操作 DB 或消费 MQ 事件等。在这个背景下,ByteFaaS 与服务框架团队合作,参考 io_uring 的实现在 WebAssembly 内部定义了一套通用的异步 Hostcall 框架 BytedRing,并基于这套框架在 Host 侧注入各种上层实现,从而轻松地拓展了运行时能力。BytedRing Hostcall 框架提供了 4 个核心接口,定义如下://提交一个hostcalleventfnsubmit(event_id:i32,service_provider_id:i32,data_ptr:i32,data_len:i32)//非阻塞查询某个hostcallevent返回状态fnpoll(event_id_ptr:i32,size_ptr:i32)//阻塞等待某个hostcallevent返回fnwait(event_id_ptr:i32,size_ptr:i32)//读取id=event_id的hostcallevent返回的payloadfnread(event_id:i32,service_provider_id:i32,data_ptr:i32)系统架构示意如下图所示:图 3. BytedRing 交互流程设计当然,开发者一般情况下不需要直接操作这些基础的 Hostcall,ByteFaaS Worker 执行环境在此基础上封装了公司内部开发需求常用的能力,包括 HTTP Client/Server、TTHeader Client/Server等等,开发者只需要在项目内引入对应语言的 SDK 即可轻松调用相关 Hostcall,具体用法可以参考开发者支持章节。除上述基础能力之外,目前 ByteFaaS 也在与 Mesh 团队共建设计 ByteRuntime。与 Dapr 提供的能力类似,ByteRuntime 与公司内部自研组件相结合,支持以 Mesh Sidecar 的形式为函数运行时提供更加丰富的能力,包括资源绑定、状态存储、API 配置等。3.4 函数开发 SDKWebAssembly 函数运行时通过拓展 Hostcall 接口的方式丰富了运行时能力,但这些接口并不适合直接向用户暴露。如果直接向用户提供 Hostcall 接口,一来给用户增加了额外的学习成本,难以上手,二来由于 WebAssembly Hostcall 接口只支持传递数字,会涉及大量的指针传递操作,使用起来非常复杂,开发体验极差。为了方便用户使用,ByteFaaS 为常见的开发语言提供了相应的 SDK,如下图所示,用户只需选择某个自己熟悉的语言,使用简单的编程接口编写自己的函数。目前 WebAssembly 函数已支持 5 种常用语言,Rust、Go、JavaScript、AssemblyScript、C++。图 4. ByteFaaS WebAssembly Hostcall 交互流程关于 SDK 的使用会在本文后续的开发者支持章节详细展开。4. 精简架构由于现有的经典 FaaS 架构相对比较复杂,调用链路长,不利于发挥轻量级函数运行时的优势,因此 ByteFaaS 设计了一套精简架构,以实现极致的启动速度和极低的资源开销,支持在中心机房、汇聚机房和边缘机房部署。4.1 请求路径经典 FaaS 的请求路径相对较复杂,请求可能需要经过多个系统组件处理,会增加额外的时间开销。如下图,一个典型的冷启动请求需要经历以下几个阶段:请求到达 FaaS GatewayFaaS Gateway 向 Dispatcher 组件获取函数实例信息,Dispatcher 组件负责管理所有函数实例Dispatcher 组件发现函数实例不存在时,向 Worker Manager 组件获取新的函数实例Worker Manager 从资源池中获取空闲函数实例,触发代码下载,初始化函数实例返回新启动函数实例信息给 Dispatcher,再返回给 GatewayGateway 访问新启动的函数实例图 5. 经典 FaaS 函数请求路径而在精简架构中,由于数据面被大幅度简化,仅保留了关键组件,最大程度地降低架构复杂度,同时也能最大限度地减少数据面路径给请求带来的时间开销。如下图所示,在精简架构数据面中,用户请求首先到达 Gateway,Gateway负责请求调度并直接将请求发送给相应的函数运行时进程,触发相应函数的执行,整个函数调用几乎没有额外的系统内部开销。图 6. FaaS Worker 函数请求路径4.2 流量调度常见的流量调度策略包括轮询调度(Round Robin Upstream)、随机调度(Random Upstream)、带附加权重(Weighted Round Robin)的轮询调度等。通过设计合理的调度策略系统,可以实现在保证稳定性的同时提升资源利用率,具体如:在函数稳态的前提下尽可能复用已有实例,减少不必要的实例冷启;提升 Worker 实例资源利用率,在 Worker 实例健康的前提下尽可能多的承载函数请求;在 QPS 激增时能够快速扩容,减小/杜绝因此导致的时延上升、请求超时的问题;FaaS Worker 整体采取了带闭环反馈的 Weighted Round Robin 策略,为每个租户函数维护加权路由表,并且根据系统链路监的指标实时调整加权路由表,从而较好地实现上述调度目标。流量调度系统主要由三大组件组成:Gateway 网关,也即策略调度执行器:将请求实时 Upstream 到合适的 Worker 中执行,同时也会统计上报各个函数端到端时延、并发数等指标;实例集群:除执行用户函数外也会实时上报资源负载等信息;反馈控制器:生成并实时根据反馈指标动态调整调度策略;图 7. FaaS Worker 流量调度除上述功能之外,对于用户隐私、数据安全、稳定性以及 SLA 有特殊要求的租户,整个流量调度系统也支持租户隔离资源池的功能。资源池支持为租户划分 Worker 实例资源池,并保证资源池内的 Worker 实例仅用于处理租户的函数,通过容器或者虚拟机级别的隔离满足相关需求。4.3 冷启动优化轻量级函数冷启动请求会经历如下图所示的几个阶段:函数请求到达 GatewayGateway 向控制面查询相对应的函数信息Gateway 转发函数请求到函数运行时实例下载函数代码包,保存在本地加载函数,初始化函数实例执行函数图 8. 优化前冷启动步骤其中,第 2 步可以通过在 Gateway 中缓存函数元数据信息跳过,第 4 步可以通过函数代码包预分发跳过,通过这两步优化,去除了函数冷启动请求中对内外部服务或组件的依赖。经过优化后的冷启动请求路径如下:函数请求到达GatewayGateway 查询内存中的函数元数据缓存,并转发函数请求到运行时实例加载函数,初始化函数实例执行函数图 9. 优化后冷启动步骤另外对于 WebAssembly 函数运行时而言,可以使用 WASM 的 AOT 模式,在函数发布阶段将 WebAssembly Module 进行预编译,在函数加载阶段就可以跳过 WebAssembly 的编译过程,加快函数 Module 的加载速度。以 Hello World 函数为例,展示目前 WebAssembly 函数的冷启动开销。~curl-vhttps://function-id.fn.example.com......>GET/HTTP/2>Host:function-id.fn.example.com>user-agent:curl/7.77.0>accept:*/*>*Connectionstatechanged(MAX_CONCURRENT_STREAMS==128)!{returnnewResponse("HelloWorld",{headers:{'content-type':'text/plain'},});}run(handler)完成编写源代码后,通过熟悉的构建工具(例如esbuild或swc)将源码打包构建到 /handler.js 后直接发布即可。JS WebAssembly 上手非常简单,本地开发不需要 WASM 编译工具,初学者甚至不需要了解 WebAssembly 相关背景知识就可以上手。5.2 在线预览工具为了提升开发体验,快速看到代码改动效果,ByteFaaS 针对 WebAssembly 函数提供了快速在线预览本地代码的功能,一条命令即可将本地项目部署上线并生成临时调用链接,同时还可实时输出函数日志。预览工具使用非常简单,在函数项目目录下使用 ByteFaaS 命令行工具执行 bytefaas playground 子命令即可开启实时预览,并输出实时日志:mzpat91dbytefaasplayground->ackinglocalcode...->Sendingtoplayground...->GeneratedplaygroundinvokeURL:https://d3d000cfccbc1401006d7a706174393164.fn.example.com->Startinglocalproxyserver:http://127.0.0.1:8000->Connectingtorealtimelogs...[2021-06-2116:54:14+0800CST]STDOUT-[log]HelloPlayground!(0236091a-f8de-4260-b75b-c16978c41868)命令会输出预览调用 URL,每次预览时会生成不同的 URL 并启动一个本地代理,可根据需要选择访问预览调用 URL 或本地代理地址。5.3 CPU Profiling 工具除常见的开发支持外,在线上出现 CPU 热点故障 / 资源利用率异常等疑难杂症时,我们往往希望能够通过抓取 CPU Profile 的方式来排查代码中可能出现的性能问题。ByteFaaS 与 ByteDog 团队合作,提供了 CPU Profiling 接口,支持按百分比分流部分小流量到开启了 WebAssembly JitDump 的特定的独占实例下,与此同时触发 ByteDog 对采样实例开启 CPU Profiling。整个系统调用过程示意如下:图 12. WASM CPU Profiling 过程抓取完成之后可以在 ByteDog 平台看到包含 WebAssembly 调用栈的火焰图,由此可以方便分析性能损耗的位置。抓取的火焰图示意如下:图 13. WASM CPU 火焰图6. 总结本文首先简要介绍了 ByteFaaS 函数计算平台,从经典 FaaS 的常见问题出发,提出轻量级函数的思路,引出了函数冷启动时间极短、资源开销极低的 ByteFaaS Worker 方案。随后从 WebAssembly 函数运行时、精简架构、开发者支持三个角度详细描述了 ByteFaaS Worker 方向。WebAssembly 函数运行时章节,讲解了 ByteFaaS 如何基于 WebAssembly 打造出适合 FaaS 场景使用的函数运行时和配套的 SDK。精简架构章节,阐述了 ByteFaaS 在架构层面针对 WebAssembly 函数运行时所做出的各项开发和优化工作。开发者支持部分,通过最常用的 Golang 和 JavaScript 语言展示了如何快速开发 WebAssembly 函数,以及两个方便用户开发的辅助工具。用户使用 WebAssembly 函数,可以获得以下收益:得益于 WASM 函数的快速启动特性,函数实例能够在
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-15 21:09 , Processed in 0.496664 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表