边缘云上的高性能且安全的微服务

边缘云让开发者能够在靠近用户的地方部署微服务(即细颗粒度的 Web 服务),从而实现更好的用户体验(即非常快的响应时间)、绝佳的安全性以及高可用性。 边缘云利用本地乃至私有的数据中心、CDN 网络和电信数据中心(例如 5G MEC)来提供计算服务。 边缘云的成功案例包括 Cloudflare、Fastly、Akamai、fly.io、Vercel、Netlify 等。

但是,与大型公共云相比,边缘云是一个资源受限的环境。 如果边缘微服务本身运行缓慢、臃肿或不安全,将导致在边缘云上部署的全部优势丧失殆尽。

本文将展示如何创建轻量级和高性能的 Web 服务,然后将其免费部署到边缘云提供商 fly.io 上。

Fly.io 是一个领先的边缘云 VM 服务提供商,它在世界各地都有边缘数据中心。fly.io VM 支持应用程序服务器、数据库以及我们这种微服务的轻量级 runtime。

我将使用 WasmEdge runtime 作为这些微服务的安全沙箱。 WasmEdge 是专门针对云原生服务优化的 WebAssembly runtime。我们将在基于 WasmEdge 的 Docker Images 中打包用 Rust 或 JavaScript 编写的微服务应用程序。这种方法有几种巨大优势。

  • WasmEdge 以接近原生的速度运行沙盒应用。根据一项经过同行评审的研究,WasmEdge 能够以近似 Linux 运行原生机器代码的速度运行 Rust 程序。
  • WasmEdge 是一个高度安全的 runtime。它可以保护你的应用程序免受外部和内部攻击威胁。
    • WasmEdge runtime 受攻击的脆弱点,相比常规 Linux OS runtime 大大减少。
    • 软件供应链攻击的风险大大降低了,因为 WebAssembly 沙箱仅允许明确声明的访问。
  • WasmEdge 在内存足迹上提供了一个完整的可移植应用程序 runtime 环境,仅是标准Linux OS runtime 镜像的1/10。
  • WasmEdge runtime 是跨平台的。这意味着机器的开发和部署不必相同。一旦创建了一个 WasmEdge 应用程序,就可以将其部署到支持 WasmEdge 的任何地方,包括 fly.io 基础设施。

如果是复杂的应用程序,该性能优势会进一步放大。 例如,WasmEdge AI 推理应用程序不需要 Python 安装。WasmEdge node.js 应用程序也无需安装 node.js 和 v8。

本文接下来,我将演示如何运行

  • 异步 HTTP 服务器(Rust)

  • 一个非常快的图片分类 Web 服务(在Rust)和

  • node.js Web服务器

  • 具有数据库连接的有状态的微服务

所有这些都可以在 WasmEdge 中快速且安全地运行,同时只消耗常规 Linux 容器所需资源的1/10。

前期准备

首先,请确保在系统上安装了 Docker 工具。如果没有,请按照本教程的第一节安装 Docker。接下来,我们将使用在线安装程序来安装 WasmEdge、Rust 和 fly.io 的 flyctl工具。

安装 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 工具。此处查看详细信息。

`curl -L `[`https://fly.io/install.sh`](https://fly.io/install.sh)` | sh`

安装好了 flyctl 之后, 请按照指引在 fly.io.上注册免费帐户。现在就可以在边缘云上部署 Web 服务了!

Rust 写的一个简单的微服务

我们的第一个示例是用 Rust 编写的简单 HTTP 服务。它演示了一个现代的 Web 应用程序,可以扩展以支持任意复杂的业务逻辑。基于流行的 Tokio 和 Hyper Crate,这个微服务是非常快、异步的(non-blocking),并且对于开发者来说非常容易创建。

完全静态链接的 WasmEdge 镜像只有 4MB(相比之下基础 Linux 镜像则为 40MB)。这足以运行用 Rust 的 tokio 和 hyper 框架编写的异步 HTTP 服务。

运行以下两个 CLI 命令,然后从 WasmEdge 的 Slim Docker 镜像创建fly.io 应用。

$ flyctl launch --image juntaoyuan/flyio-echo
$ flyctl deploy

好了!可以使用 curl 命令测试部署的 Web 服务是否成功。 它回显了你发布的任何数据。

$ curl https://proud-sunset-3795.fly.dev/echo -d "Hello WasmEdge on fly.io!"
Hello WasmEdge on fly.io!

juntaoyuan/flyio-echo 镜像的 dockerfile 包含 WasmEdge runtime 和自定义的 Web 应用程序 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 源代码项目在这里。它使用 Tokio API 启动 HTTP 服务器。 当服务器收到请求时,它会委派给 echo()函数以异步处理请求。这允许微服务接受和处理多个并发的 HTTP 请求。

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box> {
    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) -> Result, 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)
        }
    }
}

现在让我们在基本的微服务中加点料!

Rust 写的一个 AI 推理微服务

本例中,我们将创建一个用于图片分类的 Web 服务。它通过 Tensorflow Lite 模型处理上传的图片。我们将使用 WasmEdge 的 Rust API 来访问 Tensorflow,而不是创建一个复杂(且臃肿)的 Python 程序,它以本机机器代码速度运行推理任务(例如,如果有 GPU ,也可以在 GPU 硬件上运行)。通过 WASI-NN 标准,WasmEdge 的 Rust API 可以与 Tensorflow、PyTorch、OpenVINO 和其他 AI 框架中的 AI 模型一起使用。

对于包含完整 Tensorflow Lite 依赖项的 AI 推理应用程序,WasmEdge 占用空间小于 115MB。 相比之下,标准 Tensorflow Linux 镜像超过 400MB。

从 WasmEdge + Tensorflow 的 slim Docker 镜像开始,运行以下两个 CLI 命令来创建并部署一个 fly.io 应用程序。

$ flyctl launch --image juntaoyuan/flyio-classify
$ flyctl deploy

好了!你可以使用 curl 命令来测试部署的 Web 服务是否真的有效。它返回带有可能性等级的图片分类结果。

$ 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 runtime 的完整包、整个 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 源代码项目在这里。 基于 tokio 的异步 HTTP 服务器位于 async main() 函数中,如上一个示例所示。 classify() 函数处理请求中的图片数据,将图片转换为 tensor,运行 Tensorflow 模型,然后将返回值(在一个 tensor 中)转换为文本标签和所识别出内容的概率。

async fn classify(req: Request) -> Result, 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 = 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 服务客户端。

Node.js 中的一个简单的微服务

虽然基于 Rust 的微服务轻巧快速,但并不是每个人都是 Rust 开发者(目前)。如果更熟悉 JavaScript,你仍然可以充分利用 WasmEdge 在边缘云中的安全性、性能、占用空间小和可移植性!具体来说,可以使用 node.js API 为 WasmEdge 创建微服务!

对于 Node.js 应用程序,WasmEdge 占用空间小于 15MB。相比之下,标准 Node.js Linux 镜像超过 150MB。

从 WasmEdge + node.js 的 slim Docker 镜像开始,运行以下两个 CLI 命令,创建并部署一个 fly.io 应用。

$ flyctl launch --image juntaoyuan/flyio-nodejs-echo
$ flyctl deploy

好了!可以使用 curl 命令来测试部署的 Web 服务是否真的有效。它会回显你发布给它的任何数据。

$ 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 镜像的 Dockerfile 包含 WasmEdge runtime 、QuickJS runtime WasmEdge_quickjs.wasm、node.js 模块的完整包和 Web 服务应用程序 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 API 来创建一个异步 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 SDK 封装到 JavaScript API 中,以便 JavaScript 开发者可以轻松创建 AI 推理应用程序 。

边缘的状态微服务

使用 WasmEdge,还可以创建由数据库支持的有状态微服务。此 GitHub 代码库包含 WasmEdge 应用程序中基于 tokio 的 non-blocking 数据库客户端的示例。

  • MySQL client 允许 WasmEdge 应用程序访问大多数云数据库。
  • WasmEdge 应用程序可以使用anna-rs 作为边缘缓存或者数据库。

现在,快快使用 WasmEdge SDK 和 runtime 在边缘云上构建各种 Web 服务吧。已经迫不及待地想看到你的成果了!

你可能感兴趣的:(Wasm,WebAssembly,runtime,安全,微服务,架构)