WebAssembly (Wasm) 是最近发明的最令人兴奋和被低估的软件技术之一。它是基于堆栈的虚拟机的二进制指令格式,旨在通过内存安全和安全的沙箱以本机速度执行。 Wasm 是可移植的、跨平台的且与语言无关——被设计为语言的编译目标。虽然最初是开放网络平台的一部分,但它已经在网络之外找到了用例。 WebAssembly 现在用于浏览器、Node.js、Deno、Kubernetes 和 IoT 平台。

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

Kubernetes 上的 WebAssembly

虽然最初是为the web设计的,但 WebAssembly 被证明是编写平台和与语言无关的应用程序的理想格式。您可能知道容器世界中有类似的东西——Docker 容器。包括 Docker 联合创始人 Solomon Hykes 在内的人们认识到了这种相似性,并承认 WebAssembly 的效率更高,因为它快速、便携、安全,并且以本机速度运行。这意味着您可以将 WebAssembly 与容器一起用作 Kubernetes 上的工作负载。另一个称为WebAssembly 系统接口 (WASI)的 WebAssembly 计划以及Wasmtime项目使这成为可能。

Solomon Hykes 个人资料图片

所罗门海克斯

@solomonstre

推特徽标

如果 WASM+WASI 在 2008 年存在,我们就不需要创建 Docker。这就是它的重要性。服务器上的 Webassembly 是计算的未来。标准化的系统接口是缺失的环节。让我们希望 WASI 能够胜任这项任务twitter.com/linclark/statu...

20:39 PM - 2019 年 3 月 27 日

林克拉克@linclark

在 Web 之外运行的 WebAssembly 前景广阔。今天,这个未来又向前迈进了一大步......📢 宣布 WASI:一种用于在 Web 之外(以及在其内部)运行 WebAssembly 的系统界面 https://t.co/HdEAZAyqYu

Twitter 回复操作[Twitter 转发操作](https://twitter.com/intent/retweet?tweet_id u003d1111004913222324225)推特赞动作

Kubernetes 上的 WebAssembly 相对较新,目前还有些粗糙,但它已经被证明是革命性的。 Wasm 工作负载可以非常快,因为它们的执行速度比容器启动的速度要快。工作负载是沙盒的,因此比容器安全得多;由于二进制格式,它们的大小比容器小得多。

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

为什么生锈?

我之前写过一篇关于为什么 Rust 是未来的伟大语言的博客文章, Tl;Dr; Rust 安全且快速,没有大多数现代语言的妥协,Rust 拥有最佳生态系统和用于 WebAssembly 的工具。所以 Rust + Wasm 使它超级安全和快速。

进入 Krustlet

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

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

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

先决条件

  • 码头工人

  • kubectl

  • kind或其他本地 kubernetes 分布

  • Rust 工具包(包括 rustup、rustc 和 cargo)

准备集群

首先,您需要准备一个集群并在集群上安装 Krustlet 以在其上运行 WebAssembly。我正在使用kind来运行本地 Kubernetes 集群;您还可以使用MiniKube、MicroK8s或另一个 Kubernetes 发行版。

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

kind create cluster

进入全屏模式 退出全屏模式

现在你需要引导 Krustlet。为此,您需要安装kubectl和有权在kube-system命名空间中创建Secrets并且可以批准CertificateSigningRequests的 kubeconfig。您可以使用来自 Krustlet 的这些方便的脚本下载并运行适合您操作系统的设置脚本:

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

进入全屏模式 退出全屏模式

现在您可以安装和运行 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

进入全屏模式 退出全屏模式

注意:如果你使用 Docker for Mac,node-ip 会有所不同。按照 Krustlet 文档](https://docs.krustlet.dev/howto/krustlet-on-kind/#special-note-docker-desktop-for-mac)中的说明[找出 IP。如果您收到错误krustlet-wasi cannot be opened because the developer cannot be verified,您可以在 macOS 上的 System Preferences > Security & Privacy > General 处使用 allow Anyway 按钮来允许它。

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

kubectl certificate approve <hostname>-tls

进入全屏模式 退出全屏模式

这仅在您第一次启动 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

进入全屏模式 退出全屏模式

现在让我们通过应用下面的 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

进入全屏模式 退出全屏模式

应用后,如下所示运行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

进入全屏模式 退出全屏模式

不用担心状态。对于正常终止的工作负载有ExitCode:0,它是正常。让我们通过运行kubectl logs检查 pod 的日志。

kubectl logs hello-wasm
# Output
Hello, World!

进入全屏模式 退出全屏模式

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

为 WebAssembly 设置 Rust

现在让我们用 Rust 为 WebAssembly 准备一个环境。确保您使用的是稳定的 Rust 版本,而不是每晚发布的版本。

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

rustup target add wasm32-wasi

进入全屏模式 退出全屏模式

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

cargo new --bin rust-wasm

进入全屏模式 退出全屏模式

在您喜欢的 IDE 中打开创建的rust-wasm文件夹。我使用 Visual Studio Code 和出色的rust-analyzer和CodeLLDB插件在 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"

进入全屏模式 退出全屏模式

然后编辑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));
    }
}

进入全屏模式 退出全屏模式

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

cargo build --release --target wasm32-wasi

进入全屏模式 退出全屏模式

而已。你已经使用 Rust 成功创建了一个 WebAssembly 二进制文件。

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

让我们使用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

进入全屏模式 退出全屏模式

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

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

进入全屏模式 退出全屏模式

在Kubernetes中运行工作负载

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

首先,您需要使用docker login登录 GitHub Package Registry。在 GitHub](https://github.com/settings/tokens)上创建一个[个人访问令牌,范围为write:packages并使用它登录到注册表。

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

进入全屏模式 退出全屏模式

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

# 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

进入全屏模式 退出全屏模式

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

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

进入全屏模式 退出全屏模式

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

[GitHub 包注册表](https://res.cloudinary.com/practicaldev/image/fetch/s--YvM8kVCE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to -uploads.s3.amazonaws.com/uploads/articles/ahc5sh9bjiy0llh1j38r.png)

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

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

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

进入全屏模式 退出全屏模式

耶!您已成功将您的第一个 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"

进入全屏模式 退出全屏模式

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

annotationstolerations很重要。注释用于允许来自 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>

进入全屏模式 退出全屏模式

您应该会看到 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.

进入全屏模式 退出全屏模式

惊人的。您已经使用 Rust 成功创建了 Wasm 工作负载,并将其部署到 Kubernetes 集群,而无需使用容器。

如果您想全面了解此解决方案,请查看GitHub 存储库。

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

Kubernetes 上的 WebAssembly 尚未投入生产,因为许多支持生态系统仍处于试验阶段,而 WASI 本身仍在成熟中。网络还不稳定图书馆生态系统才刚刚出现。 Krustlet 也仍处于测试阶段,没有直接的方法来运行网络工作负载,尤其是其上的服务器。WasmEdge是一种更成熟的网络工作负载替代解决方案,但与 Krustlet 在 Kubernetes 上的设置和运行相比,它的设置和运行要复杂得多。WasmCloud是另一个值得关注的项目。因此,目前,Krustlet 适用于运行工作负载以及涉及集群监控等的用例。无论如何,这些都是您可以使用额外性能的领域。

因此,虽然 Kubernetes 上的 Wasm 令人兴奋,但 Kubernetes 上的无容器肯定即将出现。容器化应用程序仍然是生产使用的方式。对于微服务和 Web 应用程序等网络工作负载尤其如此。但是,考虑到生态系统的快速发展,尤其是在 Rust + Wasm + WASI 领域,我预计很快我们将能够在 Kubernetes 上使用 Wasm 工作负载进行生产。

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

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

  • 如何使用 OpenID Connect 和 RBAC 保护 Kubernetes 集群

  • 如何使用最佳实践保护 Kubernetes 集群

  • 如何使用 Blazor WebAssembly (WASM) 安全构建

  • 保护集群

  • RBAC 与 ABAC:定义和何时使用

  • 管理员对 AWS EKS 集群的安全访问

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

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐