使用容器鏡像交付是雲原生時代的重要特徵,這不只體如今企業內部應用中,也體如今公有云的 serverless 函數中。騰訊雲 serverless 函很多天前上線了容器鏡像交付的功能,相較於傳統 serverless,容器鏡像交付有着明顯的好處:它解決了環境依賴的問題,使 serverless 更加通用,拓展了 serverless 的能力。前端
具體來講,容器鏡像使得 serverless 函數不但能支持預設的 nodejs、Python 這些 runtimes,並且也能支持機器碼二進制程序(Native Client or NaCl),或者 WebAssembly 字節碼程序。NaCl 與 WebAssembly 對執行環境要求複雜,可是執行性能與效率會比 Nodejs 與 Python 高不少。node
放眼將來,咱們認爲 WebAssembly runtime 是一個能夠在不少場景替代 Docker 的輕量級軟件容器。WebAssembly 在速度(尤爲是冷啓動)與資源佔用上大大優於 Docker(上百倍的改進)。可是,羅馬不是一天建成的。今天騰訊雲支持的容器鏡像只有 Docker 鏡像一種,咱們暫時必須在 Docker 裏面運行 WebAssembly。git
那麼,現階段在 Docker 容器內部運行 WebAssembly 函數相比於 NaCl 有什麼好處呢?WebAssembly 擁有把 Docker 的安全隔離、跨平臺可移植、可編排等優勢,從應用的顆粒度下沉到函數的顆粒度,爲開發者帶來了很大便利。具體來講,github
- WebAssembly 函數能夠實現接近機器語言的性能。
- 與 NaCl 函數不一樣,WebAssembly 函數是跨平臺的。容許開發者在本身的電腦上測試函數,而後部署在任何服務器,硬件架構,公共雲,或者容器上。
- WebAssembly 函數能夠簡單地被打包,部署,更新升級,編排。遠比 NaCl 動態庫容易管理。
- WebAssembly 比 NaCl 函數更安全,由於 WebAssembly 提供了一個安全隔離的沙箱。
另外,CNCF 的 WebAssembly 項目 WasmEdge 爲 Rust 開發者提供了最友好的 API 來高效安全地執行 Tensorflow 模型。這比任何其餘基於 C、C++、Rust 的 NaCl tensorflow API 都簡單好用不少。值得你來學習!web
快速開發的模板
在這裏,咱們提供了一個模板,能夠快速上手進行開發。docker
模板連接:https://github.com/second-state/tencent-scf-wasm-runtimeapi
在這個 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-state/tencent-scf-wasm-runtime#%E5%89%8D%E7%AB%AF%E7%9A%84-web-ui
示例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.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。
示例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-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。敬請期待。