CRI-O + Kata containers + Weavenetでkubernetesをインストールする


普段はCRIはDocker、OCIはrunc、CNIはcalicoで構成することが多いのだけど、たまには違う構成でもとってみようと思いインストールしてみる。 特にこれまでKata containersはさわったことなかったので。 OSはUbuntuを適当に入れた

Kataのインストール

https://github.com/kata-containers/documentation/blob/master/install/ubuntu-installation-guide.md

ARCH=$(arch)
BRANCH="${BRANCH:-master}"
sudo sh -c "echo 'deb http://download.opensuse.org/repositories/home:/katacontainers:/releases:/${ARCH}:/${BRANCH}/xUbuntu_$(lsb_release -rs)/ /' > /etc/apt/sources.list.d/kata-containers.list"
curl -sL  http://download.opensuse.org/repositories/home:/katacontainers:/releases:/${ARCH}:/${BRANCH}/xUbuntu_$(lsb_release -rs)/Release.key | sudo apt-key add -
sudo -E apt-get update
sudo -E apt-get -y install kata-runtime kata-proxy kata-shim

CRI-Oのインストール

https://github.com/cri-o/cri-o#installing-cri-o

. /etc/os-release
sudo sh -c "echo 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/x${NAME}_${VERSION_ID}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list"
wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/x${NAME}_${VERSION_ID}/Release.key -O- | sudo apt-key add -

sudo apt-get update -qq
apt-get install -y cri-o-1.17
sudo systemctl enable crio

Ubuntuのパッケージは、1.18がまだ無いようなので1.17を利用した。

CRI-Oのランタイムの設定

https://github.com/kata-containers/documentation/blob/master/how-to/run-kata-with-k8s.md#cri-o

/etc/crio/crio.conf に書かれている設定を入れた。 デフォルトはruncのままにしてある。

[crio.runtime.runtimes.kata-runtime]
  runtime_path = "/usr/bin/kata-runtime"
  runtime_type = "oci"

kubernetesのインストール

kubeadmでインストール。

全ノードで

sudo modprobe overlay
sudo modprobe br_netfilter
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system

sudo apt-get update && sudo apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
sudo apt-get update
sudo apt-get install -y kubelet=1.17.0-00 kubeadm=1.17.0-00 kubectl=1.17.0-00
sudo apt-mark hold kubelet kubeadm kubectl

cat <<EOF | sudo tee /etc/systemd/system/kubelet.service.d/0-crio.conf
[Service]
Environment="KUBELET_EXTRA_ARGS=--container-runtime=remote --cgroup-driver=systemd --runtime-request-timeout=15m --container-runtime-endpoint=unix:///var/run/crio/crio.sock"
EOF
sudo systemctl daemon-reload
sudo systemctl restart kubelet

コントロールプレーンで以下を実行。

sudo kubeadm init --skip-preflight-checks --cri-socket /var/run/crio/crio.sock --pod-network-cidr=10.244.0.0/16

実行後には、joinコマンドが表示されるので、今度はそれを各ノードで実行する。もし、見逃してしまった場合は、以下のコマンドで再表示できる。

kubeadm token create --print-join-command

前に入れたときは、CNIプラグイン入れないとNodeの状態がREADYにならなかったはずなのに、 今回試したらNodeが参加した時点でREADYになってた。ランタイムが違うから?そんなことある?

とりあえず、WeaveNetをいれておく。

kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"

クラスタのテスト

OCIとして、runcを使うPodとkataを使うPodをデプロイしてみる

kubectl run hello-runc --image=gcr.io/google-samples/hello-app:1.0 --restart Never
cat <<EOF | kubectl apply -f -
apiVersion: node.k8s.io/v1beta1
kind: RuntimeClass
metadata:
  name: kata
handler: kata-runtime
EOF
kubectl get pod hello-runc -o yaml > hello-kata.yaml

hello-kata.yamlを以下の通り編集

apiVersion: v1
kind: Pod
metadata:
  labels:
    run: hello-kata
  name: hello-kata
spec:
  containers:
  - image: gcr.io/google-samples/hello-app:1.0
    imagePullPolicy: IfNotPresent
    name: hello-kata
  dnsPolicy: ClusterFirst
  restartPolicy: Never
  runtimeClassName: kata

これを流したんたけどPodが起動しない。eventを見てみると以下のようなログが。

Failed to create pod sandbox: rpc error: code = Unknown desc = container create failed: failed to launch qemu: exit status 1, error messages from qemu log: Could not access KVM kernel module: No such file or directory
qemu-vanilla-system-x86_64: failed to initialize kvm: No such file or directory

今回ESXi上の仮想マシンでやったのだけど、CPUの仮想化を有効にするの忘れてた。仮想マシンの設定変更から、 「CPU仮想化 ハードウェア アシストによる仮想化をゲストOSに公開」を有効にしたところ解決。

kubectl get pod -o wide
NAME         READY   STATUS    RESTARTS   AGE   IP          NODE    NOMINATED NODE   READINESS GATES
hello-kata   1/1     Running   0          9h    10.32.0.2   node1   <none>           <none>
hello-runc   1/1     Running   0          9h    10.38.0.3   node2   <none>           <none>

無事に起動したっぽい。

動作を見比べる

うまいことnode1とnode2に分散してPodを動かしたので、通常のruncで動くパターンとkataで動くパターンのプロセス構成などを見てみる。

kata-runtime list

kataで動いているコンテナのリストは、 kata-runtime list で確認することができる。

  • Node1 (kata利用)
$ sudo kata-runtime list
ID                                                                 PID         STATUS      BU
NDLE                                                                                                                 CREATED                          OWNER
fa157caa041230c1593ced717618dc2f96a80f4c0704b7d965421a8e95dc791f   2850        running     /run/containers/storage/overlay-containers/fa157caa041230c1593ced717618dc2f96a80f4c0704b7d965421a8e95dc791f/userdata   2020-07-11T23:51:20.244499159Z   #0
4fe1ddb9154cbfc14a7ca514e2705b91f54bfc9b89300c940ff1000b2f0bd17c   3115        running     /run/containers/storage/overlay-containers/4fe1ddb9154cbfc14a7ca514e2705b91f54bfc9b89300c940ff1000b2f0bd17c/userdata   2020-07-11T23:51:26.190503017Z   #0
  • Node2 (runc利用)
$ sudo kata-runtime list
ID          PID         STATUS      BUNDLE      CREATED     OWNER

たしかに、Node1では動いているプロセスがいて、Node2にはいないことがわかる。 でも、なんで2つ? Podはひとつしか起動してないのに。

もう少しNode1側を詳しく見てみる。

$ sudo kata-runtime state fa157caa041230c1593ced717618dc2f96a80f4c0704b7d965421a8e95dc791f
{
  "ociVersion": "1.0.1-dev",
  "id": "fa157caa041230c1593ced717618dc2f96a80f4c0704b7d965421a8e95dc791f",
  "status": "running",
  "pid": 2850,
  "bundle": "/run/containers/storage/overlay-containers/fa157caa041230c1593ced717618dc2f96a80f4c0704b7d965421a8e95dc791f/userdata",
  "annotations": {
    "io.katacontainers.pkg.oci.bundle_path": "/run/containers/storage/overlay-containers/fa157caa041230c1593ced717618dc2f96a80f4c0704b7d965421a8e95dc791f/userdata",
    "io.katacontainers.pkg.oci.container_type": "pod_sandbox"
  }
}
$ sudo kata-runtime state 4fe1ddb9154cbfc14a7ca514e2705b91f54bfc9b89300c940ff1000b2f0bd17c
{
  "ociVersion": "1.0.1-dev",
  "id": "4fe1ddb9154cbfc14a7ca514e2705b91f54bfc9b89300c940ff1000b2f0bd17c",
  "status": "running",
  "pid": 3115,
  "bundle": "/run/containers/storage/overlay-containers/4fe1ddb9154cbfc14a7ca514e2705b91f54bfc9b89300c940ff1000b2f0bd17c/userdata",
  "annotations": {
    "io.katacontainers.pkg.oci.bundle_path": "/run/containers/storage/overlay-containers/4fe1ddb9154cbfc14a7ca514e2705b91f54bfc9b89300c940ff1000b2f0bd17c/userdata",
    "io.katacontainers.pkg.oci.container_type": "pod_container"
  }
}

コンテナタイプが違うのがわかる。公式のドキュメントのアーキテクチャのところを見ると、 pod_sandboxの中に、pod_containerがあるようだ。

https://github.com/kata-containers/documentation/blob/master/design/architecture.md

$ sudo kata-runtime exec 4fe1ddb9154cbfc14a7ca514e2705b91f54bfc9b89300c940ff1000b2f0bd17c ps
PID   USER     TIME   COMMAND
    1 root       0:00 ./hello-app
   28 root       0:00 ps
$ sudo kata-runtime exec fa157caa041230c1593ced717618dc2f96a80f4c0704b7d965421a8e95dc791f ps
rpc error: code = Internal desc = Could not run process: container_linux.go:349: starting container process caused "exec: \"ps\": executable file not found in $PATH"

pod_contaierの方で、期待するアプリが動いていることが確認できた。sandboxのほうは、shすら起動できなかったので、何が動いているんだろうか。

psの結果

プロセスツリーも見比べてみた。適当にプロセスは実際のものから削っている。

  • Node1 (kata利用)
systemd-+-2*[conmon-+-pause]
        |           `-{conmon}]
        |-conmon-+-kube-proxy---7*[{kube-proxy}]
        |        `-{conmon}
        |-conmon-+-kube-utils---8*[{kube-utils}]
        |        |-launch.sh---weaver---15*[{weaver}]
        |        `-{conmon}
        |-conmon-+-kata-proxy---8*[{kata-proxy}]
        |        |-kata-shim---8*[{kata-shim}]
        |        |-qemu-vanilla-sy---3*[{qemu-vanilla-sy}]
        |        `-{conmon}
        |-conmon-+-weave-npc-+-ulogd
        |        |           `-9*[{weave-npc}]
        |        `-{conmon}
        |-conmon-+-kata-shim---10*[{kata-shim}]
        |        `-{conmon}
        |-crio---14*[{crio}]
        |-kubelet---16*[{kubelet}]
        `-lxcfs---2*[{lxcfs}]
  • Node2 (runc利用)
systemd-+-3*[conmon-+-pause]
        |           `-{conmon}]
        |-conmon-+-kube-proxy---8*[{kube-proxy}]
        |        `-{conmon}
        |-conmon-+-kube-utils---8*[{kube-utils}]
        |        |-launch.sh---weaver---16*[{weaver}]
        |        `-{conmon}
        |-conmon-+-weave-npc-+-ulogd
        |        |           `-9*[{weave-npc}]
        |        `-{conmon}
        |-conmon-+-hello-app---3*[{hello-app}]
        |        `-{conmon}
        |-crio---14*[{crio}]
        |-kubelet---16*[{kubelet}]
        `-lxcfs---2*[{lxcfs}]

見比べてみると、たしかにruncだと目的のhello-appが直接動いているのに対して、 kataの場合は、hello-appは直接ホストから見えない。 kata-shimで隠蔽されていて、隔離された環境で動いていることがわかる。

まとめ

Kata Containersは、これまで安全にコンテナ実行するために使う、ぐらいしか聞いておらず どういう風に動くのかよくわかっていなかったが、今回構築してみてその動きが理解できた。 構築も、ドキュメントによって書いてあること違ったりでいくつかトラブルところもあったが、 だいたいログ見たらどこがあやしいかわかるし、それほど苦労することはなかった。 1枚噛んでるレイヤが増えるので、性能面とリソースのオーバーヘッドが気になるので、今後その辺見てみたい。


関連記事