KubernetesのノードとしてWebAssemblyが動作するkrustletが紹介されていました。一体どういうものなのかについて公式サイトのQuickStartを実際にやってみます。
図解化するとこんな感じです。
通常はkubeletとdocker(他のコンテナランタイムでもよい)を利用してPodが起動しますが、WebAssemblyの場合はコンテナではなくバイナリ+バイナリの実行基盤のため異なる方法で起動する必要があります。それを実現しkubernetesのノードするのがkrustletの役割です。
krustletとは?
マイクロソフトが開発したツールです。以下GitHubからの引用です。
Krustlet is a tool to run WebAssembly workloads natively on Kubernetes. Krustlet acts like a node in your Kubernetes cluster. When a user schedules a Pod with certain node tolerations, the Kubernetes API will schedule that workload to a Krustlet node, which will then fetch the module and run it.
Krustletは、Kubernetes上でWebAssemblyワークロードをネイティブに実行するためのツールです。Krustletは、Kubernetesクラスタ内のノードのように動作します。ユーザーが特定のノード許容量でPodをスケジュールすると、Kubernetes APIはそのワークロードをKrustletノードにスケジュールし、そのノードがモジュールをフェッチして実行します。
Krustlet implements the kubelet API, and it will respond to common API requests like kubectl logs or kubectl delete.
Krustletはkubelet APIを実装しており、kubectlのログやkubectlの削除などの一般的なAPIリクエストに応答します。
コンテナの場合はOSのイメージが必要になるためどうしてもサイズが大きくなりがちな傾向にあります。しかしWebAssemblyの場合はコンパイルされたバイナリを実行するだけのため非常に軽量で済み、速度の向上に寄与する場合があります。
Kubernetesの実行環境としてコンテナとは別のアプローチが登場した形となります。
環境
今回作る環境はこんな感じです。
Kubernetes Nodeは以下のバージョンです
名前 | バージョン |
---|---|
OS | Fedora release 31 |
krustlet | v0.1.0 |
今回はFedoraを使います。CentOSをつかうとopenssl
やglibc
のバージョンが足りておらず以下のようなエラーがでてしまうので、初めから新しいバージョンが入っているOSで行います。
krustlet-wasi: /usr/local/lib64/libcrypto.so.1.1: version `OPENSSL_1_1_1' not found (required by krustlet-wasi) krustlet-wasi: /lib64/libc.so.6: version `GLIBC_2.18' not found (required by krustlet-wasi)
Kubernetes Masterは以下のバージョンです。
名前 | バージョン |
---|---|
OS | CentOS Linux release 7.6.1810 (Core) |
kubelet | v1.18.1 |
kubeadm | v1.18.1 |
前提条件
- kubernetesクラスタが存在していること
- 今回の手順ではMasterサーバが存在しているものとします
作業手順
以後の手順は全てKubernetes Node(Krustlet)で実施してください。
kubectlのインストール
cat <<EOF > /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg EOF # Set SELinux in permissive mode (effectively disabling it) setenforce 0 sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config yum -y install kubectl
krustletのインストール
cd /tmp wget https://krustlet.blob.core.windows.net/releases/krustlet-v0.1.0-Linux-amd64.tar.gz tar xvfpz krustlet-v0.1.0-Linux-amd64.tar.gz mv krustlet-* /usr/local/bin/ export PATH=$PATH:/usr/local/bin
kubernetes configのコピー
mkdir ~/.kube scp -p [kubernetes masterのIP]:/etc/kubernetes/admin.conf ~/.kube/config
kubernetes master上の/etc/kubernetes/admin.conf
を~/.kube/config
にコピーしてください。
証明書の作成
kubernetesAPIと通信する場合はMasterが承認した証明書を使う必要がありますので、以下の手順で作成していきます。
mkdir -p ~/.krustlet/config cd $_ openssl req -new -sha256 -newkey rsa:2048 -keyout krustlet.key -out krustlet.csr -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
CSRを作成しkubernetesに追加したら利用できるようにapproveします。そして承認された証明書をダウンロードします。
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
ではこのように作成したクライアント証明書を用いてkubernetesAPIを実行します。
krustletの起動
export KUBERNETES_SERVICE_HOST=10.0.0.187 export KUBERNETES_SERVICE_PORT=6443 krustlet-wasi --pfx-password password
KUBERNETES_SERVICE_HOST
はkubernetesクラスタのコントローラー(Master)のIPを入れてくださいKUBERNETES_SERVICE_PORT
はAPI serverのポート番号を入れてください
実行するとエラーが出ることがありますが無視してください。
[2020-04-23T14:30:52Z ERROR kubelet::kubelet] Error handling event: error decoding response body: missing field `access_token` at line 1 column 501 [2020-04-23T14:30:52Z ERROR kubelet::kubelet] Error handling event: error sending request for url (https://calico/v2/): error trying to connect: dns error: failed to lookup address information: Name or service not known
成功するとこのようになります。
[root@ip-10-0-0-116 fedora]# kubectl get nodes -o wide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME ip-10-0-0-116.ap-northeast-1.compute.internal Ready agent 3m38s v1.17.0 10.0.0.116 <none> <unknown> <unknown> mvp ip-10-0-0-187.ap-northeast-1.compute.internal Ready master 4d14h v1.18.2 10.0.0.187 <none> Red Hat Enterprise Linux 8.0 (Ootpa) 4.18.0-80.7.2.el8_0.x86_64 docker://18.9.1
ip-10-0-0-116.ap-northeast-1.compute.internal
がきちんとkubernetesクラスタに参加しています。CONTAINER-RUNTIMEがmvpになっていますね。
サンプルアプリのデプロイ
ここまでできたらGitHubで紹介されているサンプルをデプロイしてみましょう。
Hello WorldのC言語版をためす
以下の内容をk8s.yaml
という名前で保存してkubectl apply -f k8s.yaml
で適用してください。
apiVersion: v1 kind: ConfigMap metadata: name: hello-world-wasi-c data: myval: "cool stuff" --- apiVersion: v1 kind: Pod metadata: name: hello-world-wasi-c spec: containers: - name: hello-world-wasi-c image: webassembly.azurecr.io/hello-world-wasi-c:v0.1.0 env: - name: FOO value: bar - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: CONFIG_MAP_VAL valueFrom: configMapKeyRef: key: myval name: hello-world-wasi-c tolerations: - key: "krustlet/arch" operator: "Equal" value: "wasm32-wasi" effect: "NoExecute"
するとhello-world-wasi-c
が起動し終了していることがわかります。
[root@ip-10-0-0-116 fedora]# kubectl get pods NAME READY STATUS RESTARTS AGE hello-world-wasi-c 0/1 ExitCode:0 0 66s
ログを見てみます。標準出力にログが出力されており、Podの名前や変数を取得できていることがわかります。
[root@ip-10-0-0-116 fedora]# kubectl logs hello-world-wasi-c hello from stdout! hello from stderr! POD_NAME=hello-world-wasi-c CONFIG_MAP_VAL=cool stuff FOO=bar []
非常にシンプルなデモアプリですがコンテナではなくWebAssemblyが動作しました。krustletを用いることで既存のkubernetesの仕組みの上でWebAssemblyを簡単に動かすことができています。(ConfigMapでデータを渡すなどできている)
さいごに
今回はkrustletを利用したkubernetesにおけるWebAssemblyの実行についてみていきました。まだv0.1.0と生まれたてのツールですが、kubernetesの裾野を広げつつ、コンテナの次を感じさせる技術ですね。