关于webassembly:在腾讯云上部署基于-WebAssembly-的高性能-serverless-函数

32次阅读

共计 5448 个字符,预计需要花费 14 分钟才能阅读完成。

应用容器镜像交付是云原生时代的重要特色,这不仅体现在企业外部利用中,也体现在私有云的 serverless 函数中。腾讯云 serverless 函数日前上线了容器镜像交付的性能,相较于传统 serverless,容器镜像交付有着显著的益处:它解决了环境依赖的问题,使 serverless 更加通用,拓展了 serverless 的能力。

具体来说,容器镜像使得 serverless 函数岂但能反对预设的 nodejs、Python 这些 runtimes,而且也能反对机器码二进制程序(Native Client or NaCl),或者 WebAssembly 字节码程序。NaCl 与 WebAssembly 对执行环境要求简单,然而执行性能与效率会比 Nodejs 与 Python 高很多。

放眼将来,咱们认为 WebAssembly runtime 是一个能够在很多场景代替 Docker 的轻量级软件容器。WebAssembly 在速度(尤其是冷启动)与资源占用上大大优于 Docker(上百倍的改良)。然而,罗马不是一天建成的。明天腾讯云反对的容器镜像只有 Docker 镜像一种,咱们临时必须在 Docker 外面运行 WebAssembly。

那么,现阶段在 Docker 容器外部运行 WebAssembly 函数相比于 NaCl 有什么益处呢?WebAssembly 领有把 Docker 的平安隔离、跨平台可移植、可编排等长处,从利用的颗粒度下沉到函数的颗粒度,为开发者带来了很大便当。具体来说,

  • WebAssembly 函数能够实现靠近机器语言的性能。
  • 与 NaCl 函数不同,WebAssembly 函数是跨平台的。容许开发者在本人的电脑上测试函数,而后部署在任何服务器,硬件架构,公共云,或者容器上。
  • WebAssembly 函数能够简略地被打包,部署,更新降级,编排。远比 NaCl 动静库容易治理。
  • WebAssembly 比 NaCl 函数更平安,因为 WebAssembly 提供了一个平安隔离的沙箱。

另外,CNCF 的 WebAssembly 我的项目 WasmEdge 为 Rust 开发者提供了最敌对的 API 来高效平安地执行 Tensorflow 模型。这比任何其余基于 C、C++、Rust 的 NaCl tensorflow API 都简略好用很多。值得你来学习!

疾速开发的模板

在这里,咱们提供了一个模板,能够疾速上手进行开发。

模板链接:https://github.com/second-sta…

在这个 repo 里,咱们有两个残缺的 WebAssembly 函数利用:一个是 main branch 里的图像处理函数,实现的性能是将图片变灰;另外一个在 tensorflow branch 里的 AI 推理函数,实现的性能是用 AI 推理辨认图片中最次要的物体。

这两个函数都是应用 Rust 编写的,因而咱们须要装置 Rust 编译器。装置好之后,依照上面的办法装置 wasm32-wasi 编译器指标,以生成 WebAssembly 字节码。

$ rustup target add wasm32-wasi

这两个利用模板的前端应用 next.js 编写,在这两个示例中,咱们将其前端 web UI 部署到了 GitHub Pages 上,然而你能够将其部署到任何一个反对动态网站的托管平台,比方腾讯的网站托管服务。具体如何部署,能够参考模板 github repo 的 README — 前端的 web UI。

链接:https://github.com/second-sta…

示例 1:图像处理

在这个模板中,咱们有一个曾经编译好的 WebAssembly 函数放在 ·api/grayscale.wasm文件里。这个函数的 Rust 源代码在 api/functions/image-grayscale外面。

Rust 函数读入一个图片,而后输入这个图片的黑白版。从 STDIN 读入上传的图片,而后把黑白图片从 STDOUT 输入。其输出与输入都是二进制数组。

use std::io::{self, Read, Write};
use image::{ImageOutputFormat, ImageFormat};

fn main() {let mut buf = Vec::new();
  io::stdin().read_to_end(&mut buf).unwrap();

  let image_format_detected: ImageFormat = image::guess_format(&buf).unwrap();
  let img = image::load_from_memory(&buf).unwrap();
  let filtered = img.grayscale();
  let mut buf = vec![];
  match image_format_detected {
    ImageFormat::Gif => {filtered.write_to(&mut buf, ImageOutputFormat::Gif).unwrap();},
    _ => {filtered.write_to(&mut buf, ImageOutputFormat::Png).unwrap();},
  };
  io::stdout().write_all(&buf).unwrap();
  io::stdout().flush().unwrap();}

你能够依据你的业务需要,更改 Rust 代码,比方将图片程度翻转、裁剪图片大小。改变 Rust 代码之后,须要用 cargo 命令就能够编译出新的 WebAssembly 文件 api/grayscale.wasm

$ cd api/functions/image-grayscale/
$ cargo build --release --target wasm32-wasi
$ cp target/wasm32-wasi/release/grayscale.wasm ../../

api/server.js 这个脚本从 Web 函数的网关取得 HTTP request 的数据,传给 grayscale.wasm 函数执行,再把执行后果返回给 HTTP response。

阐明:WasmEdge 反对 AOT 编译,大幅晋升了利用的性能,因而在这里,咱们应用 WasmEdge AOT 编译器在 Docker 环境内生成的 grayscale.so 文件,

app.post('/func', (req, res) => {const wasmedge = spawn(path.join(__dirname, 'wasmedge'), [path.join(__dirname, 'grayscale.so')]);

  let d = [];
  wasmedge.stdout.on('data', (data) => {d.push(data);

为了将这个函数部署为 serverless 函数,咱们须要将 grayscale.wasmserver.js 以及 WasmEdge 的执行环境一起封装在一个容器镜像外面。

$ cd api
$ docker build -t hkccr.ccs.tencentyun.com/secondstate/grayscale:0.1 ./
... ...
Successfully tagged hkccr.ccs.tencentyun.com/secondstate/grayscale:0.1

这里的 hkccr.ccs.tencentyun.com/secondstate/grayscale是在腾讯云的容器服务上建设一个容器镜像后取得的,具体能够参考咱们的 README — 筹备工作。

而后将这个容器镜像公布在腾讯云容器镜像仓库里。

$ docker push hkccr.ccs.tencentyun.com/secondstate/grayscale:0.1
The push refers to repository [hkccr.ccs.tencentyun.com/secondstate/grayscale]
... ...
0.1: digest: sha256:... size: 3246

到这一步,咱们的 WebAssembly 与 Rust 函数就构建好了,接下来就是将其部署到腾讯云 serverless 上。具体的步骤与截屏能够参考这个模板我的项目的 README。

示例 2:图像识别

在 TensorFlow 分支,咱们有一个更简单的 serverless 函数。它展现了如何用 Tensorflow 进行 AI 推理。Rust 与 WebAssembly 让咱们能够在几毫秒之内用深度学习辨认输出图片上的物体。

上面这个 Rust 程序从 STDIN 读取图像数据,而后将文本输入输入到 STDOUT。它用 WasmEdge Tensorflow API 来运行 AI 推理。

pub fn main() {
    // Step 1: Load the TFLite model
    let model_data: &[u8] = include_bytes!("models/mobilenet_v1_1.0_224/mobilenet_v1_1.0_224_quant.tflite");
    let labels = include_str!("models/mobilenet_v1_1.0_224/labels_mobilenet_quant_v1_224.txt");

    // Step 2: Read image from STDIN
    let mut buf = Vec::new();
    io::stdin().read_to_end(&mut buf).unwrap();

    // Step 3: Resize the input image for the tensorflow model
    let flat_img = wasmedge_tensorflow_interface::load_jpg_image_to_rgb8(&buf, 224, 224);

    // Step 4: AI inference
    let mut session = wasmedge_tensorflow_interface::Session::new(&model_data, wasmedge_tensorflow_interface::ModelType::TensorFlowLite);
    session.add_input("input", &flat_img, &[1, 224, 224, 3])
           .run();
    let res_vec: Vec<u8> = session.get_output("MobilenetV1/Predictions/Reshape_1");

    // Step 5: Find the food label that responds to the highest probability in res_vec
    // ... ...
    let mut label_lines = labels.lines();
    for _i in 0..max_index {label_lines.next();
    }

    // Step 6: Generate the output text
    let class_name = label_lines.next().unwrap().to_string();
    if max_value > 50 {println!("It {} a <a href='https://www.google.com/search?q={}'>{}</a> in the picture", confidence.to_string(), class_name, class_name);
    } else {println!("It does not appears to be any food item in the picture.");
    }
}

上文提到过模板我的项目中的 api/server.js 将 HTTP request 与 response 与 WasmEdge 联接起来。如果咱们更改了 Rust 函数的输出与输入,可能也须要改变 api/server.js 外面的胶水代码。

app.post('/func', (req, res) => {
  const wasmedge = spawn(path.join(__dirname, 'wasmedge-tensorflow-lite'),
    [path.join(__dirname, 'classify.so')],
    {env: {'LD_LIBRARY_PATH': __dirname}}
  );

批改了 Rust 函数 api/server.js 之后,依照上文所说的办法,从新创立与部署 Docker 镜像,新的函数就能够用了!

残缺的部署请参考模板我的项目的 README,期待你的 Rust 函数!

链接:https://github.com/second-sta…

下一步

目前,咱们依然把 WasmEdge 放在腾讯云 serverless 的 Docker 镜像外面运行。这尽管曾经带来了微小的益处,然而还没有齐全施展 WebAssembly 的劣势。WasmEdge 能够作为一个 Docker 的代替,间接运行函数,而不是在 Docker 之中运行函数。从而大幅提高 serverless 函数的性能,升高 infrastructure 的老本!目前曾经可能从 Docker 与 k8s 的治理编排工具外面启动与治理 WasmEdge 应用程序。

另外,尽管 Rust 非常适合写高性能的 serverless 函数,但 Rust 的确有着比拟平缓的学习路线,这对于 serverless 开发者来说体验并不好。咱们将推出一种“低代码”的解决方案,为具体利用设计“低代码”语言(DSL or Domain Specific Language)。WebAssembly 对语言编译器与解释器的广泛支持,使其特地适宜运行各行各业的 DSL。敬请期待。

正文完
 0