フラミナル

考え方や調べたことを書き殴ります。IT技術系記事多め

krustletをAWS上のEC2(kubernetes)で動かしてみよう

KubernetesのノードとしてWebAssemblyが動作するkrustletが紹介されていました。一体どういうものなのかについて公式サイトのQuickStartを実際にやってみます。

図解化するとこんな感じです。

f:id:lirlia:20200424014101p:plain

通常はkubeletdocker(他のコンテナランタイムでもよい)を利用して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の実行環境としてコンテナとは別のアプローチが登場した形となります。

環境

今回作る環境はこんな感じです。

f:id:lirlia:20200424013814p:plain

Kubernetes Nodeは以下のバージョンです

名前 バージョン
OS Fedora release 31
krustlet v0.1.0

今回はFedoraを使います。CentOSをつかうとopensslglibcのバージョンが足りておらず以下のようなエラーがでてしまうので、初めから新しいバージョンが入っている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)で実施してください。

f:id:lirlia:20200424013814p:plain

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でデータを渡すなどできている)

f:id:lirlia:20200424013814p:plain

さいごに

今回はkrustletを利用したkubernetesにおけるWebAssemblyの実行についてみていきました。まだv0.1.0と生まれたてのツールですが、kubernetesの裾野を広げつつ、コンテナの次を感じさせる技術ですね。