使用容器镜像交付是云原生时代的重要特征,这不仅体现在企业内部应用中,也体现在公有云的 Serverless 云
函数中。腾讯云 Serverless 云函数日前上线了「容器镜像交付」的功能,相较于传统 Serverless,容器镜像交付有着明显的好处:
它解决了环境依赖的问题,使 Serverless 更加通用,拓展了 Serverless 的能力。
具体来说,容器镜像使得 Serverless 云函数不但能支持预设的 Node.js、Python 这些 Runtimes,而且也能支持机器码二进制程序(Native Client or NaCl),或者 WebAssembly 字节码程序。NaCl 与 WebAssembly 对执行环境要求复杂,但是执行性能与效率会比 Node.js 与 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-state/tencent-scf-wasm-runtime
在这个 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 — 前端的 W
eb UI。
链接:https://github.com/second-state/tencent-scf-wasm-runtime#%E5%89%8D%E7%AB%AF%E7%9A%84-web-ui
在这个模板中,我们有一个已经编译好的 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 Function 的网关获得 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.wasm
与
server.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。
在 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-state/tencent-scf-wasm-runtime/blob/main/README.md
目前,我们仍然把 WasmEdge 放在腾讯云 Serverless 的 Docker 镜像里面运行。这虽然已经带来了巨大的好处,但是还没有完全发挥 WebAssembly 的优势。WasmEdge 可以作为一个 Docker 的替代,直接运行函数,而不是在 Docker 之中运行函数。从而大幅提高 Serverless 云函数的性能,降低 infrastructure 的成本!目前已经能够从 Docker 与 K8s 的管理编排工具里面启动与管理 WasmEdge 应用程序。
另外,虽然 Rust 非常适合写高性能的 Serverless 云函数,但 Rust 确实有着比较陡峭的学习路线,这对于 Serverless 开发者来说体验并不好。我们将推出一种「低代码」的解决方案,为具体应用设计「低代码」语言(DSL or Domain Specific Language)。WebAssembly 对语言编译器与解释器的广泛支持,使其特别适合运行各行各业的 DSL。敬请期待。
欢迎进入千人 QQ 群 (871445853) 交流 Serverless!
GitHub: github.com/serverless
官网: cloud.tencent.com/product/serverless-catalog