🫙

containerd + kata containers を試す

2024/09/18に公開

kata containers について

kata containers はコンテナ並に軽量かつ従来の hypervisor 型 VM と同等の機能をもつ仮想マシンを作成するための OSS です。現在は Openstack 等を管理する Open Infrastructure Foundation のプロジェクトの一つになっています。

https://katacontainers.io/learn/

kata containers ではコンテナ環境を作成する際に guest kernel を使った VM 環境を作成し、その中でコンテナに相当する環境を作成するようになっています。これによりホスト側の kernel を共有している従来のコンテナ環境よりもよりセキュアな環境を構築しつつ、コンテナ並みの軽量な動作を実現しています。


kata-container と従来のコンテナの比較。https://katacontainers.io/learn/ より引用

また、containerd との統合機能により kata containers を runc の代わりに low level runtime として使用することができます。今回はこの機能を試してみます。

導入

前提条件

kata containers を使用するためのハードウェア要件は Hardware requirements に確認方法が書かれていますが、具体的な条件は記載されてなさそうなので実際に確認用のコマンド kata-runtime check を実行して確認します。

kata-containers/kata-containers の Release からアーキテクチャに合った tar をダウンロードして展開すると、opt/kata ディレクトリに kata containers の動作に必要なファイル一式が含まれているため、これを /opt ディレクトリ以下にコピーします。
kata containers に関連するバイナリは opt/kata/bin/ にまとめられており、kata-runtime もこのディレクトリに含まれているためこれを実行します。ハードウェア要件を満たしていて問題ない場合は System can currently create Kata Containers 等が表示されます。

$ /opt/kata/bin/kata-runtime check
WARN[0000] failed to resolve SNP certificates path: /opt/snp/cert_chain.cert
No newer release available
System is capable of running Kata Containers
$ sudo /opt/kata/bin/kata-runtime check
WARN[0000] failed to resolve SNP certificates path: /opt/snp/cert_chain.cert
WARN[0000] Not running network checks as super user      arch=amd64 name=kata-runtime pid=36327 source=runtime
System is capable of running Kata Containers
System can currently create Kata Containers

一方で要件を満たさない場合は以下のように error が表示されます(以下は AWS EC2 インスタンス上で実行した例)。

$ sudo /opt/kata/bin/kata-runtime check
WARN[0000] failed to resolve SNP certificates path: /opt/snp/cert_chain.cert
WARN[0000] Not running network checks as super user      arch=amd64 name=kata-runtime pid=1471 source=runtime
ERRO[0000] CPU property not found                        arch=amd64 description="Virtualization support" name=vmx pid=1471 source=runtime type=flag
WARN[0000] modprobe insert module failed                 arch=amd64 error="exit status 1" module=kvm_intel name=kata-runtime output="modprobe: ERROR: could not insert 'kvm_intel': Operation not supported\n" pid=1471 source=runtime
ERRO[0000] kernel property kvm_intel not found           arch=amd64 description="Intel KVM" name=kvm_intel pid=1471 source=runtime type=module
ERRO[0000] ERROR: System is not capable of running Kata Containers  arch=amd64 name=kata-runtime pid=1471 source=runtime

上記の結果を見ると以下の条件を満たしていれば良さそうです。

  • CPU が 仮想化をサポートしている
  • kvm 関連のカーネルモジュールがロードされている。lsmod で確認可能
$ lsmod | grep kvm
kvm_amd               155648  400
ccp                   110592  1 kvm_amd
kvm                  1036288  1 kvm_amd

AWS などのクラウドの仮想マシンを使用する場合、KVM がサポートされたタイプ (ベアメタルインスタンスなど) を使用する必要があります。

インストール

今回は openstack 上に構築した以下の VM を使って検証します。

  • OS: ubuntu 22.04 LTS
  • メモリ: 16 GB
  • vCPU: 4

kata containers のインストール方法は Install Guide に記載の通りいくつかあります。

  • パッケージマネージャーでインストール
  • インストールスクリプトを実行してインストール
  • 手動でインストール

手動でインストールする場合は kata-containers/kata-containers の Release から kata-container の tar をダウンロードしてして配置すれば ok です。既に前提条件のところで実行済みなのでスキップして ok

kata 関連のバイナリは /opt/kata/bin に配置されるので、よく使う実行ファイルは /usr/local/bin などパスが通っている場所にシンボリックリンクを作成しておくと楽です。

一例
sudo ln -s /opt/kata/bin/kata-runtime /usr/local/bin
sudo ln -s /opt/kata/bin/containerd-shim-kata-v2  /usr/local/bin

使い道

kata containers の使用用途や tips などは How to Guide にたくさん記載されています。
ここではこの中からいくつか試してみます。

containerd と組み合わせて使う

How to use Kata Containers and Containerd の内容

kata containers は low level runtime の機能があるため、containerd + runc の組み合わせにおいて runc の代わりに kata containers を使ってコンテナを動作させることができます。containerd と組み合わせて使用する場合、containerd の動作を確認するため以下も必要になります。こちらのインストール手順は割愛。

  • containerd
  • runc
  • cni

kata containers を使うには containerd の構成ファイル config.toml において以下のように設定を追加、編集します。ドキュメントではフィールド名が plugins.cri となっていますが、手元の環境では plugins."io.containerd.grpc.v1.cri" とする必要がありました。(このあたりは containerd のインストール方法や設定ファイルの作成方法によるかも)
設定によっては plugins."io.containerd.grpc.v1.cri".containerd などに他のプロパティが設定済みの場合もありますが、その際は削除する必要はなくそのままで ok。

/etc/containerd/config.toml
    [plugins."io.containerd.grpc.v1.cri".containerd]
      no_pivot = false
    [plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
      [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
         privileged_without_host_devices = false
         runtime_type = "io.containerd.runc.v2"
        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
            BinaryName = ""
            CriuImagePath = ""
            CriuPath = ""
            CriuWorkPath = ""
            IoGid = 0
      [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata]
         runtime_type = "io.containerd.kata.v2"
         privileged_without_host_devices = true
         pod_annotations = ["io.katacontainers.*"]
         container_annotations = ["io.katacontainers.*"]
         [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata.options]
            ConfigPath = "/opt/kata/share/defaults/kata-containers/configuration.toml"

containerd を再起動して変更を適用。

sudo systemctl restart containerd.service

これにより containerd で kata containers を使えるようになりました。ホスト側では runc と kata containers が両方ともインストールされており、コンテナ作成時に --runtime オプションを指定することで使用する runtime を切り替えられる状態になっています。
動作を確認するために、ドキュメントの通り crictl で pod とコンテナを作成してみます。

pod の定義

pod.yml
metadata:
  attempt: 1
  name: busybox-sandbox
  namespace: default
  uid: hdishd83djaidwnduwk28bcsb
log_directory: /tmp
linux:
  namespaces:
    options: {}

container の定義

container.yml
etadata:
  name: busybox
image:
  image: docker.io/busybox:latest
command:
- top
log_path: busybox.0.log

まずは kata containers を使用せず、通常の runc を使って pod を作成。

sudo crictl runp pod.yml

作成した pod を確認。

$ sudo crictl pods
WARN[0000] runtime connect using default endpoints: [unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As the default settings are now deprecated, you should set the endpoint instead.
POD ID              CREATED             STATE               NAME                NAMESPACE           ATTEMPT             RUNTIME
78aeda417c97e       2 minutes ago       Ready               busybox-sandbox     default             1                   kata

pod に container を追加。

sudo crictl create 78aeda417c97e container.yml pod.yml

container を開始。

sudo crictl start 630998a5900f7

container の確認。

$ sudo crictl ps -a -pod 78aeda417c97e
WARN[0000] runtime connect using default endpoints: [unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As the default settings are now deprecated, you should set the endpoint instead.
WARN[0000] image connect using default endpoints: [unix:///run/containerd/containerd.sock unix:///run/crio/crio.sock unix:///var/run/cri-dockerd.sock]. As the default settings are now deprecated, you should set the endpoint instead.
CONTAINER           IMAGE                      CREATED             STATE               NAME                ATTEMPT             POD ID              POD
630998a5900f7       docker.io/busybox:latest   2 minutes ago       Created             busybox             0                   78aeda417c97e       unknown

container 内でシェルを起動して接続し、カーネルのバージョンを確認。

$ sudo crictl exec  -it 630998a5900f7 sh

/ # uname -ar
Linux kata 5.15.0-50-generic #56-Ubuntu SMP Tue Sep 20 13:23:26 UTC 2022 x86_64 GNU/Linux

containerd + runc の構成では docker 等と同様にホスト側のカーネルを共有しているため、コンテナ内でカーネルバージョンを確認するとホスト側のカーネルが表示されます。
ホスト側でも同じコマンドを実行するとバージョンが一致していることが確認できます (kata は hostname に対応)。

ホスト側
$ uname -ar
Linux kata 5.15.0-50-generic #56-Ubuntu SMP Tue Sep 20 13:23:26 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

一方で kata containers ではホスト側のカーネルとは別の guest kernel 内でコンテナ環境を実行するため、ホスト側とは別のカーネルバージョンが表示されます。
crictl で kata containers を使うには -r, --runtime オプションで kata を指定します。

$ sudo crictl runp -r kata pod.yaml
$ sudo crictl create e9cdd306453ac container.yml pod.yml
$ sudo crictl start 2c6a61e43d886
$ sudo crictl exec -it 2c6a61e43d886 sh

/ # uname -ar
Linux 6.1.62 #1 SMP Fri Aug  9 10:43:25 UTC 2024 x86_64 GNU/Linux

カーネルバージョンが 6.1.62 になっているのでホスト側のカーネルとは別のものが使用されていることがわかります。

ちなみに /opt/kata/share/kata-containers に MBR boot イメージや initd、カーネルイメージらしきファイルが配置されているので、仮想環境作成時の guest kernel にはここにあるものが使われてそうです(詳しくは調べてない)。

$ ls -l /opt/kata/share/kata-containers
total 905916
-rw-r--r-- 1 ubuntu ubuntu     76921 Aug  9 10:43 config-6.1.62-135
-rw-r--r-- 1 ubuntu ubuntu     77162 Aug  9 10:43 config-6.1.62-135-dragonball-experimental
-rw-r--r-- 1 ubuntu ubuntu     79303 Aug  9 10:43 config-6.1.62-135-nvidia-gpu
-rw-r--r-- 1 ubuntu ubuntu     86857 Aug  9 10:47 config-6.7-135-confidential
-rw-r--r-- 1 ubuntu ubuntu     85862 Aug  9 10:44 config-6.7-135-nvidia-gpu-confidential
-rw-r--r-- 1 ubuntu ubuntu  14819498 Aug 16 20:55 kata-alpine-3.18.initrd
-rw-r--r-- 1 ubuntu ubuntu  68381502 Aug 16 20:57 kata-cbl-mariner-2.0-mariner.initrd
lrwxrwxrwx 1 ubuntu ubuntu        37 Aug 21 16:07 kata-containers-confidential.img -> kata-ubuntu-latest-confidential.image
lrwxrwxrwx 1 ubuntu ubuntu        37 Aug 16 20:56 kata-containers-initrd-confidential.img -> kata-ubuntu-20.04-confidential.initrd
lrwxrwxrwx 1 ubuntu ubuntu        35 Aug 16 20:57 kata-containers-initrd-mariner.img -> kata-cbl-mariner-2.0-mariner.initrd
lrwxrwxrwx 1 ubuntu ubuntu        23 Aug 16 20:55 kata-containers-initrd.img -> kata-alpine-3.18.initrd
lrwxrwxrwx 1 ubuntu ubuntu        24 Aug 21 16:04 kata-containers.img -> kata-ubuntu-latest.image
-rw-r--r-- 1 ubuntu ubuntu  47354120 Aug 16 20:56 kata-ubuntu-20.04-confidential.initrd
-rw-r--r-- 1 ubuntu ubuntu 268435456 Aug 21 16:07 kata-ubuntu-latest-confidential.image
-rw-r--r-- 1 ubuntu ubuntu 268435456 Aug 21 16:04 kata-ubuntu-latest.image
-rw-r--r-- 1 ubuntu ubuntu       435 Aug 21 16:07 root_hash.txt
-rw-r--r-- 1 ubuntu ubuntu  44796144 Aug  9 10:43 vmlinux-6.1.62-135
-rw-r--r-- 1 ubuntu ubuntu  44809200 Aug  9 10:43 vmlinux-6.1.62-135-dragonball-experimental
-rw-r--r-- 1 ubuntu ubuntu  47046728 Aug  9 10:43 vmlinux-6.1.62-135-nvidia-gpu
-rw-r--r-- 1 ubuntu ubuntu  43444024 Aug  9 10:47 vmlinux-6.7-135-confidential
-rw-r--r-- 1 ubuntu ubuntu  41301608 Aug  9 10:44 vmlinux-6.7-135-nvidia-gpu-confidential
lrwxrwxrwx 1 ubuntu ubuntu        28 Aug  9 10:47 vmlinux-confidential.container -> vmlinux-6.7-135-confidential
lrwxrwxrwx 1 ubuntu ubuntu        42 Aug  9 10:43 vmlinux-dragonball-experimental.container -> vmlinux-6.1.62-135-dragonball-experimental
lrwxrwxrwx 1 ubuntu ubuntu        39 Aug  9 10:44 vmlinux-nvidia-gpu-confidential.container -> vmlinux-6.7-135-nvidia-gpu-confidential
lrwxrwxrwx 1 ubuntu ubuntu        29 Aug  9 10:43 vmlinux-nvidia-gpu.container -> vmlinux-6.1.62-135-nvidia-gpu
lrwxrwxrwx 1 ubuntu ubuntu        18 Aug  9 10:43 vmlinux.container -> vmlinux-6.1.62-135
-rw-r--r-- 1 ubuntu ubuntu   6702032 Aug  9 10:43 vmlinuz-6.1.62-135
-rw-r--r-- 1 ubuntu ubuntu   6706928 Aug  9 10:43 vmlinuz-6.1.62-135-dragonball-experimental
-rw-r--r-- 1 ubuntu ubuntu   6982208 Aug  9 10:43 vmlinuz-6.1.62-135-nvidia-gpu
-rw-r--r-- 1 ubuntu ubuntu  10534912 Aug  9 10:47 vmlinuz-6.7-135-confidential
-rw-r--r-- 1 ubuntu ubuntu   7426048 Aug  9 10:44 vmlinuz-6.7-135-nvidia-gpu-confidential
lrwxrwxrwx 1 ubuntu ubuntu        28 Aug  9 10:47 vmlinuz-confidential.container -> vmlinuz-6.7-135-confidential
lrwxrwxrwx 1 ubuntu ubuntu        42 Aug  9 10:43 vmlinuz-dragonball-experimental.container -> vmlinuz-6.1.62-135-dragonball-experimental
lrwxrwxrwx 1 ubuntu ubuntu        39 Aug  9 10:44 vmlinuz-nvidia-gpu-confidential.container -> vmlinuz-6.7-135-nvidia-gpu-confidential
lrwxrwxrwx 1 ubuntu ubuntu        29 Aug  9 10:43 vmlinuz-nvidia-gpu.container -> vmlinuz-6.1.62-135-nvidia-gpu
lrwxrwxrwx 1 ubuntu ubuntu        18 Aug  9 10:43 vmlinuz.container -> vmlinuz-6.1.62-135

ホスト側の qemu のプロセスで -kernel /opt/kata/share/kata-containers/vmlinux-6.1.62-135 が指定されているのでこれが使われてそう。

qemu プロセスの詳細

/opt/kata/bin/qemu-system-x86_64 -name sandbox-756f7c14c0f7915313fd028b4f044975eb1e0a61a71c3236506eaed8daf11766 -uuid 50a9c16a-133f-4355-92c5-e7b96b70a34e -machine q35,accel=kvm,nvdimm=on -cpu host,pmu=off -qmp unix:fd=3,server=on,wait=off -m 2048M,slots=10,maxmem=17025M -device pci-bridge,bus=pcie.0,id=pci-bridge-0,chassis_nr=1,shpc=off,addr=2,io-reserve=4k,mem-reserve=1m,pref64-reserve=1m -device virtio-serial-pci,disable-modern=true,id=serial0 -device virtconsole,chardev=charconsole0,id=console0 -chardev socket,id=charconsole0,path=/run/vc/vm/756f7c14c0f7915313fd028b4f044975eb1e0a61a71c3236506eaed8daf11766/console.sock,server=on,wait=off -device nvdimm,id=nv0,memdev=mem0,unarmed=on -object memory-backend-file,id=mem0,mem-path=/opt/kata/share/kata-containers/kata-ubuntu-latest.image,size=268435456,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=4,id=vsock-3140714432,guest-cid=3140714432 -chardev socket,id=char-26f658329c5bb05b,path=/run/vc/vm/756f7c14c0f7915313fd028b4f044975eb1e0a61a71c3236506eaed8daf11766/vhost-fs.sock -device vhost-user-fs-pci,chardev=char-26f658329c5bb05b,tag=kataShared,queue-size=1024 -rtc base=utc,driftfix=slew,clock=host -global kvm-pit.lost_tick_policy=discard -vga none -no-user-config -nodefaults -nographic --no-reboot -object memory-backend-file,id=dimm1,size=2048M,mem-path=/dev/shm,share=on -numa node,memdev=dimm1 -kernel /opt/kata/share/kata-containers/vmlinux-6.1.62-135 -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 cryptomgr.notests net.ifnames=0 pci=lastbus=0 root=/dev/pmem0p1 rootflags=dax,data=ordered,errors=remount-ro ro rootfstype=ext4 console=hvc0 console=hvc1 quiet systemd.show_status=false panic=1 nr_cpus=4 selinux=0 systemd.unit=kata-containers.target systemd.mask=systemd-networkd.service systemd.mask=systemd-networkd.socket scsi_mod.scan=none -pidfile /run/vc/vm/756f7c14c0f7915313fd028b4f044975eb1e0a61a71c3236506eaed8daf11766/pid -smp 1,cores=1,threads=1,sockets=4,maxcpus=4

nerdctl を使う場合

nerdctl で kata containers を使う場合 --runtime io.containerd.kata.v2 のように使用する runtime を指定します。
こちらの方が docker CLI を同じように使えるので楽です。

$ sudo nerdctl run --runtime io.containerd.kata.v2 --name kata  -d busybox sleep 1000000
$ sudo nerdctl exec -it kata sh

/ # uname -ar
Linux ce0888fa77a7 6.1.62 #1 SMP Fri Aug  9 10:43:25 UTC 2024 x86_64 GNU/Linux

runc を指定する場合は以下。

$ sudo nerdctl run  --name no-kata  -d busybox sleep 1000000
44b515c5a82b74341a33987a0bee4854a3e7ab0977d6d865d93b95e87cbb2470

$ sudo nerdctl exec -it no-kata sh
/ # uname -ar
Linux 44b515c5a82b 5.15.0-50-generic #56-Ubuntu SMP Tue Sep 20 13:23:26 UTC 2022 x86_64 GNU/Linux

ホスト側のプロセス

nerdctl を使って runc でコンテナを作成した場合、ホスト側では以下のようなプロセスが実行されます。

/usr/bin/containerd-shim-runc-v2 -namespace default -id b467cb3270d0ad7e1a552a108ce955531c6c31ddabb318cf5f5a0d459a417b40 -address /run/containerd/containerd.sock
 \_ /usr/local/bin/nerdctl _NERDCTL_INTERNAL_LOGGING /var/lib/nerdctl/1935db59
 \_ sleep 1000000

kata containers を使った場合は以下のプロセスが実行されています。デフォルトでパイパーバイザーに qemu を使用するようになっているため、/opt/kata/bin/qemu-system-x86_64 で実際に qemu のプロセスが実行されていることが確認できます。

/opt/kata/bin/containerd-shim-kata-v2 -namespace default -address /run/containerd/containerd.sock -publish-binary  -id 756f7c14c0f7915313fd028b4f044975eb1e0a61a71c3236506eaed8daf11766
  \_ /opt/kata/libexec/virtiofsd --syslog --cache=auto --shared-dir=/run/kata-containers/shared/sandboxes/756f7c14c0f7915313fd028b4f044975eb1e0a61a71c3236506eaed8daf11766/shared --fd=3 --thread-pool-size=1 --announce-submounts
  |   \_ /opt/kata/libexec/virtiofsd --syslog --cache=auto --shared-dir=/run/kata-containers/shared/sandboxes/756f7c14c0f7915313fd028b4f044975eb1e0a61a71c3236506eaed8daf11766/shared --fd=3 --thread-pool-size=1 --announce-submounts
  \_ /opt/kata/bin/qemu-system-x86_64 -name sandbox-756f7c14c0f7915313fd028b4f044975eb1e0a61a71c3236506eaed8daf11766 -uuid 50a9c16a-133f-4355-92c5-e7b96b70a34e -machine q35,accel=kvm,nvdimm=on -cpu host,pmu=off -qmp unix:fd=3,server=on,wait=off -m 2048M,slots=10,maxmem=17025M -device pci-bridge,bus=pcie.0,id=pci-bridge-0,chassis_nr=1,shpc=off,addr=2,io-reserve=4k,mem-reserve=1m,pref64-reserve=1m -device virtio-serial-pci,disable-modern=true,id=serial0 -device virtconsole,chardev=charconsole0,id=console0 -chardev socket,id=charconsole0,path=/run/vc/vm/756f7c14c0f7915313fd028b4f044975eb1e0a61a71c3236506eaed8daf11766/console.sock,server=on,wait=off -device nvdimm,id=nv0,memdev=mem0,unarmed=on -object memory-backend-file,id=mem0,mem-path=/opt/kata/share/kata-containers/kata-ubuntu-latest.image,size=268435456,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=4,id=vsock-3140714432,guest-cid=3140714432 -chardev socket,id=char-26f658329c5bb05b,path=/run/vc/vm/756f7c14c0f7915313fd028b4f044975eb1e0a61a71c3236506eaed8daf11766/vhost-fs.sock -device vhost-user-fs-pci,chardev=char-26f658329c5bb05b,tag=kataShared,queue-size=1024 -rtc base=utc,driftfix=slew,clock=host -global kvm-pit.lost_tick_policy=discard -vga none -no-user-config -nodefaults -nographic --no-reboot -object memory-backend-file,id=dimm1,size=2048M,mem-path=/dev/shm,share=on -numa node,memdev=dimm1 -kernel /opt/kata/share/kata-containers/vmlinux-6.1.62-135 -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 cryptomgr.notests net.ifnames=0 pci=lastbus=0 root=/dev/pmem0p1 rootflags=dax,data=ordered,errors=remount-ro ro rootfstype=ext4 console=hvc0 console=hvc1 quiet systemd.show_status=false panic=1 nr_cpus=4 selinux=0 systemd.unit=kata-containers.target systemd.mask=systemd-networkd.service systemd.mask=systemd-networkd.socket scsi_mod.scan=none -pidfile /run/vc/vm/756f7c14c0f7915313fd028b4f044975eb1e0a61a71c3236506eaed8daf11766/pid -smp 1,cores=1,threads=1,sockets=4,maxcpus=4
  \_ /usr/local/bin/nerdctl _NERDCTL_INTERNAL_LOGGING /var/lib/nerdctl/1935db59

コンテナ作成速度の比較

kata containers はコンテナ並に軽量との記載はありますが、実際にコンテナ作成速度やコンテナのリソース(メモリ使用量など)にどれぐらいの差があるのかを確認するため、以下のような簡単なシェルスクリプトを使ってコンテナを 100 個直列で作成した際の動作を見てみます。

test.sh
#!/bin/bash
set -xue

for i in {000..099}
do
  sudo nerdctl run -d --name test-$i nginx:alpine
done

イメージは軽量な nginx:alpine にしておきます。

runc の場合

比較のため、まずは kata containers を使用せず runc でコンテナを起動した際の動作を確認してみます。
作成したコンテナに対して sudo nerdctl inspect [name]Created" によりコンテナの作成時刻が確認できるので、test-000 と test-099 の時刻の差分よりコンテナ作成にかかった時間を計算すると 32 秒となりました。1 コンテナあたりに変換すると 0.4 秒となります。
また、sudo nerdctl stats で各コンテナの統計情報が確認できます。これで見るとメモリ使用量は 1 コンテナあたり約 4.3 MB となっていました。特にアクセスは行っていないため CPU 使用率の有意な変化は見られませんでした。
その他、htop でホスト側のメモリ使用量を確認すると約 4.3 GB となっていました。

kata containers の場合

kata containers で実行するために --runtime io.containerd.kata.v2 を追加します。

test.sh
#!/bin/bash
set -xue

for i in {000..100}
do
  sudo nerdctl run -d --runtime io.containerd.kata.v2 --name test-$i nginx:alpine
done

bash test.sh で実行した際にコンテナの作成ログが標準出力に表示されますが、実際やってみると runc と比較して 1 コンテナの作成に時間がかかるのが目に見えてわかります(それでも通常使用ではそこまで気になるほどではないですが)。また、87 コンテナ起動したあたりで以下のエラーにより作成に失敗しました。

WARN[0000] cannot set cgroup manager to "systemd" for runtime "io.containerd.kata.v2"
FATA[0047] failed to create shim task: Failed to Check if grpc server is working: rpc error: code = DeadlineExceeded desc = timed out connecting to vsock 4203134821:1024: unknown

エラー内容はホスト側のリソース不足によるためホスト側のスペックを上げれば解決する可能性あり。
作成できたコンテナから上記と同様の計算を行うと以下のようになりました。

  • 1 コンテナの作成にかかる時間は 2.1 秒。
  • 1 コンテナあたりのメモリ使用量は 8.8 MB
  • htop で確認した際のホストのメモリ使用量は 13.3 GB

測定のまとめ

runc と kata containers でそれぞれ測定した際の結果を表にまとめると以下のようになります。

項目 runc kata containers
コンテナ作成にかかる時間 (秒/コンテナ) 0.4 2.1
コンテナあたりのメモリ使用量 (MB) 4.3 8.8
ホストのメモリ使用量 (GB) 4.3 13.3

そこまで厳密な検証ではないですが、runc と比較するとコンテナ作成時間や 1 コンテナのメモリ使用量に有意な差があり、通常のコンテナランタイムと完全に同等のパフォーマンスでコンテナを作成できるというわけではないようです。とはいえ kata containers は VM を作ってその中でコンテナを動かしているので、コンテナの約 2 倍のメモリや数秒程度でこれらの環境を作成・管理できているという点は注目するべきかもしれません。
上記は kata containers のパイパーバイザーに qemu を使用した際の測定であるため、他のハイパーバイザーに切り替えた際に結果が異なることもあります。その他ホスト側の CPU のスペック等色々依存する要因は考えられるため、上記の結果はあくまで一例として捉えるのが良さそうです。

仕組み

kata containers を runc の代わりとして使用した際のアーキテクチャは Containerd Runtime V2 API: Shim V2 API に簡単な図があります。runc を使う場合、containerd は containerd-shim v2 を通じてコンテナを管理する構成になっています(container のリポジトリの Runtime v2 にある図などを参考)。一方で kata containers では kata-agent と呼ばれるコンポーネントが guest kernel を使った VM の管理やその中でのコンテナの作成などを管理しています。そして containerd と kata-manager の仲介となるのが containerd-shim-kata-v2 となっています。


https://github.com/kata-containers/kata-containers/blob/main/docs/design/arch-images/shimv2.svg より引用

containerd-shim-kata-v2 を導入以前は kata-proxy や kata-runtime などいろいろな処理がありましたが、導入後は containerd-shim-kata-v2 がまとめて管理するようになっています。なので runc の部分がそのまま kata-agent による pod sandbox に置き換わったと考えれば良さそうです。

ハイパーバイザーを切り替える

kata containers で仮想環境を作成する際にデフォルトでは QEMU が使用されますが、他のパイパーバイザーを使用することもできます。サポートされているハイパーバイザーは Hypervisors に記載されています。ここでは VMM 用に設計された cloud-hypervisor に切り替えてコンテナを作ってみます。
cloud-hypervisor 用の実行ファイルや設定ファイルは kata containers インストール時の tar に含まれているため追加のインストール作業は不要です。ただ nerdctl から clh の runtime を指定するには、/etc/containerd/config.tomlruntimes.kata-clh の追加が必要です(他の方法でもできるかも)。

/etc/containerd/config.toml
        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata]
          runtime_type = "io.containerd.kata.v2"
          runtime_path = "/opt/kata/bin/containerd-shim-kata-v2"
          privileged_without_host_devices = true
          pod_annotations = ["io.katacontainers.*"]
          container_annotations = ["io.katacontainers.*"]
          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata.options]
            ConfigPath = "/opt/kata/share/defaults/kata-containers/configuration.toml"

+        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata-clh]
+          runtime_type = "io.containerd.kata-clh.v2"

/usr/local/bin/containerd-shim-kata-clh-v2 を作成。中身は clh 用の kata containers 設定ファイルを指定し、残りの引数を containerd-shim-kata-v2 に渡すだけの単純なシェルスクリプト。パスは環境に合わせて変更する。

/usr/local/bin/containerd-shim-kata-clh-v2
#!/bin/bash
KATA_CONF_FILE=/etc/kata-containers/configuration-clh.toml /usr/local/bin/containerd-shim-kata-v2 $@

sudo systemctl restart containerd で変更を適用すると、--runtime io.containerd.kata-clh.v2 でハイパーバイザーを clh に指定してコンテナを起動できるようになります。

$ sudo nerdctl run --runtime io.containerd.kata-clh.v2 --name kata-clh -d docker.io/library/nginx:latest
WARN[0000] cannot set cgroup manager to "systemd" for runtime "io.containerd.kata-clh.v2"
9bb18310cc891271bcad5fabbb6b684027788e0747671fcbc76f12bb6205c991

$ sudo nerdctl exec -it kata-clh bash
ERRO[0000] resize pty                                    error="\"no tty\": unavailable"
root@9bb18310cc89:/#

root@9bb18310cc89:/# uname -ar
Linux 9bb18310cc89 6.1.62 #1 SMP Fri Aug  9 10:43:25 UTC 2024 x86_64 GNU/Linux

ホスト側でプロセスを確認すると、qemu の代わりに /opt/kata/bin/cloud-hypervisor プロセスが起動していることが確認できます。

/opt/kata/bin/containerd-shim-kata-v2 -namespace default -address /run/containerd/containerd.sock -publish-binary  -id de47cc0b26d51b69b867db32bc9904ac17329740ceaeffe3319641fc2c37902d
 \_ /opt/kata/libexec/virtiofsd --syslog --cache=auto --shared-dir=/run/kata-containers/shared/sandboxes/de47cc0b26d51b69b867db32bc9904ac17329740ceaeffe3319641fc2c37902d/shared --fd=3 --thread-pool-size=1 --announce-submounts
 |   \_ /opt/kata/libexec/virtiofsd --syslog --cache=auto --shared-dir=/run/kata-containers/shared/sandboxes/de47cc0b26d51b69b867db32bc9904ac17329740ceaeffe3319641fc2c37902d/shared --fd=3 --thread-pool-size=1 --announce-submounts
 \_ /opt/kata/bin/cloud-hypervisor --api-socket /run/vc/vm/de47cc0b26d51b69b867db32bc9904ac17329740ceaeffe3319641fc2c37902d/clh-api.sock
 \_ /usr/local/bin/nerdctl _NERDCTL_INTERNAL_LOGGING /var/lib/nerdctl/1935db59

せっかくなので先程と同様にコンテナ 100 個を作成した際のパフォーマンスもみておきます。
同じ条件でコンテナを作成したところ、qemu では 87 個ほど作成した辺りでエラーとなりましたが。 clh の場合はエラーとならず 100 個まで作成できました。
1 コンテナの作成時間などを計算したところ以下のようになりました。

項目 qemu clh
コンテナ作成にかかる時間 (秒/コンテナ) 2.1 2.1
コンテナあたりのメモリ使用量 (MB) 8.8 8.8
ホストのメモリ使用量 (GB) 13.3 13.1

Hypervisors の比較表では Container Creation speed, Memory density が qemu では good であるのに対し clh ではいずれも excellent となっていますが、今回の検証では qemu と clh でほぼ差異がない結果となりました。コンテナ数やコンテナイメージを変えたりするなど条件を変えた場合に clh の方がパフォーマンスが良い結果になることが期待されます。
ハイパーバイザーに関しては Virtualization in Kata Containers も少し記載があります。

kubernetes で使う

kubernetes で containerd + kata containers を使う際の手順は How to use Kata Containers and containerd with Kubernetes を参照。上記の手順では containerd のインストールや CNI の設定なども書かれていますが、既に k8s で containerd を使う準備ができている場合、必要な手順は kata containers 用の Runtime Class を設定すれば使えるようになります。

runtime.yml
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: kata
handler: kata

作成した runtimeClass を使用するには、pod のマニフェストに runetimeClassName を指定します。deployment の場合は pod template の方に定義する必要がある点に注意。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 10
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      runtimeClassName: kata
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - containerPort: 80

検証のため、containerd で runc のみをインストールしたノード kata-w1 と、runc + kata containers を使えるようにしたノード kata を用意します。上記の runtimeClass の定義の場合、ノードが kata containers 等の runtime が設定済みで実行できるかどうかを判別しません。そのため、kata container が使用できないノード kata-w1 にも pod がスケジュールされます。

NAME                              READY   STATUS              RESTARTS   AGE   IP            NODE      NOMINATED NODE   READINESS GATES
nginx-deployment-d49c76cf-fhgsj   0/1     ContainerCreating   0          90s   <none>        kata-w1   <none>           <none>
nginx-deployment-d49c76cf-fnnq6   0/1     ContainerCreating   0          90s   <none>        kata-w1   <none>           <none>
nginx-deployment-d49c76cf-gkh9w   1/1     Running             0          90s   10.244.2.9    kata      <none>           <none>
nginx-deployment-d49c76cf-j77v9   0/1     ContainerCreating   0          90s   <none>        kata-w1   <none>           <none>
nginx-deployment-d49c76cf-jfps7   1/1     Running             0          90s   10.244.2.7    kata      <none>           <none>
nginx-deployment-d49c76cf-kjd7g   0/1     ContainerCreating   0          90s   <none>        kata-w1   <none>           <none>
nginx-deployment-d49c76cf-ln72w   1/1     Running             0          90s   10.244.2.8    kata      <none>           <none>
nginx-deployment-d49c76cf-nn4gm   1/1     Running             0          90s   10.244.2.10   kata      <none>           <none>
nginx-deployment-d49c76cf-tkqsd   1/1     Running             0          90s   10.244.2.11   kata      <none>           <none>
nginx-deployment-d49c76cf-vsj9r   0/1     ContainerCreating   0          90s   <none>        kata-w1   <none>           <none>

kubectl describe [pod_name] で見ると runtime が見つからない等のエラーが発生していることがわかります。

Events:
  Type     Reason                  Age                  From               Message
  ----     ------                  ----                 ----               -------
  Normal   Scheduled               3m41s                default-scheduler  Successfully assigned default/nginx-deployment-d49c76cf-fhgsj to kata-w1
  Warning  FailedCreatePodSandBox  5s (x17 over 3m40s)  kubelet            Failed to create pod sandbox: rpc error: code = Unknown desc = failed to get sandbox runtime: no runtime for "kata" is configured

runtime が対応しているノードにだけ pod を配置するには runtimeclass に scheduling を設定する必要があります。ドキュメントにはマニフェストの記載例がありませんが、RedHat の 6.2. スケジューリングメカニズムをカプセル化するための RuntimeClass オブジェクトの作成 にあるのでこれを参考にします。
現時点では nodeSelector, tolerations がサポートされていて、基本的に node に設定された label に基づいて pod 配置可能なノードを制御する形式になっています。一例として、kata container が使えるノードには runtime-kata=true を設定します。

kubectl label nodes kata runtime-runc=true
kubectl label nodes kata runtime-kata=true
kubectl label nodes kata-w1 runtime-runc=true

runtimeClass では nodeSelector で runtime-kata: "true" を指定。

runtime.yml
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: kata
handler: kata
scheduling:
  nodeSelector:
    runtime-kata: "true"

これで再度 replica 数 10 の deployment を作成すると、すべての pod が runtime-kata: "true" のラベルを持つ kata ノードに配置されるようになります。

NAME                              READY   STATUS    RESTARTS   AGE   IP            NODE   NOMINATED NODE   READINESS GATES
nginx-deployment-d49c76cf-9mftt   1/1     Running   0          34s   10.244.2.15   kata   <none>           <none>
nginx-deployment-d49c76cf-dn4w6   1/1     Running   0          34s   10.244.2.17   kata   <none>           <none>
nginx-deployment-d49c76cf-gb76f   1/1     Running   0          34s   10.244.2.20   kata   <none>           <none>
nginx-deployment-d49c76cf-hvrj8   1/1     Running   0          34s   10.244.2.21   kata   <none>           <none>
nginx-deployment-d49c76cf-kvq44   1/1     Running   0          34s   10.244.2.13   kata   <none>           <none>
nginx-deployment-d49c76cf-n8vck   1/1     Running   0          34s   10.244.2.18   kata   <none>           <none>
nginx-deployment-d49c76cf-t7zr4   1/1     Running   0          34s   10.244.2.14   kata   <none>           <none>
nginx-deployment-d49c76cf-vjm62   1/1     Running   0          34s   10.244.2.16   kata   <none>           <none>
nginx-deployment-d49c76cf-xkjq9   1/1     Running   0          34s   10.244.2.12   kata   <none>           <none>
nginx-deployment-d49c76cf-xm5s2   1/1     Running   0          34s   10.244.2.19   kata   <none>           <none>

クラスタ内で kata containers をサポートしている/していないノードが混在している場合は上記のような方法でサポートしているノードのみで pod を起動させることができます。

ちなみに pod のプロパティとして Runtime Class Name: kata が指定されていることは確認できますが、実際にコンテナがどのような runtime で起動されるかはノード側の CRI 側に依存しているため、pod が kata containers を使って起動されたかどうかを示す固有の情報はありません。

$ k describe pod nginx-deployment-d49c76cf-9mftt
Name:                nginx-deployment-d49c76cf-9mftt
Namespace:           default
...
Runtime Class Name:  kata
...
Containers:
  nginx:
    Container ID:   containerd://00dc76dad52234b6bda10daaef70e870908ae76cd3895d1acab87a99ac3f89ca

前に見たように pod が起動しているノード上では kata 関係のプロセスが動作しているのでそちらで判別できます。

annotation で hypervisor の設定を制御する

kata containers ではコンテナやポッドに annotation をつけることで様々な動作を設定できます。サポートされている annotation は Kata Configuration Annotations に一覧があります。例えば VM (コンテナ) に割り当てるメモリを変更するには io.katacontainers.config.hypervisor.default_memory という annotation に割り当てるメモリ量を MiB 単位で指定するというような具合になります。

nerdctl ではメジャーバージョン 2 より annotation のオプションに対応しているのでこちらで動作を確認してみます。
まず普通に kata containers でコンテナを起動して、コンテナ内から CPU、メモリを確認すると CPU が1、メモリは 2 GB 割り当てられていることが確認できます。

$ sudo nerdctl run --runtime io.containerd.kata.v2 --name kata -it  docker.io/library/ubuntu
WARN[0000] cannot set cgroup manager to "systemd" for runtime "io.containerd.kata.v2"
root@7c454968eae8:/#

root@7c454968eae8:/# free -h
               total        used        free      shared  buff/cache   available
Mem:           1.9Gi        47Mi       1.9Gi        28Ki       8.5Mi       1.9Gi
Swap:             0B          0B          0B

root@7c454968eae8:/# cat /proc/cpuinfo
processor       : 0
vendor_id       : AuthenticAMD
cpu family      : 23
model           : 24
model name      : AMD Ryzen 5 3550H with Radeon Vega Mobile Gfx
stepping        : 1
microcode       : 0x8108109
cpu MHz         : 2096.054
cache size      : 512 KB
physical id     : 0
siblings        : 1
core id         : 0
cpu cores       : 1
apicid          : 0
initial apicid  : 0
fpu             : yes
fpu_exception   : yes
cpuid level     : 13
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm rep_good nopl cpuid extd_apicid tsc_known_freq pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm cmp_legacy svm cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw perfctr_core ssbd ibpb vmmcall fsgsbase tsc_adjust bmi1 avx2 smep bmi2 rdseed adx smap clflushopt sha_ni xsaveopt xsavec xgetbv1 clzero xsaveerptr virt_ssbd arat npt nrip_save arch_capabilities
bugs            : sysret_ss_attrs null_seg spectre_v1 spectre_v2 spec_store_bypass retbleed smt_rsb srso div0
bogomips        : 4192.10
TLB size        : 1024 4K pages
clflush size    : 64
cache_alignment : 64
address sizes   : 48 bits physical, 48 bits virtual
power management:

kata containers から起動されるホスト側の qemu プロセスの引数で指定されている -m 2048M がメモリ、-smp 1 が cpu 1 に対応しています(たぶん)。

qemu プロセスの詳細

/opt/kata/bin/qemu-system-x86_64 -name sandbox-fc4b38ccfa3cdf758559413f993ff677c7e51737b59332ac81135406ae2dda32 -uuid 0f043adf-7e49-4a36-824b-4dde7ba028f5 -machine q35,accel=kvm,nvdimm=on -cpu host,pmu=off -qmp unix:fd=3,server=on,wait=off -m 2048M,slots=10,maxmem=17013M -device pci-bridge,bus=pcie.0,id=pci-bridge-0,chassis_nr=1,shpc=off,addr=2,io-reserve=4k,mem-reserve=1m,pref64-reserve=1m -device virtio-serial-pci,disable-modern=true,id=serial0 -device virtconsole,chardev=charconsole0,id=console0 -chardev socket,id=charconsole0,path=/run/vc/vm/fc4b38ccfa3cdf758559413f993ff677c7e51737b59332ac81135406ae2dda32/console.sock,server=on,wait=off -device nvdimm,id=nv0,memdev=mem0,unarmed=on -object memory-backend-file,id=mem0,mem-path=/opt/kata/share/kata-containers/kata-ubuntu-latest.image,size=268435456,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=4,id=vsock-3794461917,guest-cid=3794461917 -chardev socket,id=char-b8b8147ca7b201a2,path=/run/vc/vm/fc4b38ccfa3cdf758559413f993ff677c7e51737b59332ac81135406ae2dda32/vhost-fs.sock -device vhost-user-fs-pci,chardev=char-b8b8147ca7b201a2,tag=kataShared,queue-size=1024 -rtc base=utc,driftfix=slew,clock=host -global kvm-pit.lost_tick_policy=discard -vga none -no-user-config -nodefaults -nographic --no-reboot -object memory-backend-file,id=dimm1,size=2048M,mem-path=/dev/shm,share=on -numa node,memdev=dimm1 -kernel /opt/kata/share/kata-containers/vmlinux-6.1.62-135 -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 cryptomgr.notests net.ifnames=0 pci=lastbus=0 root=/dev/pmem0p1 rootflags=dax,data=ordered,errors=remount-ro ro rootfstype=ext4 console=hvc0 console=hvc1 quiet systemd.show_status=false panic=1 nr_cpus=4 selinux=0 systemd.unit=kata-containers.target systemd.mask=systemd-networkd.service systemd.mask=systemd-networkd.socket scsi_mod.scan=none -pidfile /run/vc/vm/fc4b38ccfa3cdf758559413f993ff677c7e51737b59332ac81135406ae2dda32/pid -smp 1,cores=1,threads=1,sockets=4,maxcpus=4

次に以下の annotation を設定して cpu を 2、メモリを 1 GB に変更してみます。

sudo nerdctl run --runtime io.containerd.kata.v2 \
      --name kata \
      --annotation io.katacontainers.config.hypervisor.default_max_vcpus=2 \
      --annotation io.katacontainers.config.hypervisor.default_vcpus=2.0 \
      --annotation io.katacontainers.config.hypervisor.default_memory=1024 \
      -it --rm docker.io/library/ubuntu

以下のエラーが発生

failed to create containerd task: failed to create shim task: annotation io.katacontainers.config.hypervisor.default_vcpus is not enabled: unknown

色々調べると annotation を設定するためには明示的に enable に指定する必要があるようです。annotations はソースコードとしては以下の箇所で処理されています。

https://github.com/kata-containers/kata-containers/blob/main/src/runtime/pkg/katautils/config.go

NumVCPUs                       float32                   `toml:"default_vcpus"`
DefaultMaxVCPUs                uint32                    `toml:"default_maxvcpus"`
MemorySize                     uint32                    `toml:"default_memory"`

このフィールド名に基づいて、kata container の設定ファイルである /etc/kata-containers/configuration.tomlenable_annotations に有効化したい annotation を追加します。

/etc/kata-containers/configuration.toml
# List of valid annotation names for the hypervisor
# Each member of the list is a regular expression, which is the base name
# of the annotation, e.g. "path" for io.katacontainers.config.hypervisor.path"
- enable_annotations = ["enable_iommu", "virtio_fs_extra_args", "kernel_params"]
+ enable_annotations = ["enable_iommu", "virtio_fs_extra_args", "kernel_params", "default_vcpus", "default_max_vcpus", "default_memory"]

手元の環境では kata runtime 実行時に上記の設定ファイルがうまく読み込まれなかったため、以下も追加で設定しました。このあたりは環境によるかも

  • /usr/local/bin/containerd-shim-kata-v2 内で KATA_CONF_FILE で読み込むファイルを明示的に指定。
  • 残りの引数は /opt/kata/bin/containerd-shim-kata-v2 実行時に受け渡す。
/usr/local/bin/containerd-shim-kata-v2
#!/bin/bash
KATA_CONF_FILE=/etc/kata-containers/configuration.toml /opt/kata/bin/containerd-shim-kata-v2 $@

再度 annotation をつけてコンテナを作成

sudo nerdctl run --runtime io.containerd.kata.v2 \
      --name kata \
      --annotation io.katacontainers.config.hypervisor.default_max_vcpus=2 \
      --annotation io.katacontainers.config.hypervisor.default_vcpus=2.0 \
      --annotation io.katacontainers.config.hypervisor.default_memory=1024 \
      -it --rm docker.io/library/ubuntu

cpu が 2 つに増えており、メモリが 1 GB になっていることがわかります。

root@eec0c2ee2217:/# cat /proc/cpuinfo
processor       : 0
vendor_id       : AuthenticAMD
cpu family      : 23
model           : 24
model name      : AMD Ryzen 5 3550H with Radeon Vega Mobile Gfx
stepping        : 1
microcode       : 0x8108109
cpu MHz         : 2096.054
cache size      : 512 KB
physical id     : 0
siblings        : 1
core id         : 0
cpu cores       : 1
apicid          : 0
initial apicid  : 0
fpu             : yes
fpu_exception   : yes
cpuid level     : 13
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm rep_good nopl cpuid extd_apicid tsc_known_freq pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm cmp_legacy svm cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw perfctr_core ssbd ibpb vmmcall fsgsbase tsc_adjust bmi1 avx2 smep bmi2 rdseed adx smap clflushopt sha_ni xsaveopt xsavec xgetbv1 clzero xsaveerptr virt_ssbd arat npt nrip_save arch_capabilities
bugs            : sysret_ss_attrs null_seg spectre_v1 spectre_v2 spec_store_bypass retbleed smt_rsb srso div0
bogomips        : 4192.10
TLB size        : 1024 4K pages
clflush size    : 64
cache_alignment : 64
address sizes   : 48 bits physical, 48 bits virtual
power management:

processor       : 1
vendor_id       : AuthenticAMD
cpu family      : 23
model           : 24
model name      : AMD Ryzen 5 3550H with Radeon Vega Mobile Gfx
stepping        : 1
microcode       : 0x8108109
cpu MHz         : 2096.054
cache size      : 512 KB
physical id     : 1
siblings        : 1
core id         : 0
cpu cores       : 1
apicid          : 1
initial apicid  : 1
fpu             : yes
fpu_exception   : yes
cpuid level     : 13
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm rep_good nopl cpuid extd_apicid tsc_known_freq pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm cmp_legacy svm cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw perfctr_core ssbd ibpb vmmcall fsgsbase tsc_adjust bmi1 avx2 smep bmi2 rdseed adx smap clflushopt sha_ni xsaveopt xsavec xgetbv1 clzero xsaveerptr virt_ssbd arat npt nrip_save arch_capabilities
bugs            : sysret_ss_attrs null_seg spectre_v1 spectre_v2 spec_store_bypass retbleed smt_rsb srso div0
bogomips        : 4192.10
TLB size        : 1024 4K pages
clflush size    : 64
cache_alignment : 64
address sizes   : 48 bits physical, 48 bits virtual
power management:


root@eec0c2ee2217:/# free -h
               total        used        free      shared  buff/cache   available
Mem:           985Mi        41Mi       949Mi        32Ki       8.6Mi       944Mi
Swap:             0B          0B          0B

ホスト側の qemu プロセスを比較すると引数が -m 1024M, -smp 2 に変更されていることが確認できます。

qemu プロセスの詳細

/opt/kata/bin/qemu-system-x86_64 -name sandbox-94710f792caefe73519d9269ce80cc4340a90602274ebf1c20686433911ac07c -uuid f8638b72-72bd-4ff0-9095-acca9755d29c -machine q35,accel=kvm,nvdimm=on -cpu host,pmu=off -qmp unix:fd=3,server=on,wait=off -m 1024M,slots=10,maxmem=17013M -device pci-bridge,bus=pcie.0,id=pci-bridge-0,chassis_nr=1,shpc=off,addr=2,io-reserve=4k,mem-reserve=1m,pref64-reserve=1m -device virtio-serial-pci,disable-modern=true,id=serial0 -device virtconsole,chardev=charconsole0,id=console0 -chardev socket,id=charconsole0,path=/run/vc/vm/94710f792caefe73519d9269ce80cc4340a90602274ebf1c20686433911ac07c/console.sock,server=on,wait=off -device nvdimm,id=nv0,memdev=mem0,unarmed=on -object memory-backend-file,id=mem0,mem-path=/opt/kata/share/kata-containers/kata-ubuntu-latest.image,size=268435456,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=4,id=vsock-1826160476,guest-cid=1826160476 -chardev socket,id=char-7f812da4e742018b,path=/run/vc/vm/94710f792caefe73519d9269ce80cc4340a90602274ebf1c20686433911ac07c/vhost-fs.sock -device vhost-user-fs-pci,chardev=char-7f812da4e742018b,tag=kataShared,queue-size=1024 -rtc base=utc,driftfix=slew,clock=host -global kvm-pit.lost_tick_policy=discard -vga none -no-user-config -nodefaults -nographic --no-reboot -object memory-backend-file,id=dimm1,size=1024M,mem-path=/dev/shm,share=on -numa node,memdev=dimm1 -kernel /opt/kata/share/kata-containers/vmlinux-6.1.62-135 -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 cryptomgr.notests net.ifnames=0 pci=lastbus=0 root=/dev/pmem0p1 rootflags=dax,data=ordered,errors=remount-ro ro rootfstype=ext4 console=hvc0 console=hvc1 quiet systemd.show_status=false panic=1 nr_cpus=2 selinux=0 systemd.unit=kata-containers.target systemd.mask=systemd-networkd.service systemd.mask=systemd-networkd.socket scsi_mod.scan=none -pidfile /run/vc/vm/94710f792caefe73519d9269ce80cc4340a90602274ebf1c20686433911ac07c/pid -smp 2,cores=1,threads=1,sockets=2,maxcpus=2

上記のように kata containers では annotation を使うことで VM や hypervisor の動作を制御することが可能となっています。

k8s の場合は pod annotations に設定することで同様の動作が実現できます。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ubuntu
  labels:
    app: ubuntu
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ubuntu
  template:
    metadata:
      labels:
        app: ubuntu
      annotations:
        io.katacontainers.config.hypervisor.default_max_vcpus: "2"
        io.katacontainers.config.hypervisor.default_vcpus: "2.0"
        io.katacontainers.config.hypervisor.default_memory: "1024"
    spec:
      runtimeClassName: kata
      containers:
      - name: ubuntu
        image: ubuntu:23.04
        command:
          - sleep
          - "36000"

Discussion