在 Kubernetes 上用 Krustlet 调度 WASM
大饼夹一切
前言
坊间有两个传言:
- Kubernetes 正在成长为一个统一调度器
- WASM 如果早点成气候,就没 Docker 什么事了
Krustlet 往前踏了一小步:他的官方描述是“Kubernetes Kubelet in Rust for running WASM”——使用 Rust 实现的 Kubelet,可以在 Kubernetes 中运行 WASM。
Krustlet 是 Deis 实验室的产品,算是 Helm 的同门师兄弟。
项目还非常初期,包括镜像拉取、Pod 生命周期等功能都没能完整实现,只能作为一个概念方面的尝试。
部署
官方提供了 EKS、AKS 以及 Kind 的部署方案,这里我们使用 Kind 进行部署。使用如下配置文件定义一个三节点集群:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
- role: worker
$ kind create cluster --config=config-3-node.yaml
Creating cluster "kind" ...
✓ Ensuring node image (kindest/node:v1.17.0) 🖼
✓ Preparing nodes 📦 📦 📦 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
集群启动之后,就可以安装 Krustlet 了。
踩坑过程中,注意到 Krustlet 的几个依赖项目:
- Openssl 1.1.x 支持,所以有些老系统可能需要升级;
- 接入 Krustlet 时,需要满足接入新节点的 Kubernetes 权限要求;
- Krustlet 和 Kubelet 一样,也要使用 kubeconfig 文件进行认证。
在 Release 页面可能找到压缩包下载,目前支持 Linux 和 MacOS 两个平台。
解压之后,创建 ~/.krustlet/lib
目录,将压缩包中的 *.so
和 *.dylib
复制到 ~/.krustlet/lib
之中,可执行文件复制到 /usr/local/bin
接下来为 krustlet 准备用于加入 Kubernetes 的证书:
#!/bin/sh
mkdir -p ~/.krustlet/config
cd ~/.krustlet/config
openssl req -new -sha256 -newkey rsa:2048 -keyout krustlet.key -out krustlet.csr -days 365 -nodes -subj "/C=US/ST=./L=./O=./OU=./CN=krustlet"
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
name: krustlet
spec:
request: $(cat krustlet.csr | base64 | tr -d '\n')
usages:
- digital signature
- key encipherment
- server auth
EOF
kubectl certificate approve krustlet
kubectl get csr krustlet -o jsonpath='{.status.certificate}' | base64 --decode > krustlet.crt
openssl pkcs12 -export -out certificate.pfx -inkey krustlet.key -in krustlet.crt -password "pass:password"
启动
启动过程很简单:
$ krustlet-wasi --pfx-password password --node-ip 10.89.81.61
[2020-04-14T06:16:34Z ERROR kubelet::kubelet] Error handling event: error decoding response body: missing field `access_token` at line 1 column 501
[2020-04-14T06:18:04Z ERROR kubelet::kubelet] Error handling event: error decoding response body: missing field `detail` at line 1 column 119
[2020-04-14T06:28:37Z ERROR kubelet::kubelet] Error handling event: error decoding response body: missing field `detail` at line 1 column 119
其中的
--node-ip
参数是可选的,在 Docker for OS X 中,需要用ifconfig en0
确定一下适配器 IP,如果是在物理机/虚拟机环境中,这个参数可以省略或者根据实际情况做出调整。
是的你没看错,启动就出了点小问题。接下来看看节点情况:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
kind-control-plane Ready master 136m v1.17.0
kind-worker Ready <none> 136m v1.17.0
kind-worker2 Ready <none> 136m v1.17.0
kind-worker3 Ready <none> 136m v1.17.0
abcd-mb0 Ready agent 111m v1.17.0
看看节点的情况:
$ kubectl describe nodes abcd-mb0
...
Taints: krustlet/arch=wasm32-wasi:NoExecute
...
System Info:
Machine ID:
System UUID:
Boot ID:
Kernel Version:
OS Image:
Operating System: linux
Architecture: wasm-wasi
Container Runtime Version: mvp
Kubelet Version: v1.17.0
Kube-Proxy Version: v1.17.0
...
Operating System: linux
Architecture: wasm-wasi
Container Runtime Version: mvp
...
可以看到有很多信息是缺失的,说明项目的确很早期。
Taints
字段的定义,要求特定 Pod 才能调度到该节点上。
最后其架构显示的是 wasm-wasi。
运行一个 Pod
编写一个 YAML 文件:
apiVersion: v1
kind: Pod
metadata:
name: krustlet-tutorial
spec:
containers:
- name: krustlet-tutorial
image: webassembly.azurecr.io/hello-world-wasi-rust:v0.1.0
imagePullPolicy: Always
tolerations:
- key: "node.kubernetes.io/network-unavailable"
operator: "Exists"
effect: "NoSchedule"
- key: "krustlet/arch"
operator: "Equal"
value: "wasm32-wasi"
effect: "NoExecute"
镜像名好像很普通,Pull 一下:
$ docker pull webassembly.azurecr.io/hello-world-wasi-rust:v0.1.0
v0.1.0: Pulling from hello-world-wasi-rust
670adc713612: Pulling fs layer
invalid rootfs in image configuration
看来这个镜像并不普通。。后面会讲一下。
Pod 定义中声明了 tolerations
,让该 Pod 可以在新节点上运行。提交 yaml 之后,可以看到 Pod 运行:
$ kubectl get pods -w
NAME READY STATUS RESTARTS AGE
krustlet-tutorial 0/1 Pending 0 6s
krustlet-tutorial 0/1 Running 0 8s
krustlet-tutorial 1/1 Running 0 8s
krustlet-tutorial 0/1 ExitCode:0 0 8s
看到这个 Pod 飞快的完成了运行并成功退出,查看他的日志:
hello from stdout!
hello from stderr!
Args are: []
镜像
前面使用 docker pull
未能成功下载,这是因为这个镜像其实是一个封装为 OCI 的 WASM,可以使用 was-to-oci 工具下载,并使用 wasmtime 尝试运行:
$ wasm-to-oci pull registry.microservice.rocks/module-wasm:v1
INFO[0007] Pulled: registry.microservice.rocks/module-wasm:v1
INFO[0007] Size: 1964621
INFO[0007] Digest: sha256:670adc7136128af3a2848d86c18013b1009e7dedb8a686ecacda175094aa083c
完成后可以看到当前目录出现了一个 module.wasm
文件,运行一下:
$ wasmtime module.wasm
hello from stdout!
hello from stderr!
Args are: ["module.wasm"]
反过来,也可以使用这个工具把 WASM 文件推送到镜像库中。例如:
$ wasm-to-oci push module.wasm registry.microservice.rocks/module-wasm:v1
INFO[0001] Pushed: registry.microservice.rocks/module-wasm:v1
INFO[0001] Size: 1964621
INFO[0001] Digest: sha256:54b09224f004231ffb37d14ac478a101d94c58aac93b8da7b67ed84147763d09
玩后感
目前 Krustlet 对 Docker Registry 的支持似乎是有问题的,因此无法运行我们自己用 wasm-to-oci
推送到私库的 WASM。网络通信等内容也就无法测试了。好在目前版本只是 0.1.0,还有很多值得一等。