fbpx

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 ContainersQEMUを用いる、いわゆるMicroVM構成にしてみます。

なお、デフォルトのruncでインストールする例は以前のブログ「k0sでKubernetesをVirtualbox/Vagrantにインストールしてみよう」をご覧ください。

Kata Containersとは

通常のコンテナは、カーネルネームスペースやcgroupsなどの機能を用いてカーネルを共有しつつプロセス同士を隔離しています。この方法は軽量かつ高速ですが、カーネルを共有しているためセキュリティ的な不安が残ります。
Kata Containersはコンテナごとに専用の軽量な仮想マシンを起動し、カーネルを共有せずにプロセス同士を隔離することで、よりセキュアなコンテナ環境を実現するためのオープンソースのコンテナランタイムです。

利用できる仮想マシンは QEMUFirecracker などがあります。さらに、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環境の構築が可能なので、興味のある方は是非お試しください。

Author

Chef・Docker・Mirantis製品などの技術要素に加えて、会議の進め方・文章の書き方などの業務改善にも取り組んでいます。「Chef活用ガイド」共著のほか、Debian Official Developerもやっています。

Daisuke Higuchiの記事一覧

新規CTA