初探 WebAssembly 在 serverless 中的应用| 🏆 技术专题第七期征文

2,456 阅读4分钟

本文将介绍一种新的 Serverless 架构:基于 WebAsssembly 虚拟机的 Serverless 架构。通过本文,你会了解到 WebAssembly 技术、Serverless WebAssembly 架构、如何使用 Serverless WebAssembly 开发一个简单的应用程序。

先来看一个 MobileNet TensorFlow model 在 Serverless Wasm 的实例。上传一张食物照片,识别图片里的食物是什么。

点击这里,上传你的午餐,将自动识别你中午吃了什么!

当下的 Serverless 架构

在 Serverless 架构中,开发者与用户无需关注服务器的运维与费用,而是将关注点投入在实现业务逻辑的代码上,并按实际资源使用量交费。在不降低用户体验的前提下,Serverless 极大降低了开发与运营的成本。

Serverless 云函数是今天实现互联网应用的最快捷最简单的方法。国内外各大云厂商都有自己的 Serverless 业务,或架构在系统或硬件级别的虚拟机,或建立在容器,如 Docker 之上。

这两种方法在安全与性能上各有千秋。 根据伯克利大学的 Johann Schleier-Smith 的观点,使用系统或硬件级别的虚拟机提供了最佳的隔离和安全性,但是运行很慢,并且管理起来很复杂。使用容器的方法安全性比较差,但是性能比系统级虚拟机高得多。

新兴的 Serverless Wasm 架构

除了上面两种 Serverless 架构外,还有一种新兴的方法:使用特定于应用程序的虚拟机,比如 WebAssembly(Wasm) 。

我在《为什么说软件服务的未来必然是WebAssembly?》一文中介绍过 WebAssembly 在服务端的巨大潜力。一言以概之,WebAssembly 提供了接近了接近本机代码的性能,但是依然保持着良好的安全性。

如果在 2008 年已经有了 WASM + WASI,那么我们压根无需创始 Docker 这个项目了。 Wasm 就有这么重要!服务器上的 WebAssembly 是计算的未来。 — Docker 联合创始人 Solomon Hykes

Serverless Wasm 提供了高度的抽象,用户不需要绑定自己的操作系统或软件堆栈,只需执行编译后的字节码应用程序。WebAssembly 提供了一个高级的“基于功能”的安全模型,用于访问系统资源(例如,通过WASI 规范),而不是粗粒度的操作系统级隔离。

接下来让我们以 Second State FaaS 为例,具体了解下 Serverless Wasm 架构。

Serverless Wasm

Serverless Wasm 的不足之处在于,只支持能够编译为 Wasm 字节码的应用程序,目前 Rust、C、C++、AssemblyScript 是支持 Wasm 最完善的语言。 但是随着 Wasm 的生态不断完善发展,Wasm 通过 LLVM 工具链将支持更多的编程语言。

Second State FaaS 目前把重点放在了对 Rust 函数的支持。 Rust 连续5年被 Stack OverFlow 评为最受开发者欢迎的编程语言。 Rust 是安全的、高性能的,内存安全的,没有GC。

Serverless Wasm 的整个工作流是这样的:

  • 使用编译工具 ssvmup 将 Rust 函数编译成 wasm 字节码
  • 通过 HTTP POST 将编译好的 .wasm 文件上传到 FaaS 中
  • 得到返回的 wasm_id、wasm_sha256、SSVM_Usage_Key、SSVM_Admin_Key,用于管理当前的应用程序
  • 最后通过 HTTP API 调用、执行 Rust 函数

Rust 函数与 Wasm 虚拟机的结合非常适合计算密集的任务,比如 AI 推理、视频转码、边缘计算等场景。

一个快速上手的例子

接下来一起用 Serverless Wasm 实现一个简单的 Hello World 示例。

工具链准备

1 使用 Docker image。 下面这个 Docker image 包含了所有需要的工具链

$ docker pull secondstate/ssvm-nodejs-starter:v2
$ docker run -p 3000:3000 --rm -it -v $(pwd):/app secondstate/ssvm-nodejs-starter:v2
(docker) # ssvmup build
... ...

2 在github 上 fork 这个repo,然后使用 GitHub 新发布的 GitHub CodeSpaces 打开 Hello World 示例。在这个 IDE 里,你可以重写 Rust 函数,然后在 terminal window 运行命令行。

3 如果你安装了 Ubuntu Linux 20.04,只需再安装 Rust 以及 ssvmup

Rust 代码

源代码可以在这里查看

下面是 Hello World 的完整 Rust 代码。 这个 Rust 函数接收一个字符串参数并返回一个字符串值。Hello 是输入参数的前缀,然后将字符串返回给函数的调用者。

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn say(s: &str) -> String {
  let r = String::from("hello ");
  return r + s;
}

编译之前,请确保 Cargo.toml 文件声明了正确的依赖关系。

[dependencies]
wasm-bindgen = "=0.2.61"

编译并部署 Rust 函数

使用 ssvvmup 工具将 Rust 函数编译成 WebAssembly 函数。使用下面的命名行,编译好的 wasm 文件将保存在 pkg 目录中。

$ ssvmup build

将在 pkg 目录的 .wasm 文件上传到 Second State FaaS 中的 /api/executables RPC 服务端点中。上传之前,记得检查 .wasm 的文件名。

$ curl --location --request POST 'https://rpc.ssvm.secondstate.io:8081/api/executables' \
--header 'Content-Type: application/octet-stream' \
--header 'SSVM-Description: say hello' \
--data-binary '@pkg/hello_lib_bg.wasm'

.wasm 文件上传完成后,Second State FaaS 将返回 wasm_idwasm_id 可以访问已经部署好的 wasm 文件里的函数。

调用 FaaS 功能

现在可以通过 web 调用 Rust 函数了!RPC 服务端点在 /api/run/wasm_id/function_name ,其中 wasm_id 是刚刚部署的 Wasm 文件的 ID,function_name 是我们要在 wasm 文件中调用的函数名称。

HTTP request body 作为调用参数会传递给函数。如我们所见,这个函数采用字符串参数,因此 HTTP body 会被转换为文本字符串并传递给该函数。TTP response body 是该函数的返回字符串值。

$ curl --location --request POST 'https://rpc.ssvm.secondstate.io:8081/api/run/161/say' \
--header 'Content-Type: text/plain' \
--data-raw 'Second State FaaS'

hello Second State FaaS

Web UI

FaaS 最引人注目的用例之一是使函数充当静态网站的后端服务。下面是对此 FaaS 函数进行 AJAX 调用的 JavaScript 代码。

$.ajax({
      url: "https://rpc.ssvm.secondstate.io:8081/api/run/161/say",
      type: "post",
      data : $('#input').val(),
      contentType: "text/plain",
      processData: false,
      success: function (data) {
        $('#result').html(data);
      }
});

点击这里可以访问有 Web UI 的演示。 serverless 函数将静态函数转换为 web 应用程序。

更多资料

这样,我们就完成了 serverless wasm 的一个简单例子。如果你对 serverless wasm 感兴趣,欢迎访问 serverless wasm 网站,了解更多 serverless wasm 在 AI 推理上的应用实例与教程。

🏆 技术专题第七期 |万物皆可 Serverless