本文将介绍一种新的 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 的不足之处在于,只支持能够编译为 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_id
。wasm_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 推理上的应用实例与教程。