k0sでKata Containersを使ってみよう(QEMU編) #k0s #mirantis #kubernetes #k8s #katacontainers #qemu #microvm
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
k0sとは、軽量かつ使いやすい、100%オープンソースのKubernetesディストリビューションです。主な特徴としては、
- フル機能のKubernetesを構築するために必要なすべてを単一バイナリに同梱
- k0s特有の改変を加えていない、CNCF認定の純正なKubernetesをデプロイ
- 最低必要リソース1vCPU・1GBメモリ・2GBストレージのシングルノードから、HA構成の大規模クラスタまでサポート
- Konnectivityをデフォルトで有効化
などが挙げられます。より詳しい情報は公式ドキュメントをご覧ください。
本稿ではこのk0sを使ってKubernetesをインストールしてみるのですが、containerdがコンテナを起動するために用いるランタイムをデフォルトのruncではなく、Kata ContainersとQEMUを用いる、いわゆるMicroVM構成にしてみます。
なお、デフォルトのruncでインストールする例は以前のブログ「k0sでKubernetesをVirtualbox/Vagrantにインストールしてみよう」をご覧ください。
Kata Containersとは
通常のコンテナは、カーネルネームスペースやcgroupsなどの機能を用いてカーネルを共有しつつプロセス同士を隔離しています。この方法は軽量かつ高速ですが、カーネルを共有しているためセキュリティ的な不安が残ります。
Kata Containersはコンテナごとに専用の軽量な仮想マシンを起動し、カーネルを共有せずにプロセス同士を隔離することで、よりセキュアなコンテナ環境を実現するためのオープンソースのコンテナランタイムです。
利用できる仮想マシンは QEMU や Firecracker などがあります。さらに、Kata ContainersはシームレスにKubernetesやDockerに統合できるという特徴も持っています。
前提条件
ホストマシンにVagrantとVirtualboxがインストール済みであるとします。IPアドレスは必要に応じて適宜読み替えてください。
Vagrantfile
Vagrantfileで3台のゲストを起動します。また、入れ子で仮想マシンを起動するため、ワーカーノードには --nested-hw-virt on オプションの付与が必要となります。
IPアドレスやメモリは必要に応じて変更してください。
- master : 192.168.123.101 : Kubernetesマスターノード
- node01 : 192.168.123.201 : Kubernetesワーカーノード1
- node02 : 192.168.123.202 : Kubernetesワーカーノード2
Vagrant.configure("2") do |config| config.vm.box = "ubuntu/bionic64" config.vm.box_check_update = false config.vm.define "master" do |cf| cf.vm.hostname = "master" cf.vm.network "private_network", ip: "192.168.123.101" cf.vm.provider "virtualbox" do |vb| vb.memory = 4096 end end ( 1..2 ).each do |i| config.vm.define "node0#{i}" do |cf| cf.vm.hostname = "node0#{i}" cf.vm.network "private_network", ip: "192.168.123.20#{i}" cf.vm.provider "virtualbox" do |vb| vb.memory = 4096 vb.customize [ "modifyvm", :id, "--nested-hw-virt", "on" ] end end end end
k0sのインストールとコントロールプレーンの起動
masterゲストにて、 https://github.com/k0sproject/k0s/releases から執筆時の最新バージョンである v1.22.3+k0s.0 をダウンロードします。
vagrant@master:~$ curl -sSLf https://github.com/k0sproject/k0s/releases/download/v1.22.3%2Bk0s.0/k0s-v1.22.3+k0s.0-amd64 > k0s vagrant@master:~$ sudo mv k0s /usr/local/bin/k0s vagrant@master:~$ sudo chmod +x /usr/local/bin/k0s vagrant@master:~$ k0s version v1.22.3+k0s.0
デフォルトでのインストール設定を default-config オプションで出力し、IPアドレスを変更します。
vagrant@master:~$ k0s default-config > k0s.yaml.orig vagrant@master:~$ cp -a k0s.yaml.orig k0s.yaml vagrant@master:~$ vi k0s.yaml
差分は次の通りになります。
vagrant@master:~$ diff -u k0s.yaml.orig k0s.yaml --- k0s.yaml.orig 2021-11-15 06:02:06.571286002 +0000 +++ k0s.yaml 2021-11-15 06:09:59.899832001 +0000 @@ -5,11 +5,10 @@ name: k0s spec: api: - address: 10.0.2.15 + address: 192.168.123.101 k0sApiPort: 9443 port: 6443 sans: - - 10.0.2.15 - 192.168.123.101 controllerManager: {} images: @@ -71,7 +70,7 @@ scheduler: {} storage: etcd: - peerAddress: 10.0.2.15 + peerAddress: 192.168.123.101 type: etcd telemetry: enabled: true
ではk0sの初期設定と起動を行いましょう。
vagrant@master:~$ sudo mkdir /etc/k0s vagrant@master:~$ sudo mv k0s.yaml /etc/k0s/ vagrant@master:~$ sudo k0s install controller -c /etc/k0s/k0s.yaml vagrant@master:~$ sudo k0s start
起動の確認を行います。
vagrant@master:~$ sudo k0s status Version: v1.22.3+k0s.0 Process ID: 3334 Role: controller Workloads: false
ワーカーノードの参加トークンを払い出します。これを node01 と node02 の両ゲストにコピーしておきます。
vagrant@master:~$ sudo k0s token create --role=worker -c /etc/k0s/k0s.yaml > join-token
コントロールプレーンでは、デフォルトのインストール方法と特に違いはありません。
k0sで1つ目のワーカーノードを追加
では、ワークロードをスケジューリングするためのワーカーノードを追加しましょう。ここからデフォルトの runc を使用する手順と異なってきます。
まず、Kata Containers snap packageの手順に従い、Kata Containersをsnapでインストールします。snapでインストールするとcontained-shim-kata-v2のパスが標準と異なるため、シンボリックリンクを張っておきます。
vagrant@node01:~$ sudo snap install kata-containers --stable --classic 2021-11-15T05:34:02Z INFO Waiting for automatic snapd restart... kata-containers 2.2.3 from Kata Containers (katacontainers✓) installed vagrant@node01:~$ sudo ln -s /snap/kata-containers/current/usr/bin/containerd-shim-kata-v2 /usr/local/bin/ vagrant@node01:~$ ls -l /usr/local/bin/ total 4 lrwxrwxrwx 1 root root 61 Nov 15 05:35 containerd-shim-kata-v2 -> /snap/kata-containers/current/usr/bin/containerd-shim-kata-v2
snapでインストールしたKata ContainersにはQEMUが含まれているので、別途QEMUをインストールする必要はありません。
vagrant@node01:~$ ls -l /snap/kata-containers/current/usr/bin/ total 125763 -rwxr-xr-x 1 root root 40849034 Nov 5 15:17 containerd-shim-kata-v2 -rwxr-xr-x 1 root root 16703 Nov 5 15:17 kata-collect-data.sh -rwxr-xr-x 1 root root 28316201 Nov 5 15:17 kata-monitor -rwxr-xr-x 1 root root 41948536 Nov 5 15:17 kata-runtime -rwxr-xr-x 1 root root 17648952 Nov 5 15:00 qemu-system-x86_64
先ほどの参加トークンで一旦通常通りのインストールを行います。
vagrant@node01:~$ sudo k0s install worker --token-file /etc/k0s/join-token --kubelet-extra-args '--node-ip=192.168.123.201' vagrant@node01:~$ sudo k0s start
このままではruncを使用する状態なので、一旦停止します。
vagrant@node01:~$ sudo k0s stop
もしこれで /var/lib/k0s/bin/containerd-shim-runc-v2 やコンテナが残ってしまっていたら、適宜 kill してください。
では、containerd が runc ではなく Kata Containers を使うように設定を変更しましょう。まずは containerd のデフォルト設定ファイルを書き出します。
vagrant@node01:~$ sudo /var/lib/k0s/bin/containerd config default > containerd.toml.orig vagrant@node01:~$ cp containerd.toml.orig containerd.toml
Configure containerd to use Kata Containersを参照して設定ファイルを変更します。書いてある内容と実際の containerd.toml の内容が違うのでちょっと困ってしまいますが、前後の内容から推測して記載します。 plugins.cri は plugins."io.containerd.grpc.v1.cri" と読み替えればよいようです。
vagrant@node01:~$ vi containerd.toml vagrant@node01:~$ diff -u containerd.toml.orig containerd.toml --- containerd.toml.orig 2021-11-15 06:18:02.576921052 +0000 +++ containerd.toml 2021-11-15 06:21:10.388921052 +0000 @@ -111,6 +111,11 @@ ShimCgroup = "" SystemdCgroup = false + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata] + runtime_type = "io.containerd.kata.v2" + + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata.options] + [plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime] base_runtime_spec = "" container_annotations = []
作成した containerd.toml ファイルは /etc/k0s/ ディレクトリに配置します。元々ある containerd.toml ファイルはバージョン番号しか書かれていない最低限のものなので、上書きしてしまって構いません。
vagrant@node01:~$ sudo cp containerd.toml /etc/k0s/
では、再度起動しましょう。
vagrant@node01:~$ sudo k0s start vagrant@node01:~$ sudo k0s status Version: v1.22.3+k0s.0 Process ID: 6393 Role: worker Workloads: true
ここで一旦 master ゲストに戻ります。
Create runtime class for Kata Containersに従い、RuntimeClassを作成します。
vagrant@master:~$ vi kata.yaml apiVersion: node.k8s.io/v1beta1 kind: RuntimeClass metadata: name: kata handler: kata vagrant@master:~$ sudo k0s kubectl apply -f kata.yaml
Run pod in Kata Containersに従い、テストPodを起動します。spec.runtimeClassNameにkataを指定するところがポイントです。
vagrant@master:~$ vi nginx-kata.yaml apiVersion: v1 kind: Pod metadata: name: nginx-kata spec: runtimeClassName: kata containers: - name: nginx image: nginx vagrant@master:~$ sudo k0s kubectl apply -f nginx-kata.yaml pod/nginx-kata created vagrant@master:~$ sudo k0s kubectl get pods NAME READY STATUS RESTARTS AGE nginx-kata 1/1 Running 0 46s
Podが起動しました。
しかし、本当にKata Containersで動いているのでしょうか? node01で確かめてみましょう。まず ctr コマンドでランタイムを確認します。
vagrant@node01:~$ sudo k0s ctr c ls runtime.name==io.containerd.kata.v2 CONTAINER IMAGE RUNTIME 3076231e5f07beafca818140065b5689f0679cd97a8b5b5410e1cd795aa949b8 docker.io/library/nginx:latest io.containerd.kata.v2 4de8dc6d430b8a718a41072694f6208c50b9b615cb1f68add902a39a0debfa30 k8s.gcr.io/pause:3.5 io.containerd.kata.v2
ランタイムが io.containerd.runc.v2 ではなく io.containerd.kata.v2 と、Kata Containers になっていることがわかります。さらに実際のプロセスも見てみましょう。
vagrant@node01:~$ ps axf | grep '[ k]ata' 7810 ? Sl 0:00 /snap/kata-containers/1285/usr/bin/containerd-shim-kata-v2 -namespace k8s.io -address /run/k0s/containerd.sock -publish-binary /var/lib/k0s/bin/containerd -id 4de8dc6d430b8a718a41072694f6208c50b9b615cb1f68add902a39a0debfa30 7839 ? Sl 0:00 _ /snap/kata-containers/current/usr/libexec/kata-qemu/virtiofsd --syslog -o cache=auto -o no_posix_lock -o source=/run/kata-containers/shared/sandboxes/4de8dc6d430b8a718a41072694f6208c50b9b615cb1f68add902a39a0debfa30/shared --fd=3 -f --thread-pool-size=1 7856 ? Sl 0:00 _ /snap/kata-containers/current/usr/libexec/kata-qemu/virtiofsd --syslog -o cache=auto -o no_posix_lock -o source=/run/kata-containers/shared/sandboxes/4de8dc6d430b8a718a41072694f6208c50b9b615cb1f68add902a39a0debfa30/shared --fd=3 -f --thread-pool-size=1 7845 ? Sl 0:21 /snap/kata-containers/1285/usr/bin/qemu-system-x86_64 -name sandbox-4de8dc6d430b8a718a41072694f6208c50b9b615cb1f68add902a39a0debfa30 -uuid 35efd9af-118a-4c1d-b778-37be16d46dd7 -machine q35,accel=kvm,kernel_irqchip=on,nvdimm=on -cpu host,pmu=off -qmp unix:/run/vc/vm/4de8dc6d430b8a718a41072694f6208c50b9b615cb1f68add902a39a0debfa30/qmp.sock,server=on,wait=off -m 2048M,slots=10,maxmem=4968M -device pci-bridge,bus=pcie.0,id=pci-bridge-0,chassis_nr=1,shpc=on,addr=2 -device virtio-serial-pci,disable-modern=true,id=serial0 -device virtconsole,chardev=charconsole0,id=console0 -chardev socket,id=charconsole0,path=/run/vc/vm/4de8dc6d430b8a718a41072694f6208c50b9b615cb1f68add902a39a0debfa30/console.sock,server=on,wait=off -device nvdimm,id=nv0,memdev=mem0,unarmed=on -object memory-backend-file,id=mem0,mem-path=/snap/kata-containers/1285/usr/share/kata-containers/kata-containers.img,size=134217728,readonly=on -device virtio-scsi-pci,id=scsi0,disable-modern=true -object rng-random,id=rng0,filename=/dev/urandom -device virtio-rng-pci,rng=rng0 -device vhost-vsock-pci,disable-modern=true,vhostfd=3,id=vsock-1436109088,guest-cid=1436109088 -chardev socket,id=char-12bcd3ca2a7c53e5,path=/run/vc/vm/4de8dc6d430b8a718a41072694f6208c50b9b615cb1f68add902a39a0debfa30/vhost-fs.sock -device vhost-user-fs-pci,chardev=char-12bcd3ca2a7c53e5,tag=kataShared -netdev tap,id=network-0,vhost=on,vhostfds=4,fds=5 -device driver=virtio-net-pci,netdev=network-0,mac=ae:d0:83:25:a5:cb,disable-modern=true,mq=on,vectors=4 -rtc base=utc,driftfix=slew,clock=host -global kvm-pit.lost_tick_policy=discard -vga none -no-user-config -nodefaults -nographic --no-reboot -daemonize -object memory-backend-file,id=dimm1,size=2048M,mem-path=/dev/shm,share=on -numa node,memdev=dimm1 -kernel /snap/kata-containers/1285/usr/share/kata-containers/vmlinux-5.10.25.container -append tsc=reliable no_timer_check rcupdate.rcu_expedited=1 i8042.direct=1 i8042.dumbkbd=1 i8042.nopnp=1 i8042.noaux=1 noreplace-smp reboot=k console=hvc0 console=hvc1 cryptomgr.notests net.ifnames=0 pci=lastbus=0 root=/dev/pmem0p1 rootflags=dax,data=ordered,errors=remount-ro ro rootfstype=ext4 quiet systemd.show_status=false panic=1 nr_cpus=2 systemd.unit=kata-containers.target systemd.mask=systemd-networkd.service systemd.mask=systemd-networkd.socket scsi_mod.scan=none -pidfile /run/vc/vm/4de8dc6d430b8a718a41072694f6208c50b9b615cb1f68add902a39a0debfa30/pid -smp 1,cores=1,threads=1,sockets=2,maxcpus=2
このように、Kata Containers経由のQEMUでコンテナが動作していることがわかりました。
masterでテスト用のnginxを削除します。
vagrant@master:~$ sudo k0s kubectl delete -f nginx-kata.yaml
k0sで2つ目のワーカーノードを追加
node02 にて、node01 と同様の手順でKata Containersを利用するように設定したワーカーノードとして追加作業を行います。k0s install worker の --kubelet-extra-args で指定する --node-ip= のIPアドレス以外はすべて同じです。
2つ目のワーカーノードが追加できたら、デモアプリケーションとしてお馴染みの Sock Shop をデプロイします。ただしそのままではKata Containersを利用する設定になっていないので、少し手を加えましょう。ここでは *-db と front-end Pod を Kata Containers で、他の Pod を runc で動かすようにしてみます。
vagrant@master:~$ wget https://raw.githubusercontent.com/microservices-demo/microservices-demo/master/deploy/kubernetes/complete-demo.yaml vagrant@master:~$ cp -a complete-demo.yaml complete-demo-kata.yaml vagrant@master:~$ vi complete-demo-kata.yaml vagrant@master:~$ diff -u complete-demo.yaml complete-demo-kata.yaml --- complete-demo.yaml 2021-11-15 06:38:14.080124004 +0000 +++ complete-demo-kata.yaml 2021-11-15 06:40:28.284124004 +0000 @@ -89,6 +89,7 @@ labels: name: carts-db spec: + runtimeClassName: kata containers: - name: carts-db image: mongo @@ -219,6 +220,7 @@ labels: name: catalogue-db spec: + runtimeClassName: kata containers: - name: catalogue-db image: weaveworksdemos/catalogue-db:0.3.0 @@ -263,6 +265,7 @@ labels: name: front-end spec: + runtimeClassName: kata containers: - name: front-end image: weaveworksdemos/front-end:0.3.12 @@ -403,6 +406,7 @@ labels: name: orders-db spec: + runtimeClassName: kata containers: - name: orders-db image: mongo @@ -652,6 +656,7 @@ annotations: prometheus.io.scrape: "false" spec: + runtimeClassName: kata containers: - name: session-db image: redis:alpine @@ -847,6 +852,7 @@ labels: name: user-db spec: + runtimeClassName: kata containers: - name: user-db image: weaveworksdemos/user-db:0.3.0
起動してみます。
vagrant@master:~$ sudo k0s kubectl apply -f complete-demo-kata.yaml (省略) vagrant@master:~$ sudo k0s kubectl -n sock-shop get pod NAME READY STATUS RESTARTS AGE carts-b4d4ffb5c-gkp2q 1/1 Running 0 9m13s carts-db-cd9d99f77-hq47g 1/1 Running 0 9m13s catalogue-759cc6b86-5j78q 1/1 Running 0 9m13s catalogue-db-57cdcc99d5-glpc5 1/1 Running 0 9m13s front-end-67c79dfdc5-dxclq 1/1 Running 0 9m13s orders-7664c64d75-kt7wf 1/1 Running 0 9m13s orders-db-6fdcd79fc7-6mwp7 1/1 Running 0 9m12s payment-7bcdbf45c9-4bgvm 1/1 Running 0 9m12s queue-master-5f6d6d4796-mbncr 1/1 Running 0 9m12s rabbitmq-5bcbb547d7-8mj2q 2/2 Running 0 9m12s session-db-5885d7f467-ms286 1/1 Running 0 9m12s shipping-7f7999ffb7-pwzx6 1/1 Running 0 9m12s user-68df64db9c-xtxlw 1/1 Running 0 9m11s user-db-954965f9f-d2z8r 1/1 Running 0 9m11s
node01 と node02 で、Kata Containersで起動しているか確認します。
vagrant@node01:~$ sudo k0s ctr c ls runtime.name==io.containerd.kata.v2 CONTAINER IMAGE RUNTIME a83bd4dfdbfc2cbf1453ad0ce747bbeabb1c23def590052689c9dc0fb07fc1a1 docker.io/weaveworksdemos/front-end:0.3.12 io.containerd.kata.v2 f3e81d3a46e5dbb1ef86923415cf539167ea8f9fb0284c1045bc9ba9b756ab32 k8s.gcr.io/pause:3.5 io.containerd.kata.v2
vagrant@node02:~$ sudo k0s ctr c ls runtime.name==io.containerd.kata.v2 CONTAINER IMAGE RUNTIME 146ab445a2391eb8780a0c78f3ec6c53ba0088e9d4ea0e714de186d206c1cde7 k8s.gcr.io/pause:3.5 io.containerd.kata.v2 38a46d993721ffde29b982b087bf5bdc1ccc24b6373909e5c336b25c6aaf5746 docker.io/library/mongo:latest io.containerd.kata.v2 48295ed87198e6c5aca57e3dc60b5494bd0bb4f168d7849fd304311723584eac docker.io/weaveworksdemos/catalogue-db:0.3.0 io.containerd.kata.v2 7d63ed1069afba8af5bb2a84f742374149fd31cc15d9165e25c195080501847e docker.io/library/redis:alpine io.containerd.kata.v2 7f06cb880c570ed42a66380a561333ba55d7b68eb1349c1ac89a802b13cb238c k8s.gcr.io/pause:3.5 io.containerd.kata.v2 a286cf614178ce16afcc638e17d1cace9a27b9466c8095f571bd32b6b8a504c1 docker.io/weaveworksdemos/user-db:0.3.0 io.containerd.kata.v2 b7a8e447fa4104cf801c6b7e9155ed5bcb247cc3a22574a757fcc28f598347e3 k8s.gcr.io/pause:3.5 io.containerd.kata.v2 bb439a89174d5ca8251b3584d4d5f50af1f542703c9acf9e55e9c751d86c5c1f k8s.gcr.io/pause:3.5 io.containerd.kata.v2 dc28a2b77e1d2f1314ca86e87e372cf39c3e4343134318e84556b48535b2fb64 docker.io/library/mongo:latest io.containerd.kata.v2 f2c38f2d9bd224f3fc93dbe021cebe475fb4051862202b6b9b8d76f693aaa232 k8s.gcr.io/pause:3.5 io.containerd.kata.v2
少々配置に偏りがありますが、無事にKata Containersで起動しています。これで http://192.168.123.201:30001 または http://192.168.123.202:30001 にアクセスすると、Sock ShopのUIが表示されます。
まとめ
本稿ではこのk0sを使ってKubernetesをインストールし、runcの代わりにKata ContainersとQEMUでコンテナを起動するという、いわゆるMicroVM構成を行ってみました。k0sでインストールできるKubernetesはピュアなKubernetesであるため、他の方法でインストールするKubernetesと同様の手順でKata Containersの組み込みを行うことができます。
このようにk0sを用いれば、高速・容易にMicroVMを利用したKubernetes環境の構築が可能なので、興味のある方は是非お試しください。