在Kubernetes上运行WASM负载(基于Krustlet)

在Kubernetes上运行WASM负载

环境

Ubuntu 64位 18.04 8G RAM(VMWare Workstation Pro 虚拟机上运行)

基本流程描述

使用Krustlet在Kubernetes本地运行WASM,为了在krustlet节点上运行一个应用,使用rust编写了一个简单的程序并编译为WASM格式,推送到OCI仓库中(选择使用GitHub Package Registry),再将其拉取下来运行。

具体操作流程

安装docker

官方安装教程: https://docs.docker.com/engine/install/ubuntu/ 按照教程中步骤执行即可。安装后执行docker命令通常会出现如下错误:

Got permission denied while trying to connect to the Docker daemon socket

出现问题的原因在于,docker进程使用Unix Socket 而不是TCP端口。而默认情况下,Unix socket 属于root 用户,因此需要root权限才能访问。为了后续操作方便,选择将当前用户添加入docker用户组,指令如下:

sudo groupadd docker          #添加docker用户组
sudo gpasswd -a $USER docker #将当前用户添加至docker用户组
sudo newgrp docker           #更新docker用户组

命令执行后需重启系统或重启服务。

安装kubectl和kind

kind官网中介绍到kind (Kubernetes in Docker) 是一个非常方便的用于创建K8s测试集群的工具,可以使用kind创建的集群来对K8s进行测试(kind is a tool for running local Kubernetes clusters using Docker container “nodes”. kind was primarily designed for testing Kubernetes itself, but may be used for local development or CI.)kind 的架构如下,它将docker容器作为一个K8s的"node",并在该"node"中安装K8s组件。

安装kind前必须先安装kubectl,它是K8s的命令行工具,使得你可以对 Kubernetes 集群运行命令。 你可以使用 kubectl 来部署应用、监测和管理集群资源以及查看日志。

安装kubectl

  1. 下载kubectl最新版本:

    curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"

    288391054b6a3e1ca8c1d8058543849

  2. 验证该可执行文件:

    • 下载kubectl校验和文件

      curl -LO "https://dl.k8s.io/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl.sha256"
    • 使用校验和文件验整kubectl二进制文件

      echo "$(<kubectl.sha256)  kubectl" | sha256sum --check

      如果有效,输出为:

      kubectl: OK
  3. 安装kubectl,执行:

    sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
  4. 测试是否安装成功

    kubectl version --client

    出现如上图中信息即为安装成功。

安装kind

kind官网

在已安装docker和kubectl的前提下执行以下指令

curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.9.0/kind-linux-amd64
chmod +x ./kind
mv ./kind /${some-dir-in-your-PATH}/kind #{some-dir-in-your-PATH}为自定义路径,该目录必须已加入$PATH,建议选择/usr/local/bin。可以执行 cat /etc/environment 查看已加入$PATH的目录有哪些

使用kind创建K8s集群

默认方法创建

直接执行以下指令,将自动创建一个名为kind的集群

kind create cluster

创建完成后切换到该集群并运行,后续实现也是在该集群中进行。

kubectl cluster-info --context kind-kind

image-20220218110843587

使用自定义配置文件创建

创建时若要自定义配置集群,可以通过编写yaml配置文件实现,使用--config 字段来指定已有的配置文件创建新集群,可以通过自定义配置文件的方法创建多控制平面多节点的集群

  • 使用 vim 新建kind-config.yaml文件,将该配置文件中的内容复制进去并保存。

  • 执行以下指令,构建出一个根据kind-config.yaml文件配置、名为kind-2的集群。

    kind create cluster --config kind-config.yaml --name kind-2

安装Krustlet并在kind中运行

官方安装指导文档

安装二进制文件

进入 https://github.com/krustlet/krustlet/releases 下载,本次安装选择版本为v1.0.0-alpha,cd进入压缩包所在目录,解压后移入自定义的目录下(该目录必须已被添加入$PATH中,本次选择移入/usr/local/bin),执行

tar -xzf krustlet-v0.7.0-linux-amd64.tar.gz
mv krustlet-wasi /usr/local/bin/

获取bootstrap config

Krustlet在第一次运行时需要 bootstrap token 和 config,执行如下命令生成bootstrap.conf文件,默认路径为/root/.krustlet/config

bash <(curl https://raw.githubusercontent.com/krustlet/krustlet/main/scripts/bootstrap.sh)

但是,在执行过程中,由于网络原因可能会出现如下失败情况:

本处解决方法参考https://github.com/hawtim/hawtim.github.io/issues/10,在/etc 下的hosts文件中添加以下几行:

199.232.68.133 raw.githubusercontent.com
199.232.68.133 user-images.githubusercontent.com
199.232.68.133 avatars2.githubusercontent.com
199.232.68.133 avatars1.githubusercontent.com

保存后退出即可,重新执行命令情况如下:

如果想再次检验是否成功,可以检查目录/root/.krustlet/config 是否已自动生成:

确定默认网关

找到kind使用的默认网关地址,执行:

docker network ls 
docker network inspect 0db88ae157d9|grep "Gateway" #0db88ae157d9是根据上条指令结果中kind对应NETWORK ID确定的,不同机器的ID不同

docker network inspect命令用于显示一个或多个网络的详细信息,在本机上,命令执行结果如下图,可知在本机中kind默认网关地址为172.18.0.1 :

运行Krustlet

在管理员权限下执行以下指令,注意node-ip的参数即为上一步骤中获得的ip地址:

export KUBECONFIG=~/.krustlet/config/kubeconfig
krustlet-wasi --node-ip 172.18.0.1 --bootstrap-file=/root/.krustlet/config/bootstrap.conf

可以看见,此时系统提示需要审批CSR,根据提示,另起一个新的终端,执行:

kubectl certificate approve ubuntu-tls  #ubuntu为hostname,根据本机实际情况填写,通常会出现在上条命令执行后的提示中

再次执行kubectl get csr确认,发现ubuntu-tls对应的状态已改变为Approved

上述步骤完成后进行校验,执行

kubectl get nodes -o wide

可以看到新增了一个节点ubuntu,在该节点上可以运行WebAssembly负载:image-20220218145145477

至此,我们就得到了一个可以运行WASM的节点,下面,我们选择自己编写一个简单的RUST程序并将其转译为WASM文件,再将其推送到镜像仓库中去,从而实现在Kubernetes中运行WASM。

创建RUST样例并转为WASM文件格式

编写样例及转译选择使用VSCode,因为它带有成熟的rust扩展和WASM相关扩展插件。

创建rust样例

初次使用VSCode编写样例参考了本链接中内容:https://www.runoob.com/rust/rust-setup.html,需要安装rust工具包(rustup,rustc,cargo),VSCode中安装rls 和 Native Debug 两个扩展。终端执行:

cargo new --bin rust-wasm

然后使用VSCode打开这个被创建出的rust-wasm文件夹,编辑其中的cargo.html文件,添加以下内容:

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

接着,编辑main.rs中的rust程序,使用样例如下:

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));
}
}

这一程序每60秒向 API 发出 GET 请求,解析和打印一次响应。

转为WASM文件格式

为了构建一个能够支持WASI的WASM模块,使用rustup在工具链中安装名为wasm32-wasi目标。

rustup target add wasm32-wasi 

执行 rustup show 可以显示当前安装的工具链信息,安装前:

安装后:

然后执行

cargo build --release --target wasm32-wasi

文件夹中会自动生成wasm文件,位置如下,VSCode在安装wasm扩展后可右键查看

image-20220218102405081

image-20220218102441714

在kubernetes上运行WASM负载

拉取官方示例中的镜像运行

在运行自己的wasm负载前,我选择先拉取官方提供的镜像示例运行,以此确认Krustlet是否可以正常运行。执行如下指令:

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

apply之后,执行 kubectl get pods,可以看见在krustlet节点上运行的podimage-20220218135432517

这里的STATUS显示ExitCode:0,对于一个正常终止的工作负载来说是正常的,且我在其他文档和教程中也见到了同样的情况,可以使用下面的指令来确认这个pod的输出日志:

kubectl logs hello-wasm

image-20220218135854562

这代表我们已经成功地设置了一个可以在集群中运行WASM工作负载的Kubelet!下面我们尝试把自己的wasm文件推送到镜像仓库中,再拉取下来运行。

将自己的WASM module推送到OCI仓库中,再拉取下来运行

在能够将负载运行在K8s上之前,需要先将module推送到支持OCI的镜像仓库中去,本次我选择使用GitHub Package Registry

首先,需要使用 docker login指令登录GitHub Package Registry,在GitHub上创建personal access token,记得勾选 write::package选项,生成后复制用以登录进registry中image-20220218003907551

image-20220218004033440

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

image-20220218104649263

现在我们需要将 Wasm 二进制文件作为 OCI artifact 推送,需要使用 wasm-to-oci CLI。使用以下命令安装,首先在官方仓库中下载linux版本的包,然后执行:

mv linux-amd64-wasm-to-oci wasm-to-oci
chmod +x wasm-to-oci
sudo cp wasm-to-oci /usr/local/bin

现在,我们就可以将之前创建的rust-wasm.wasm文件推送至GitHub Package Registry

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

image-20220218105041906

检查自己Github上的package页面,可以看见已经推送成功image-20220218004639953

可以看到,目前这个包处于private状态,为了能够从kubernetes集群中获取它,我们将权限更改为public。点击这个包名,下滑找到Change visibility按钮,然后改为public即可。image-20220218005004223

我们可以用以下指令来拉取它,用以确认可行性

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

image-20220218105439790

成功后,创建一个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"

记得用自己github账户的username去更换文件里的<your GitHub user>,切记一定要全部小写!

最后,执行以下命令应用,并检查pod的状态,running即为已经成功运行!

kubectl apply -f k8s.yaml

kubectl get pods -o wide

image-20220218141809431

rust-wasi-example状态为Running,代表运行成功!

参考链接

posted @   ZMXXX  阅读(296)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示