WebAssembly 在 MOSN 中的实践 - 基础框架篇

简介: 本文将介绍 WebAssembly 技术在 MOSN 中的实践,首先介绍了当前 MOSN 在扩展隔离方面所面临的痛点,并对 Wasm 技术的相关背景知识进行介绍。
作者:叶永杰
来源:金融级分布式架构公众号
本文将介绍 WebAssembly 技术在 MOSN 中的实践,首先介绍了当前 MOSN 在扩展隔离方面所面临的痛点,并对 Wasm 技术的相关背景知识进行介绍。随后描述了Wasm 扩展框架的整体架构,并介绍了我们在 Proxy-Wasm 社区规范中所做的贡献,最后描述了框架在性能、异常调试等方面的实践内容。

作为金融级服务网格中的流量代理组件,MOSN 在承载蚂蚁数十万服务容器之间流量的同时,也承载着诸多例如限流、鉴权、路由等中间件基础能力。这些能力以不同的扩展形式与 MOSN 运行于同一进程内。非隔离的运行方式在保障性能的同时,却也给 MOSN 带来了不可预知的安全风险。

针对上述问题,我们采用 WebAssembly(Wasm) 技术,给 MOSN 实现了一个安全隔离的沙箱环境,让扩展程序能够运行在隔离沙箱之中,并对其资源、能力进行严格限制,使程序故障止步于沙箱,从而实现安全隔离的目标。本文将着重叙述 MOSN 中的 Wasm 扩展框架,并介绍我们在 Proxy-Wasm 这一开源规范上的贡献。

总体设计

image.png

上图为 MOSN Wasm 扩展框架的整体示意图。如图所示,对于 MOSN 的任意扩展点(Codec、NetworkFilter、StreamFilter 等),用户均能够通过 Wasm 扩展框架,以隔离沙箱的形式运行自定义的扩展代码。而 MOSN 与 Wasm 扩展代码之间的交互,则是通过 Proxy-Wasm 标准 ABI 来完成的。

隔离沙箱

当我们在讨论 Wasm 时,都明白 Wasm 能够提供一个安全隔离的沙箱环境,但并不是每个人都了解 Wasm 实现隔离沙箱的技术原理。这时又要搬出计算机科学中的至理名言: “计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决”。Wasm 实际上也是通过引用一个“中间层”来实现的安全隔离。简单来说,Wasm 通过一个运行时 (Runtime) 来运行 Wasm 沙箱扩展,每个 Wasm 沙箱都有其独立的线性内存空间和一组导入/导出模块。

image.png

一方面,每个 Wasm 沙箱都有其独立的线性内存空间,其内存模型如上图所示。Wasm 代码只能通过简单的 load/store 等指令访问线性内存空间的有限部分,并通过符号(下标)的方式来间接访问函数、全局变量等。上述限制杜绝了类似 C 语言中访问任意内存地址的骚操作。同时,用于间接调用函数的符号表对于 Wasm 代码而言是只读的,保证了 Wasm 代码的执行是受控的。此外,Wasm 沙箱的整个线性内存空间由宿主机 (Wasm Runtime) 分配及管理,通过严格的内存管理保证沙箱的隔离性。

另一方面,Wasm 也规定了代码中任何可能产生外部影响的操作只能通过导入/导出模块来实现。当我们在编写 C 语言源码时,可以直接通过系统调用来访问系统的环境变量、文件、网络等资源。而在 Wasm 的世界中,并不存在系统调用相关的指令,任何对外部资源的访问必须通过导入模块来间接实现。以文件读写为例,在 Wasm 中要想进行文件读写,需要宿主机提供实现文件读写功能的导入函数,Wasm 代码调用该导入函数,由宿主机间接进行文件读写,再将操作结果返回给 Wasm 扩展。在上述过程中,实际的文件读写操作由宿主机完成,宿主机对这一过程有绝对的控制权,包括但不限于只允许读写指定文件、限制读写内容、完全禁止读写等。

扩展框架

MOSN 以 插件(Plugin) 的形式对 Wasm 扩展进行统一管理,插件是指一组 Wasm 沙箱实例及其配置的集合。用户通过配置来加载、更新以及卸载 Wasm 插件,并通过配置来描述沙箱实例的运行规格(使用的执行引擎、Wasm 文件路径、实例数量等)。下面展示了一个典型的 Wasm 插件配置:

image.png

当 MOSN 加载上述插件配置时,会按照以下流程生成插件对应的 Wasm 沙箱实例:

image.png

在后续运行的过程中,用户通过 Wasm 扩展框架获取指定插件的沙箱实例, 然后通过沙箱实例暴露的 API 与扩展程序进行交互。本文的下一小节将对此交互过程进行详细描述。在 MOSN 中,Wasm 扩展框架与具体用途无关,在 MOSN 已有的任何一处扩展点,均可以直接使用 Wasm 框架来获取安全隔离的插件执行能力。

如下图所示,Wasm 扩展框架主要分为 Manager、VM 和 ABI 三个子模块。其中

  • Manager 模块负责对 Wasm 插件的配置进行统一管理,提供插件的增删查改功能,并负责将用户提供的插件配置渲染成一组的 Wasm 沙箱实例
  • VM 模块提供对 Wasm Runtime(虚拟机) 的统一封装,负责 .wasm 文件的编译、执行,以及 Wasm 沙箱实例的资源管理
  • ABI 模块则提供对外的使用接口,可以看作是 MOSN 与 Wasm 扩展代码之间交互的胶水层

image.png

本文不再对框架的具体实现细节进行介绍,感兴趣的读者可以阅读开源 PR 文档了解细节。

由于当前市面上几乎不存在使用 Go 语言直接编写的 Wasm Runtime,因此 MOSN 只能通过 CGO 调用的方式来间接地调用由 C++/Rust 编写的 Wasm 执行引擎。我们从 SDK 完善程度、性能、项目活跃度等角度综合考虑,经过一系列横向对比之后,选择了 Wasmer 作为 MOSN 默认的执行引擎。

Proxy-Wasm ABI 规范

本小节将介绍 MOSN 具体是如何跟 Wasm 扩展程序进行交互的。先说结论: MOSN 跟 Wasm 扩展代码之间的交互采用的是社区规范: Proxy-Wasm。

Proxy-Wasm 是开源社区针对「网络代理场景」设计的一套 ABI 规范,属于当前的事实规范。当前支持该规范的网络代理软件包括 Envoy、MOSN 和 ATS(Apache Traffic Server),支持该规范的 Wasm 扩展 SDK 包括 C++、Rust 和 Go。采用该规范的好处在于能让 MOSN 复用社区既有的 Wasm 扩展 (包括 Go 实现以及 C++/Rust 实现),也能让原本专门为 MOSN 开发的 Wasm 扩展运行在 Envoy 等网络代理产品上。

Proxy-Wasm 规范定义了宿主机与 Wasm 扩展程序之间的交互细节,包括 API 列表、函数调用规范以及数据传输规范这几个方面。其中,API 列表包含了 L4/L7、property、metrics、日志等方面的扩展点,涵盖了网络代理场景下所需的大部分交互点,且可以划分为宿主侧扩展和 Wasm 侧扩展。这里简单展示规范中的部分内容,完整内容请参考 spec。

image.png


image.png

规范的实现需要宿主侧和 Wasm 侧两边配合才能正常工作。对于 Wasm 侧,社区已经有 C++、Rust 和 Go 三种语言实现的 SDK,用户可以直接使用这些 SDK 来编写与宿主无关的 Wasm 扩展程序。而对于宿主侧,社区只提供了 C++ 和 Rust 的宿主侧实现。为此,我们在项目中使用 Go 语言对 Proxy-Wasm 规范的宿主侧进行了实现,并将其贡献给开源社区,使之成为社区推荐的 Go-Host 实现 (如下图所示)。需要强调的是,宿主侧实现并不依赖具体的网络代理程序,理论上任何直接通过 Host 程序与 Wasm 扩展进行交互。

image.png

我们以 HTTP 场景为例,介绍在 MOSN 中是如何通过 Proxy-Wasm 规范来与 Wasm 扩展程序进行交互,处理 HTTP 请求的。

image.png

  • MOSN 收到 HTTP 请求时,将请求解码成 Header、Body、Trailer 三元组结构,按照配置依次执行 StreamFilters。
  • 执行到 Wasm StreamFilter 时,MOSN 将请求三元组传递给 Proxy-Wasm 宿主侧实现 proxy-wasm-go-host。
  • 宿主侧 go-host 将 MOSN 请求三元组编码成规范指定的格式,并调用规范中的 proxy_on_request_headers 等接口,将请求信息传递至 Wasm 侧。
  • Wasm 侧 SDK 将请求数据从规范格式转换为便于用户使用的格式,随后调用用户编写的扩展代码。
  • 用户代码返回,Wasm 侧将返回结果按规范格式传递回 MOSN 侧。
  • MOSN 继续执行后续 StreamFilter。

上述示例中,我们并不限制 Wasm 侧的语言实现,用户可以使用 C++/Rust/Go 几种语言来编写自定义的扩展代码。与之相对的,只需要用相应语言的 Proxy-Wasm-SDK 一起编译成 .wasm 文件,即可运行在 MOSN 之上。

工程实践

Quick Start

本小节主要演示如何在 MOSN 中进行配置并运行 Wasm 扩展插件流程。演示所需的源文件参考 example。

在演示中,我们通过配置让 Wasm 扩展插件来处理 MOSN 接收的 HTTP 请求,MOSN 的监听端口为 2045。在 Wasm 处理请求的源码中,我们通过 Proxy-Wasm 规范中的 proxy_dispatch_http_call 接口向外部 HTTP 服务器发起请求,Wasm 源码内指定外部 HTTP 服务器的监听端口为 2046。演示场景的流程如下图所示:

image.png

该演示流程主要分为以下步骤:

  • 将扩展程序编译成 .wasm 文件
  • 启动 MOSN 并加载 Wasm 插件
  • 启动外部 HTTP 服务器
  • 请求验证

1.编译 Wasm 扩展程序

我们在示例工程中提供了 C 和 Go 两种语言实现的 Wasm 扩展源码,对 Proxy-Wasm 规范的采用使得我们能够利用多种语言 (C++/Rust/Go) 来编写 Wasm 扩展代码。出于编译的便利性,这里使用 Go 源码实现进行演示。

进入 example/wasm/httpCall 目录,执行命令:

make

上述操作会将目录下的 filter-go.go 源码文件编译成 filter-go.wasm 文件

2.启动 MOSN

示例工程提供了一份加载 filter-go.wasm 扩展文件的配置,通过以下命令即可启动:

./mosn start -c config.json

上述命令中使用的 MOSN 可执行程序可以通过以下命令由源码构建:

image.png

3.启动外部 HTTP 服务器

该示例工程中,Wasm 扩展源码会通过 MOSN 向外部 HTTP 服务器发起请求,请求的 URL 为:

http://127.0.0.1:2046/

为此,示例工程也提供了一段 HTTP 服务器代码,当其收到 HTTP 请求时,均会返回响应头: from: external http server,返回响应体: response body from external http server。

执行以下命令将启动上述 HTTP 服务器:

go run server.go

4.请求验证

上述操作准备就绪后,便可通过 Curl 来进行请求验证了。

curl -v http://127.0.0.1:2045/

执行上述命令后,MOSN 终端将能够观察到以下日志:

image.png

性能测试

测试环境:

  • OS: macOS Catalina 10.15.4
  • CPU: Intel(R) Core(TM) i7-7660U CPU @ 2.50GHz 4Core
  • MEM: 16 GB 2133 MHz LPDDR3
  • Go Version: go1.14.13 darwin/amd64

测试场景:

  • 拓扑: client --http1.1--> MOSN
  • 操作: MOSN 收到 H1 请求后,往请求头中添加一个 Header 随后返回 200

测试数据:

「native」表示添加 Header 的操作使用 MOSN 原生的 Stream Filter 完成;

「wasm」表示添加 Header 的操作使用 Wasm 扩展完成

  • 固定 QPS 模式,将 QPS 固定为 2000 进行压测
压测命令: sofaload --h1 -c 100 -t 4 --qps=2000 -D 30 http://127.0.0.1:2045/

image.png

  • 压测模式,不限制压测 QPS,将流量打到最大
压测命令: sofaload --h1 -c 100 -t 4 -n 1000000 http://127.0.0.1:2045/

image.png

异常调试

对于实际的工程项目而言,光能运行是不够的,必须具备一定的问题排查和定位能力,才能在遇到程序故障时,解析异常源码的调用堆栈,快速定位第一现场,从而提高开发及调试的效率。
由于 Wasm 本身的定位是与编程语言无关的字节码规范,不同语言的源代码 (C++/Go/JavaScript 等) 均能够编译为统一的 Wasm 字节码,因此如何屏蔽具体编程语言的细节模型,制定语言无关的调试信息规范,是社区需要解决的难题之一。

针对这一问题,在当前的工程实践中,JavaScript 语言采用的是 Source Map 格式,而 C++、Rust 和 Go 语言采用的是 Dwarf 格式的调试信息。对具体调试信息格式的介绍并不在本文的范围之内,读者可自行参考外部文章。这里需要强调的是,对于 Wasm 而言,还需要对调试信息的格式进行一定的扩展,才能满足实际的应用需要。与其他编程语言不同的是,.wasm 文件是能够被转换成 .wat 格式,并手动编辑内容的,编译好的 .wasm 文件仍然有修改段内容的可能。为了适应这种场景,Wasm 调试规范对 Dwarf 格式中的位置信息编码进行了调整,指令的偏移值被设置成基于 Code 段的偏移:

With WebAssembly, the .debug_line section maps Code section-relative instruction offsets to source locations.

为此,我们在解析指令偏移时,需要偏移数值进行调整,减去 Code 段的偏移量,才能得到 Wasm 指令的实际偏移值,进而利用 .debug_line 段定位到准确的源码行。下图展示了利用 MOSN 输出的错误日志定位 Wasm 故障源码行的示例。

image.png

https://www.atatech.org/articles/200651

总结

对于蚂蚁而言,安全可信永远是我们追求的目标,而面对越来越多的扩展场景,MOSN 需要一个安全可靠的隔离环境,以避免扩展代码给 MOSN 运行造成的安全风险。为此,我们采用 WebAssembly 技术,为 MOSN 实现了一个基于 Wasm 隔离沙箱的插件扩展框架。MOSN 采用网络代理社区中的 Proxy-Wasm 规范,实现了语言无关、宿主无关的网络代理扩展能力。同时,我们也向开源社区贡献了 Proxy-Wasm-Go-Host 实现,积极融入开源社区。

需要注意的是,当前 WebAssembly 技术仍处于发展阶段,Go 语言自身对 WebAssenbly 生态的支持仍有巨大的提升空间。我们在实践的过程中,也总是面临 Go 语言在 Wasm 生态中不够给力的情况。由于 Go 官方编译器还不支持将 Go 源码程序编译成 WASI 系统接口 (GOOS=wasi) 的 .wasm 文件,我们不得不借助 TinyGo 来完成 Go 扩展程序的编译,而这也导致我们需要面对 TinyGo 在语言特性支持程度、性能、稳定性等方面不足的痛点。与之相比,C++/Rust 对 Wasm 生态的支持程度就要完善得多。

总而言之,WebAssembly 技术的出现仍然为我们提供了一种启发和希望,促使我们进一步思考如何在云原生时代更好地践行安全可信这一信条。

延伸阅读

image.png

相关文章
|
Ubuntu 测试技术 Linux
百度搜索:蓝易云【Ubuntu系统打RT实时内核补丁教程】
现在,你已经成功在Ubuntu系统上打上RT实时内核补丁,并且系统将使用RT内核运行,提供更好的实时性能。请注意,内核编译和替换是一项复杂的操作,建议在实施前备份重要数据,并在测试环境中进行验证。
187 2
|
自然语言处理 iOS开发 C++
Alibaba.com瘦包40MB——业界最全的iOS包大小技术总结
本文总结提炼了Alibaba.com App的瘦身的技术和策略,系统化地介绍APP瘦身的业务价值、分析技术、瘦身技术、防劣化机制,让读者可以系统化地了解APP瘦身的技术体系。并基于实践经验,介绍各种瘦身技术的ROI,让读者可以避免踩雷,将资源浪费在效果不佳的技术上。希望对你有所帮助。
Alibaba.com瘦包40MB——业界最全的iOS包大小技术总结
|
9月前
|
机器学习/深度学习 人工智能 自动驾驶
企业内训|AI大模型在汽车行业的前沿应用研修-某汽车集团
本课程是TsingtaoAI为某汽车集团高级项目经理设计研发,课程全面系统地解析AI的发展历程、技术基础及其在汽车行业的深度应用。通过深入浅出的理论讲解、丰富的行业案例分析以及实战项目训练,学员将全面掌握机器学习、深度学习、NLP与CV等核心技术,了解自动驾驶、智能制造、车联网与智能营销等关键应用场景,洞悉AI技术对企业战略布局的深远影响。
549 97
|
设计模式 前端开发 Java
玩转Spring—Spring5新特性之Reactive响应式编程实战
玩转Spring—Spring5新特性之Reactive响应式编程实战
618 0
|
8月前
|
机器学习/深度学习 人工智能 算法
《AI赋能鸿蒙Next,开启智能关卡设计新时代》
在游戏开发中,关卡设计至关重要。借助鸿蒙Next系统和AI技术,通过学习玩家行为、自动生成内容、自适应难度调整、优化布局与流程及增强互动性,可实现个性化、多样化的智能关卡设计,提升沉浸感和趣味性,同时提高开发效率与质量。然而,开发者需关注数据安全与算法偏见等问题,确保AI与游戏的深度融合。
170 3
|
缓存 安全 网络协议
Envoy中Wasm Filter相关概念解释
本文旨在介绍Envoy中Wasm Filter相关概念,让用户对相关架构有更加深入的了解,可以快速开发出自己的Wasm插件。 阿里云服务网格(Service Mesh,简称ASM)提供一个全托管式的服务网格平台,兼容社区Istio开源服务网格,用于简化服务的治理,包括服务调用之间的流量路由与拆分管理、服务间通信的认证安全以及网格可观测性能力,从而极大地减轻开发与运维的工作负担。 ASM支持Wasm插件。
520 3
|
监控 安全
公司如何监控员工电脑:WebAssembly 语言的浏览器端探索
在数字化办公环境中,公司可能需要对员工电脑进行监控以确保信息安全和工作效率。WebAssembly 作为一种高性能的二进制指令格式,可在浏览器端实现复杂监控功能,如获取浏览器信息和监测网络活动,无需安装额外软件,降低了系统侵入性。然而,公司在实施监控时需遵守法律法规和道德规范,确保员工隐私得到保护,并明确告知监控目的与范围。
159 3
|
Rust 安全 数据挖掘
【颠覆常规】Rust科学计算革命:掌握数值分析与数据处理的全新利器,让你的工作事半功倍!
【8月更文挑战第31天】Rust语言凭借其出色的内存安全和高性能特性,在科学计算领域逐渐崭露头角。本文通过具体代码示例展示了Rust在数值计算与数据分析中的应用,包括矩阵乘法、统计分析及线性方程组求解。通过安装Rust并引入`ndarray`、`nalgebra`和`statrs`等库,读者可以轻松实现各类科学计算任务。Rust在科学计算领域的潜力巨大,未来可期。
690 1
|
监控 Devops 测试技术
DevOps实践: 持续集成和持续部署(CI/CD)的入门指南
【9月更文挑战第10天】在快速迭代的软件开发世界中,DevOps已经成为加速产品交付、提升软件质量和团队协作的关键策略。本文将深入浅出地介绍DevOps的核心组成部分——持续集成(Continuous Integration, CI)与持续部署(Continuous Deployment, CD)的基本概念、实施步骤以及它们如何革新传统的软件开发流程。你将学习到如何通过自动化工具简化开发流程,并理解为什么CI/CD是现代软件开发不可或缺的一环。
|
存储 自然语言处理 算法
阿里云百炼之RAG算法能力分享会来喽|速来围观~
阿里云百炼是基于通义大模型、行业大模型以及三方大模型的一站式大模型开发平台。提供完整的模型训练工具和全链路开发套件,预置丰富的应用插件,提供便捷的集成方式,结合企业专属数据和API,帮企业高效完成大模型应用构建。RAG检索增强应用是在通义千问-Max大模型基础之上,专项增强「基于知识检索的大模型生成能力」,支持基于结构化/非结构化内容的文字生成场景。
920 5

热门文章

最新文章