Red Hatでコンサルタントをしている織です。本記事ではibkrunについて紹介します。Red Hat Advent Calender 2023の12/3のエントリです (だいぶ過ぎてますが、空いたままだったので急遽埋めました)。
libkrunについては、inductorさんによるKernel/VM探検隊online part2の講演が有名かもしれません。本記事(および続編...を書く予定です)では、別の観点でlibkrunをご紹介できればと思います。
libkrunとは
libkrun とは動的ライブラリとして提供される仮想マシンモニター(VMM)です。とても簡単に言うと、このライブラリを使うとプロセスをLinux KVMの仮想マシンとして隔離した状態で実行することができます。CのAPIを提供していますが、中の実装はほとんどRustで書かれています。Firecrackerやrust-vmmのコードも取り込んでいます。
可能な限り軽量かつシンプルな実装を目指しており、使用している仮想デバイスも最小限です。
- virtio-console
- virtio-vsock (specialized for TSI, Transparent Socket Impersonation)
- virtio-fs
- virtio-baloon (only free-page reporting)
- virtio-rng
- virtio-block (for AMD SEV)
libkrunを動かしてみる
さっそく使ってみましょう。最近のFedoraだと、必要なコンポーネントは全てrpmで提供されています。以下の検証はFedora 38が稼働するノートPCで行いました。
$ sudo dnf install -y libkrun libkrun-devel libkrunfw
最小限のコードは以下のような感じになります。
#include <libkrun.h> void main() { char *const envp[] = {0}; int ctx_id = krun_create_ctx(); krun_set_vm_config(ctx_id, 1, 512); krun_set_root(ctx_id, "rootfs"); krun_set_exec(ctx_id, "/bin/bash", 0, &envp[0]); krun_start_enter(ctx_id); }
minimal.c
として保存し、コンパイルします。
$ gcc -o minimal miniaml.c -lkrun
rootfs
というディレクトリを掘って、そこにLinuxとして稼働できる最小限のファイルツリーを作ります。まずはfedoraイメージでコンテナを実行します。
$ podman run --name fedora fedora uname
コンテナのファイル群をrootfs以下にコピーします。
$ mkdir rootfs $ podman export fedora | tar xf - -C rootfs
また、後の実験で使ういくつかのファイルもコピーしておきます。
$ sudo cp /etc/resolv.conf ./rootfs/etc/ $ sudo cp /usr/sbin/ip ./rootfs/usr/sbin/ $ sudo cp /lib64/libbpf.so.1 ./rootfs/lib64/ $ sudo cp /lib64/libmnl.so.0 ./rootfs/lib64/
これで準備は整いました。libkrunをリンクした実行ファイル minimal
を実行すると、rootfs以下のファイルを使って仮想マシンが作られ、その中でプロセスが起動します。
$ ./minimal bash-5.2#
minimal.cの krun_set_exec()
で呼び出したbashが起動しました。minimalの中では、Fedora 39 のコンテナイメージのファイルを使った仮想マシンが動いています。
bash-5.2# cat /etc/fedora-release Fedora release 39 (Thirty Nine)
NICは何もついていません。
bash-5.2# /usr/sbin/ip addr show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever
が、外と通信はできてしまいます。まずはminimalの中からインターネットにアクセスしてみます。
bash-5.2# curl ftp.jaist.ac.jp lrwxrwxrwx 1 ftp ftp 17 Feb 15 2016 debian -> pub/Linux/debian/ lrwxrwxrwx 1 ftp ftp 27 Feb 15 2016 debian-backports -> pub/Linux/debian-backports/ lrwxrwxrwx 1 ftp ftp 20 Feb 15 2016 debian-cd -> pub/Linux/debian-cd/ drwxr-xr-x 30 ftp ftp 32 May 16 2021 pub lrwxrwxrwx 1 ftp ftp 36 Feb 15 2016 raspbian -> pub/Linux/raspbian-archive/raspbian/ lrwxrwxrwx 1 ftp ftp 17 Feb 15 2016 ubuntu -> pub/Linux/ubuntu/
また、minimalを実行したKVMホストからminimalの中にアクセスしてみましょう。 まずはminimalの中で、ポート34000番でWebサーバを起動します。
bash-5.2# python3 -m http.server 34000 Serving HTTP on 0.0.0.0 port 34000 (http://0.0.0.0:34000/) ...
次に別のターミナルを開き、KVMホストから該当ポートにアクセスします。
$ curl localhost:34000 <!DOCTYPE HTML> <html lang="en"> <head> <meta charset="utf-8"> <title>Directory listing for /</title> </head> <body> ...
libkrunが外と通信する仕組みについては、日を改めて紹介したいと思います こちらに書きました。
Podmanのコンテナを仮想マシンで隔離して実行する
最近のFedoraでは、Podmanを実行する際のOCIランタイムとしてcrunを使用します。crunは、コンテナ実行時に run.oci.handler
というアノテーションが渡されると、その情報に応じて実行するハンドラを切り替える仕組みを持っています。この仕組みを使うことで、crunはコンテナを実行する際に、dlopen(3)でlibkrun.soをロードしlibkrunの仕組みを使ってコンテナプロセスを起動します。
$ podman run -d \ -v /dev/kvm:/dev/kvm \ -p 8080:80 \ --annotation=run.oci.handler=krun \ --name nginx nginx
これで、nginxのコンテナがKVM仮想マシンの中に隔離された状態で起動しました。
$ curl localhost:8080 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } ...
プロセスIDから見える情報を確認してみます。
$ pid=$(podman inspect nginx | jq '.[].State.Pid') $ ps -p ${pid} f PID TTY STAT TIME COMMAND 2839261 ? Ssl 0:01 [libcrun:krun] /docker-entrypoint.sh nginx -g daemon off;
なんとなくlibkrunを使って実行されている感があります。 このPIDで使用しているファイルディスクリプタを確認すると、実際KVM仮想マシン内で実行されていることを確認することができます。
$ ls -l /proc/${pid}/fd | grep kvm lrwx------. 1 ori ori 64 Dec 9 01:24 19 -> anon_inode:kvm-vm lrwx------. 1 ori ori 64 Dec 9 01:29 29 -> anon_inode:kvm-vcpu:0 lrwx------. 1 ori ori 64 Dec 9 01:29 31 -> anon_inode:kvm-vcpu:1 lrwx------. 1 ori ori 64 Dec 9 01:29 33 -> anon_inode:kvm-vcpu:2 lrwx------. 1 ori ori 64 Dec 9 01:29 35 -> anon_inode:kvm-vcpu:3 lrwx------. 1 ori ori 64 Dec 9 01:29 37 -> anon_inode:kvm-vcpu:4 lrwx------. 1 ori ori 64 Dec 9 01:29 39 -> anon_inode:kvm-vcpu:5 lrwx------. 1 ori ori 64 Dec 9 01:29 41 -> anon_inode:kvm-vcpu:6 lrwx------. 1 ori ori 64 Dec 9 01:29 43 -> anon_inode:kvm-vcpu:7