无容器!如何使用 Rust 在 Kubernetes 上运行 WebAssembly 工作负载 Kubernetes 上的 WebAssembly 为何选择 Rust?开启 Krustlet 之旅 使用 Krustlet 在 Kubernetes 上运行 WebAssembly 工作负载 那么,我们准备好用 WebAssembly 取代容器了吗?了解更多关于 Kubernetes 和 WebAssembly 的信息

2025-06-07

无容器!如何使用 Rust 在 Kubernetes 上运行 WebAssembly 工作负载

Kubernetes 上的 WebAssembly

为什么使用 Rust?

进入 Krustlet

使用 Krustlet 在 Kubernetes 上运行 WebAssembly 工作负载

那么,我们准备好用 WebAssembly 替换容器了吗?

了解有关 Kubernetes 和 WebAssembly 的更多信息

WebAssembly (Wasm) 是近年来最令人兴奋却又被低估的软件技术之一。它是一种基于堆栈的虚拟机的二进制指令格式,旨在通过内存安全且可靠的沙盒以原生速度执行。Wasm 具有可移植性、跨平台性和语言无关性,旨在作为多种语言的编译目标。虽然 Wasm 最初是开放 Web 平台的一部分,但它已在 Web 领域之外找到了应用场景。WebAssembly 目前已应用于浏览器、Node.js、Deno、Kubernetes 和物联网平台。

您可以在WebAssembly.org上了解有关 WebAssembly 的更多信息。

Kubernetes 上的 WebAssembly

尽管 WebAssembly 最初是为Web设计的,但它已被证明是编写与平台和语言无关的应用程序的理想格式。您可能知道容器世界中存在类似的容器——Docker 容器。包括 Docker 联合创始人 Solomon Hykes 在内的许多人都认识到了这种相似性,并认为 WebAssembly 效率更高,因为它快速、可移植、安全,并且能够以原生速度运行。这意味着您可以将 WebAssembly 与容器一起用作 Kubernetes 上的工作负载。另一项名为WebAssembly 系统接口 (WASI)的 WebAssembly 计划以及Wasmtime项目使这成为可能。

Kubernetes 上的 WebAssembly 相对较新,目前还存在一些缺陷,但它已被证明具有革命性。Wasm 工作负载可以非常快速,因为它们的执行速度比容器的启动速度更快。这些工作负载被沙盒化,因此比容器更安全;由于采用二进制格式,它们的大小也比容器小得多。

如果您想了解有关 WASI 的更多信息,请查看Mozilla 的原始公告

为什么使用 Rust?

我之前写过一篇博文,解释了为什么 Rust 是一门面向未来的优秀语言。简而言之,Rust 安全快速,且没有大多数现代语言的妥协,而且 Rust 拥有WebAssembly 的最佳生态系统和工具。因此,Rust + Wasm 使其超级安全快速。

进入 Krustlet

Krustlet是一个用 Rust 编写的Kubelet,适用于 WebAssembly 工作负载(可以使用任何语言编写)。它根据tolerations清单中的指定内容监听新的 Pod 分配并运行它们。由于默认的 Kubernetes 节点无法原生运行 Wasm 工作负载,因此您需要一个能够运行 Wasm 工作负载的 Kubelet,而这正是 Krustlet 的用武之地。

使用 Krustlet 在 Kubernetes 上运行 WebAssembly 工作负载

今天,您将使用 Krustlet 在 Kubernetes 上运行用 Rust 编写的 Wasm 工作负载。

先决条件

准备集群

首先,你需要准备一个集群,并在集群上安装 Krustlet 以在其上运行 WebAssembly。我使用kind来运行本地 Kubernetes 集群;你也可以使用MiniKubeMicroK8s其他 Kubernetes 发行版

第一步是使用以下命令创建集群:

kind create cluster
Enter fullscreen mode Exit fullscreen mode

现在你需要启动 Krustlet。为此,你需要kubectl安装一个 kubeconfig,该 kubeconfig 有权Secretskube-system命名空间中创建并批准CertificateSigningRequests。你可以使用 Krustlet 提供的这些便捷脚本,下载并运行适合你操作系统的安装脚本:

# Setup for Linux/macOS
curl https://raw.githubusercontent.com/krustlet/krustlet/main/scripts/bootstrap.sh | /bin/bash
Enter fullscreen mode Exit fullscreen mode

现在您可以安装并运行 Krustlet。

从发布页面下载二进制版本并运行。请根据你的操作系统下载合适的版本。

# Download for Linux
curl -O https://krustlet.blob.core.windows.net/releases/krustlet-v1.0.0-alpha.1-linux-amd64.tar.gz
tar -xzf krustlet-v1.0.0-alpha.1-linux-amd64.tar.gz
# Install for Linux
KUBECONFIG=~/.krustlet/config/kubeconfig \
  ./krustlet-wasi \
  --node-ip=172.17.0.1 \
  --node-name=krustlet \
  --bootstrap-file=${HOME}/.krustlet/config/bootstrap.conf
Enter fullscreen mode Exit fullscreen mode

注意:如果您使用的是 Mac 版 Docker,则 node-ip 会有所不同。请按照Krustlet 文档中的说明来获取 IP 地址。如果出现错误,您可以使用macOS系统偏好设置>安全和隐私>常规中的“仍然允许”按钮krustlet-wasi cannot be opened because the developer cannot be verified来允许该错误。

您应该会看到一个手动批准 TLS 证书的提示,因为 Krustlet 使用的服务证书必须手动批准。打开一个新的终端并运行以下命令。主机名将显示在 Krustlet 服务器的提示符中。

kubectl certificate approve <hostname>-tls
Enter fullscreen mode Exit fullscreen mode

仅在首次启动 Krustlet 时才需要执行此操作。请保持 Krustlet 服务器运行。您可能会看到一些错误记录,但由于 Krustlet 仍处于测试阶段,存在一些不完善之处,因此我们暂时忽略它。

让我们看看节点是否可用。运行kubectl get nodes,你应该会看到类似这样的内容:

kubectl get nodes -o wide
# Output
NAME                 STATUS   ROLES                  AGE   VERSION         INTERNAL-IP   EXTERNAL-IP   OS-IMAGE       KERNEL-VERSION            CONTAINER-RUNTIME
kind-control-plane   Ready    control-plane,master   16m   v1.21.1         172.21.0.2    <none>        Ubuntu 21.04   5.15.12-200.fc35.x86_64   containerd://1.5.2
krustlet             Ready    <none>                 12m   1.0.0-alpha.1   172.17.0.1    <none>        <unknown>      <unknown>                 mvp
Enter fullscreen mode Exit fullscreen mode

现在,让我们通过应用下面的 Wasm 工作负载来测试 Krustlet 是否按预期工作。如您所见,我们已经进行了tolerations定义,因此它不会在普通节点上进行调度。

kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: hello-wasm
spec:
  containers:
  - name: hello-wasm
    image: webassembly.azurecr.io/hello-wasm:v1
  tolerations:
  - effect: NoExecute
    key: kubernetes.io/arch
    operator: Equal
    value: wasm32-wasi   # or wasm32-wasmcloud according to module target arch
  - effect: NoSchedule
    key: kubernetes.io/arch
    operator: Equal
    value: wasm32-wasi
EOF
Enter fullscreen mode Exit fullscreen mode

一旦应用,kubectl get pods按如下所示运行,您应该会看到 pod 在 Krustlet 节点上运行。

kubectl get pods --field-selector spec.nodeName=krustlet
# Output
NAME                    READY   STATUS       RESTARTS   AGE
hello-wasm              0/1     ExitCode:0   0          71m
Enter fullscreen mode Exit fullscreen mode

不必担心状态。正常终止的工作负载出现 是正常的ExitCode:0。让我们通过运行 来检查 Pod 的日志kubectl logs

kubectl logs hello-wasm
# Output
Hello, World!
Enter fullscreen mode Exit fullscreen mode

您已成功设置可以在集群上运行 Wasm 工作负载的 Kubelet。

为 WebAssembly 设置 Rust

现在让我们用 Rust 为 WebAssembly 准备一个环境。请确保你使用的是稳定的 Rust 版本,而不是夜间版本。

首先,您需要添加wasm32-wasiRust 目标,以便将 Rust 应用编译为 WebAssembly。运行以下命令:

rustup target add wasm32-wasi
Enter fullscreen mode Exit fullscreen mode

现在您可以使用 Cargo 创建一个新的 Rust 应用程序。

cargo new --bin rust-wasm
Enter fullscreen mode Exit fullscreen mode

在您喜欢的 IDE 中打开创建的rust-wasm文件夹。我使用 Visual Studio Code 以及出色的rust-analyzerCodeLLDB插件进行 Rust 开发。

创建 WebAssembly 工作负载

让我们编写一个小服务,在控制台上打印随机的猫咪信息。为此,你可以使用一个提供随机猫咪信息的免费公共 API

编辑cargo.toml并添加以下依赖项:

[dependencies]
wasi-experimental-http = "0.7"
http = "0.2.5"
serde_json = "1.0.74"
env_logger = "0.9"
log = "0.4"
Enter fullscreen mode Exit fullscreen mode

然后编辑src/main.rs并添加以下代码:

use http;
use serde_json::Value;
use std::{str, thread, time};

fn main() {
    env_logger::init();
    let url = "https://catfact.ninja/fact".to_string();
    loop {
        let req = http::request::Builder::new()
            .method(http::Method::GET)
            .uri(&url)
            .header("Content-Type", "text/plain");
        let req = req.body(None).unwrap();

        log::debug!("Request: {:?}", req);

        // send request using the experimental bindings for http on wasi
        let mut res = wasi_experimental_http::request(req).expect("cannot make request");

        let response_body = res.body_read_all().unwrap();
        let response_text = str::from_utf8(&response_body).unwrap().to_string();
        let headers = res.headers_get_all().unwrap();

        log::debug!("{}", res.status_code);
        log::debug!("Response: {:?} {:?}", headers, response_text);

        // parse the response to json
        let cat_fact: Value = serde_json::from_str(&response_text).unwrap();

        log::info!("Cat Fact: {}", cat_fact["fact"].as_str().unwrap());

        thread::sleep(time::Duration::new(60, 0));
    }
}
Enter fullscreen mode Exit fullscreen mode

代码很简单。它向 API 发出 GET 请求,每 60 秒解析并打印响应。现在,您可以使用以下 Cargo 命令将其构建为 Wasm 二进制文件:

cargo build --release --target wasm32-wasi
Enter fullscreen mode Exit fullscreen mode

就是这样。您已成功使用 Rust 创建了 WebAssembly 二进制文件。

在本地运行工作负载(可选)

让我们使用Wasmtime在本地运行工作负载,Wasmtime 是一个适用于 Wasm 和 WASI 的小型 JIT 风格运行时。由于 Wasmtime 不支持开箱即用的网络,我们需要使用wasi-experimental-http提供的包装器。您可以使用以下命令从源代码构建它。

git clone https://github.com/deislabs/wasi-experimental-http.git
# Build for your platform
cargo build
# move to any location that is added to your PATH variable
mv ./target/debug/wasmtime-http ~/bin/wasmtime-http
Enter fullscreen mode Exit fullscreen mode

现在从项目文件夹运行以下命令rust-wasm

wasmtime-http target/wasm32-wasi/release/rust-wasm.wasm -a https://catfact.ninja/fact -e RUST_LOG=info
Enter fullscreen mode Exit fullscreen mode

在 Kubernetes 中运行工作负载

在 Kubernetes 中运行工作负载之前,您需要将二进制文件推送到支持 OCI 构件的注册表。符合 OCI 标准的注册表可用于任何 OCI 构件,包括 Docker 镜像、Wasm 二进制文件等等。Docker Hub 目前不支持 OCI 构件;因此,您可以使用其他注册表,例如GitHub Package RegistryAzure Container RegistryGoogle Artifact Registry。我将使用 GitHub Package Registry,因为它最容易上手,而且大多数人可能已经拥有 GitHub 帐户。

首先,您需要使用 登录 GitHub Package Registry 。在 GitHub 上docker login创建具有作用域的个人访问令牌,并使用它来登录注册表。write:packages

export CR_PAT=<your-token>
echo $CR_PAT | docker login ghcr.io -u <Your GitHub username> --password-stdin
Enter fullscreen mode Exit fullscreen mode

现在,您需要将 Wasm 二进制文件推送为 OCI 工件;为此,您可以使用wasm-to-oci CLI。使用以下命令将其安装到您的计算机上。请下载适合您操作系统的版本。

# Install for Linux
curl -LO https://github.com/engineerd/wasm-to-oci/releases/download/v0.1.2/linux-amd64-wasm-to-oci
# move to any location that is added to your PATH variable
mv linux-amd64-wasm-to-oci ~/bin/wasm-to-oci
Enter fullscreen mode Exit fullscreen mode

现在,您可以将之前构建的二进制文件推送到 GitHub 包注册表。从rust-wasm文件夹运行以下命令。

wasm-to-oci push target/wasm32-wasi/release/rust-wasm.wasm ghcr.io/<your GitHub user>/rust-wasm:latest
Enter fullscreen mode Exit fullscreen mode

您应该会看到一条成功消息。现在检查您个人资料中的 GitHub Packages 页面,您应该会看到列出的工件。

GitHub 软件包注册表

默认情况下,该工件是私有的,但您需要将其公开,以便可以从 Krustlet 集群访问它。单击包名称,然后单击“包设置”按钮,向下滚动,单击“更改可见性”,然后更改为“公共”

您可以使用以下命令拉取工件来检查这一点:

wasm-to-oci pull ghcr.io/<your GitHub user>/rust-wasm:latest
Enter fullscreen mode Exit fullscreen mode

耶!您已成功将第一个 Wasm 工件推送到 OCI 注册表。现在,让我们将其部署到您之前创建的 Kubernetes 集群。

创建一个 YAML 文件,例如k8s.yaml,包含以下内容:

apiVersion: v1
kind: Pod
metadata:
  name: rust-wasi-example
  labels:
    app: rust-wasi-example
  annotations:
    alpha.wasi.krustlet.dev/allowed-domains: '["https://catfact.ninja/fact"]'
    alpha.wasi.krustlet.dev/max-concurrent-requests: "42"
spec:
  automountServiceAccountToken: false
  containers:
    - image: ghcr.io/<your GitHub user>/rust-wasm:latest
      imagePullPolicy: Always
      name: rust-wasi-example
      env:
        - name: RUST_LOG
          value: info
        - name: RUST_BACKTRACE
          value: "1"
  tolerations:
    - key: "node.kubernetes.io/network-unavailable"
      operator: "Exists"
      effect: "NoSchedule"
    - key: "kubernetes.io/arch"
      operator: "Equal"
      value: "wasm32-wasi"
      effect: "NoExecute"
    - key: "kubernetes.io/arch"
      operator: "Equal"
      value: "wasm32-wasi"
      effect: "NoSchedule"
Enter fullscreen mode Exit fullscreen mode

注意<your GitHub user>:请记住用您自己的 GitHub 用户名替换。

annotations非常tolerations重要。注解用于允许来自 krustlet 的外部网络调用,而容忍度则限制 Pod 只能在 Wasm 节点上调度/运行。我们还传递了一些应用程序将使用的环境变量。

现在使用以下命令应用清单并检查 pod 状态。

kubectl apply -f k8s.yaml

kubectl get pods -o wide
# Output
NAME                READY   STATUS    RESTARTS   AGE     IP       NODE       NOMINATED NODE   READINESS GATES
rust-wasi-example   1/1     Running   0          6m50s   <none>   krustlet   <none>           <none>
Enter fullscreen mode Exit fullscreen mode

您应该看到 Wasm 工作负载在 Krustlet 节点上成功运行。让我们检查一下日志。

kubectl logs rust-wasi-example
# Output
[2022-01-16T11:42:20Z INFO  rust_wasm] Cat Fact: Polydactyl cats (a cat with 1-2 extra toes on their paws) have this as a result of a genetic mutation. These cats are also referred to as 'Hemingway cats' because writer Ernest Hemingway reportedly owned dozens of them at his home in Key West, Florida.
[2022-01-16T11:43:21Z INFO  rust_wasm] Cat Fact: The way you treat a kitten in the early stages of its life will render its personality traits later in life.
Enter fullscreen mode Exit fullscreen mode

太棒了!您已成功使用 Rust 创建 Wasm 工作负载,并将其部署到 Kubernetes 集群,无需使用容器。
如果您想完整了解此解决方案,请查看GitHub 代码库

那么,我们准备好用 WebAssembly 替换容器了吗?

Kubernetes 上的 WebAssembly 尚未达到生产环境要求,因为许多支持生态系统仍处于试验阶段,而 WASI 本身也仍在不断完善中。网络功能尚不稳定,库生态系统也才刚刚起步。Krustlet 也仍处于测试阶段,目前尚无直接运行网络工作负载(尤其是在其上运行服务器)的方法。WasmEdge是一个更成熟的网络工作负载替代方案,但与 Kubernetes 上的 Krustlet 相比,它的设置和运行要复杂得多。WasmCloud是另一个值得关注的项目。因此,就目前而言,Krustlet 适合运行作业的工作负载以及涉及集群监控等用例。无论如何,这些领域都可以利用其额外的性能。

因此,尽管 Kubernetes 上的 Wasm 令人兴奋,而且 Kubernetes 上的无容器化也已近在眼前,但容器化应用仍然是生产环境的主流选择。对于微服务和 Web 应用等网络工作负载尤其如此。但是,鉴于生态系统的快速发展,尤其是在 Rust + Wasm + WASI 领域,我预计我们很快就能在 Kubernetes 上使用 Wasm 工作负载进行生产。

了解有关 Kubernetes 和 WebAssembly 的更多信息

如果您想了解有关 Kubernetes 和安全性的更多信息,请查看这些附加资源。

如果您喜欢本教程,那么您很可能会喜欢我们发布的其他教程。请在 Twitter 上关注 @oktadev,并订阅我们的 YouTube 频道,以便在我们发布新的开发者教程时收到通知。

文章来源:https://dev.to/oktadev/containerless-how-to-run-web assembly-workloads-on-kubernetes-with-rust-2j8f
PREV
我希望自己知道的事情(编程四年之前)——第一部分 ℹ️ 结论
NEXT
使用 Node、React 和 Okta 构建用户注册