在Kubernetes上运行WASM负载(基于Krustlet)
环境
Ubuntu 64位 18.04 8G RAM(VMWare Workstation Pro 虚拟机上运行)
基本流程描述
使用Krustlet在Kubernetes本地运行WASM,为了在krustlet节点上运行一个应用,使用rust编写了一个简单的程序并编译为WASM格式,推送到OCI仓库中(选择使用
具体操作流程
官方安装教程: 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前必须先安装kubectl,它是K8s的命令行工具,使得你可以对 Kubernetes 集群运行命令。 你可以使用 kubectl 来部署应用、监测和管理集群资源以及查看日志。
安装kubectl
-
下载kubectl最新版本:
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
-
验证该可执行文件:
-
下载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
-
-
安装kubectl,执行:
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
-
测试是否安装成功
kubectl version --client
出现如上图中信息即为安装成功。
安装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
使用自定义配置文件创建
创建时若要自定义配置集群,可以通过编写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负载:
至此,我们就得到了一个可以运行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扩展后可右键查看
在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节点上运行的pod
这里的STATUS显示ExitCode:0,对于一个正常终止的工作负载来说是正常的,且我在其他文档和
kubectl logs hello-wasm
这代表我们已经成功地设置了一个可以在集群中运行WASM工作负载的Kubelet!下面我们尝试把自己的wasm文件推送到镜像仓库中,再拉取下来运行。
将自己的WASM module推送到OCI仓库中,再拉取下来运行
在能够将负载运行在K8s上之前,需要先将module推送到支持OCI的镜像仓库中去,本次我选择使用
首先,需要使用 docker login指令登录GitHub Package Registry,在GitHub上创建
export CR_PAT=<your-token>
echo $CR_PAT | docker login ghcr.io -u <Your GitHub username> --password-stdin
现在我们需要将 Wasm 二进制文件作为 OCI artifact 推送,需要使用 wasm-to-oci CLI。使用以下命令安装,首先在
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
检查自己Github上的package页面,可以看见已经推送成功
可以看到,目前这个包处于private状态,为了能够从kubernetes集群中获取它,我们将权限更改为public。点击这个包名,下滑找到Change visibility按钮,然后改为public即可。
我们可以用以下指令来拉取它,用以确认可行性
wasm-to-oci pull ghcr.io/<your GitHub user>/rust-wasm:latest
成功后,创建一个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
rust-wasi-example状态为Running,代表运行成功!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)