边缘云允许开发者在靠近用户的地方部署微服务(即细粒度的网络服务)。这为他们提供了更好的用户体验(和非常快的响应时间)、安全和高可用性。
它还利用本地甚至私人数据中心、CDN 网络和电信数据中心(例如 5G MECs)来提供计算服务。
边缘云的成功例子包括 Cloudflare、Fastly、Akamai、fly.io、Vercel、Netlify 等等。
但与大型公有云相比,边缘云也是一个资源受限的环境。如果边缘微服务本身很慢或很臃肿或不安全,它们将违背在边缘云上部署的整个目的。
在这篇文章中,我将向你展示如何在 WebAssembly 沙盒中创建轻量级和高性能的 Web 服务,然后将它们免费部署在边缘云提供商 fly.io。
Fly.io 是一家在边缘云上提供虚拟机服务的领先供应商。它在世界各地都有边缘数据中心。fly.io 的虚拟机支持应用服务器、数据库,以及在我们的案例中支持微服务的轻量级运行时。
我将使用 WasmEdge Runtime 作为这些微服务的安全沙盒。WasmEdge 是一个专门为云原生服务优化的 WebAssembly 运行时。
我们将把用 Rust 或 JavaScript 编写的微服务应用打包在基于 WasmEdge 的 Docker 镜像中。
这种方法有几个引人注目的优势:
如果应用程序很复杂,则性能优势会被放大。例如,WasmEdge 上的 AI 人工智能应用程序不需要安装 Python。WasmEdge 上的 node.js 应用程序不需要安装 Node.js 和 v8。
在本文的其余部分,我将演示如何运行:
所有这些都在 WasmEdge 中快速、安全地运行,而消耗的资源是普通 Linux 容器的 1/10。
首先,如果你的系统中已经安装了 Docker 工具,那就太好了。如果没有,请按照本手册的第一节现在就安装 Docker。然后我们将使用在线安装程序来安装 WasmEdge、Rust 和 [fly.io] 的 flyctl
工具(http://fly.io)。
安装 WasmEdge。详见这里。
curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash -s -- -e all
安装 Rust。详见这里。
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
为 fly.io 安装 flyctl
工具(http://fly.io )。详见这里。
curl -L https://fly.io/install.sh | sh
一旦你安装了flyctl
,按照指示在 fly.io 上注册一个免费账户。现在你已经准备好在边缘云上部署网络服务了。
我们的第一个例子是一个用 Rust 编写的简单 HTTP 服务。它展示了一个现代网络应用,可以扩展到支持任意复杂的业务逻辑。
基于流行的 tokio 和 hyper crates,这个微服务是快速的、异步的(非阻塞的),并且对开发者来说非常容易创建。
完全静态链接的 WasmEdge 镜像只有 4MB,而基本的 Linux 镜像是 40MB。这足以运行一个用 Rust 的 tokio 和 hyper 框架编写的异步 HTTP 服务。
运行以下两个 CLI 命令,从我们为 WasmEdge 设计的超薄(slim) Docker 镜像中创建并部署一个 fly.io 应用程序。
$ flyctl launch --image juntaoyuan/flyio-echo
$ flyctl deploy
为什么这样做?你可以使用 curl 命令来测试所部署的网络服务是否真的工作。无论你向它发布什么数据,它都会回显(echoes back)出来。
$ curl https://proud-sunset-3795.fly.dev/echo -d "Hello WasmEdge on fly.io!"
Hello WasmEdge on fly.io!
juntaoyuan/flyio-echo
Docker 镜像的 Docker 文件包含 WasmEdge 运行时的完整包和自定义网络应用wasmedge_hyper_server.wasm
。
FROM wasmedge/slim-runtime:0.11.0
ADD wasmedge_hyper_server.wasm /
CMD ["wasmedge", "--dir", ".:/", "/wasmedge_hyper_server.wasm"]
构建wasmedge_hyper_server.wasm
应用程序的 Rust 源代码项目,可在 GitHub 上找到。它使用 tokio API 来启动一个 HTTP 服务器。
当服务器收到一个请求时,它委托给 echo()
函数来异步处理该请求。这使得微服务可以接受并处理多个并发的 HTTP 请求。
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let addr = SocketAddr::from(([0, 0, 0, 0], 8080));
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
loop {
let (stream, _) = listener.accept().await?;
tokio::task::spawn(async move {
if let Err(err) = Http::new().serve_connection(stream, service_fn(echo)).await {
println!("Error serving connection: {:?}", err);
}
});
}
}
异步的 echo()
函数如下。它利用 hyper 提供的 HTTP API 来解析请求并生成响应。这里,响应只是请求数据体。
async fn echo(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
match (req.method(), req.uri().path()) {
... ...
(&Method::POST, "/echo") => Ok(Response::new(req.into_body())),
... ...
// Return the 404 Not Found for other routes.
_ => {
let mut not_found = Response::default();
*not_found.status_mut() = StatusCode::NOT_FOUND;
Ok(not_found)
}
}
}
现在,让我们在基本的微服务中添加一些令人印象深刻的东西吧!
在这个例子中,我们将创建一个用于图像分类的网络服务。它通过一个 Tensorflow Lite 模型处理上传的图像。
我们将使用 WasmEdge 的 Rust API 来访问 Tensorflow,而不是创建一个复杂(和臃肿)的 Python 程序,它以完整的本地机器码速度运行推理任务(例如,如果有的话,利用 GPU 硬件)。
通过 WASI-NN 标准,WasmEdge 的 Rust API 可以使用 Tensorflow、PyTorch、OpenVINO 和其他 AI 框架中的 AI 模型。
对于包含完整 Tensorflow Lite 依赖项的 AI 推理应用程序,WasmEdge 占用空间小于 115MB。相比之下,标准 Tensorflow Linux 映像超过 400MB。
运行以下两个 CLI 命令,从我们用于 WasmEdge + Tensorflow 的超薄 Docker 镜像创建并部署一个 fly.io 应用程序。
$ flyctl launch --image juntaoyuan/flyio-classify
$ flyctl deploy
就是这样!你可以使用 curl 命令来测试部署的 Web 服务是否真正起作用。它返回具有置信度(confidence level)的图像分类结果。
$ curl https://silent-glade-6853.fly.dev/classify -X POST --data-binary "@grace_hopper.jpg"
military uniform is detected with 206/255 confidence
juntaoyuan/flyio-classify
Docker 镜像的 Dockerfile 包含 WasmEdge 运行时的完整包、整个 Tensorflow 库及其依赖项,以及自定义 Web 应用程序 wasmedge_hyper_server_tflite.wasm
。
FROM wasmedge/slim-tf:0.11.0
ADD wasmedge_hyper_server_tflite.wasm /
CMD ["wasmedge-tensorflow-lite", "--dir", ".:/", "/wasmedge_hyper_server_tflite.wasm"]
构建wasmedge_hyper_server_tflite.wasm
应用程序的 Rust 源代码项目,可在 GitHub 上找到。基于 tokio 的异步 HTTP 服务器在异步main()
函数中,与前面的例子一样。
classify()
函数处理请求中的图像数据,将图像变成张量,运行 Tensorflow 模型,然后将返回值(在张量中)变成文本标签和可能分类的概率。
async fn classify(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
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");
match (req.method(), req.uri().path()) {
(&Method::POST, "/classify") => {
let buf = hyper::body::to_bytes(req.into_body()).await?;
let flat_img = wasmedge_tensorflow_interface::load_jpg_image_to_rgb8(&buf, 224, 224);
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");
... ...
let mut label_lines = labels.lines();
for _i in 0..max_index {
label_lines.next();
}
let class_name = label_lines.next().unwrap().to_string();
Ok(Response::new(Body::from(format!("{} is detected with {}/255 confidence", class_name, max_value))))
}
// Return the 404 Not Found for other routes.
_ => {
let mut not_found = Response::default();
*not_found.status_mut() = StatusCode::NOT_FOUND;
Ok(not_found)
}
}
}
在本文的最后一节,我们将讨论如何为 Rust 微服务添加更多的功能,如数据库客户端和 Web 服务客户端。
虽然基于 Rust 的微服务是轻量和快速的,但不是每个人都是 Rust 开发者。
如果你更熟悉 JavaScript,你仍然可以在边缘云中利用 WasmEdge 的安全性、性能、占用空间小和可移植性。具体来说,你可以使用 Node.js API 为 WasmEdge 创建微服务!
对于 Node.js 应用程序,WasmEdge 占用空间小于 15MB。相比之下,标准 Node.js Linux 映像超过 150MB。
运行以下两个 CLI 命令,从我们用于 WasmEdge + Node.js 的超薄 Docker 映像创建并部署一个 fly.io 应用程序。
$ flyctl launch --image juntaoyuan/flyio-nodejs-echo
$ flyctl deploy
这就是了!你可以使用 curl 命令来测试所部署的网络服务是否真的工作。无论你向它发布什么数据,它都会回显出来。
$ curl https://solitary-snowflake-1159.fly.dev -d "Hello WasmEdge for Node.js on fly.io!"
Hello WasmEdge for Node.js on fly.io!
juntaoyuan/flyio-nodejs-echo
Docker 镜像的 Docker 文件包含 WasmEdge 运行时、QuickJS 运行时wasmedge_quickjs.wasm
、Node.js 模块和网络服务应用程序node_echo.js
的完整包。
FROM wasmedge/slim-runtime:0.11.0
ADD wasmedge_quickjs.wasm /
ADD node_echo.js /
ADD modules /modules
CMD ["wasmedge", "--dir", ".:/", "/wasmedge_quickjs.wasm", "node_echo.js"]
node_echo.js
应用程序的完整 JavaScript 源代码如下。你可以清楚地看到,它只是使用标准的 Node.js APIs 来创建一个异步的 HTTP 服务器,回显 HTTP 请求体。
import { createServer, request, fetch } from 'http';
createServer((req, resp) => {
req.on('data', (body) => {
resp.end(body)
})
}).listen(8080, () => {
print('listen 8080 ...\n');
})
WasmEdge 的 QuickJS 引擎不仅提供 Node.js 支持,还提供 Tensorflow 推理支持。我们将 Rust Tensorflow 和 WASI-NN SDKs 包装成 JavaScript APIs,以便 JavaScript 开发人员可以轻松创建 AI 人工智能应用程序。
使用 WasmEdge,也可以创建由数据库支持的有状态的微服务。这个 GitHub repo 包含了 WasmEdge 应用程序中基于 tokio 的非阻塞数据库客户端的例子。
你现在可以使用 WasmEdge SDK 和运行时在边缘云上构建各种 Web 服务。期待很快可以看到你的作品!