初めに
ARM の仮想化支援機能に興味を持って Raspberry Pi 4 を買ったので、何か試したくなりました。
とりあえず何か動かせそうなものを探したら、Firecracker が一番簡単そうだったので試してみました。
参考にしたページ
この記事は基本的に下記ページの通りを参考にしました。
ほぼ手順通りにやっただけです。
ラズパイ4にFirecrackerの環境を構築してMicroVMを動かそう!!: https://dev.classmethod.jp/articles/firecracker-with-raspi4b/
上記記事は Firecracker のレポジトリにある Getting Started に沿っているようです。
本記事の本文中には適宜 Getting Started の該当箇所へのポインタを貼っていきます。
- FirecrackerのGetting Started: https://github.com/firecracker-microvm/firecracker/blob/master/docs/getting-started.md
動作環境
- Raspberry Pi: Raspberry Pi 4 Modeb B (4GB RAM)
- Firecracker: v0.21.1
- OS: Ubuntu 19.10
- Linux kernel: ubuntu 5.3.0-1014-raspi2
Host OS の用意
現状 KVM が初めから有効になっている OS イメージは限られているようです。
(メジャーなディストリビューションである Raspbian は対応していませんでした)
今回は、 Ubuntu Wiki で公開されている Ubuntu server 19.10 のイメージを使います。
このイメージは KVM が有効になっています。
おそらくこれを使うのが今のところ一番簡単に KVM を試す方法じゃないかと思います。
- Ubuntu wiki: https://wiki.ubuntu.com/ARM/RaspberryPi
- Image ファイルへの直リンク http://cdimage.ubuntu.com/releases/eoan/release/ubuntu-19.10.1-preinstalled-server-arm64+raspi3.img.xz
上記イメージをダウンロードして, unxz
で解凍したものを、SD カードに書き込み、Raspberry Pi 4 に差し込んで起動します。
Firecracker のバイナリ取得
Firecracker の latest release タグの情報を取得
latest=$(basename $(curl -fsSLI -o /dev/null -w %{url_effective} https://github.com/firecracker-microvm/firecracker/releases/latest))
このワンライナーは個人的に面白いと思ったので少し説明を加えてみます(KVM や Firecracker とは直接関係ないですが…)。
-
curl -fsSLI -o /dev/null -w %{url_effective} <URL>
でリダイレクト先の URL を取得 -
basename
でURL のファイルパスの最下層の文字列を取得 (これ URL でも動くんですね)
あとは、以下の通りコマンドを実行し, firecracker
コマンドを準備します。
curl -LOJ https://github.com/firecracker-microvm/firecracker/releases/download/${latest}/firecracker-${latest}-$(uname -m)
mv firecracker-${latest}-$(uname -m) firecracker
sudo chmod +x firecracker
ACL の設定
/dev/kvm
にアクセスできるよう拡張 ACL の設定を行います。
まず、ACL の設定に必要なコマンドのインストール
sudo apt install acl
設定を変更する前に、一度 ACL の設定を確認してみます。
deep@ubuntu:~$ sudo getfacl /dev/kvm
getfacl: Removing leading '/' from absolute path names
# file: dev/kvm
# owner: root
# group: kvm
user::rw-
group::rw-
other::---
/dev/kvm
へアクセスできるように権限を追加します。
sudo setfacl -m u:${USER}:rw /dev/kvm
設定の変更を確認してみます。
deep@ubuntu:~$ sudo getfacl /dev/kvm
getfacl: Removing leading '/' from absolute path names
# file: dev/kvm
# owner: root
# group: kvm
user::rw-
user:deep:rw-
group::rw-
mask::rw-
other::---
システム要件の確認
Getting Started 該当箇所
(Click here to see a BASH script that will check if your system meets the basic requirements to run Firecracker. を押すと表示される)
以下のスクリプトを実行することで、 Firecracker を実行するための要件を満たしているか否かをチェックできます。
Your system looks ready for Firecracker!
と表示されれば OK です。
err="";
[ "$(uname) $(uname -m)" = "Linux x86_64" ] \
|| [ "$(uname) $(uname -m)" = "Linux aarch64" ] \
|| err="ERROR: your system is not Linux x86_64 or Linux aarch64."; \
[ -r /dev/kvm ] && [ -w /dev/kvm ] \
|| err="$err\nERROR: /dev/kvm is innaccessible."; \
(( $(uname -r | cut -d. -f1)*1000 + $(uname -r | cut -d. -f2) >= 4014 )) \
|| err="$err\nERROR: your kernel version ($(uname -r)) is too old."; \
dmesg | grep -i "hypervisor detected" \
&& echo "WARNING: you are running in a virtual machine." \
&& echo "Firecracker is not well tested under nested virtualization."; \
[ -z "$err" ] && echo "Your system looks ready for Firecracker!" || echo -e "$err"
サンプルイメージの取得
Getting Started 該当箇所
(get the kernel and rootfs, if you don't have any available:
のコードブロック)
下記スクリプトを実行すると、Firecracker 上で動作するサンプルイメージを取得できます。
arch=`uname -m`
dest_kernel="hello-vmlinux.bin"
dest_rootfs="hello-rootfs.ext4"
image_bucket_url="https://s3.amazonaws.com/spec.ccfc.min/img"
if [ ${arch} = "x86_64" ]; then
kernel="${image_bucket_url}/hello/kernel/hello-vmlinux.bin"
rootfs="${image_bucket_url}/hello/fsfiles/hello-rootfs.ext4"
elif [ ${arch} = "aarch64" ]; then
kernel="${image_bucket_url}/aarch64/ubuntu_with_ssh/kernel/vmlinux.bin"
rootfs="${image_bucket_url}/aarch64/ubuntu_with_ssh/fsfiles/xenial.rootfs.ext4"
else
echo "Cannot run firecracker on $arch architecture!"
exit 1
fi
echo "Downloading $kernel..."
curl -fsSL -o $dest_kernel $kernel
echo "Downloading $rootfs..."
curl -fsSL -o $dest_rootfs $rootfs
echo "Saved kernel file to $dest_kernel and root block device to $dest_rootfs."
ちなみに、ダウンロードしたファイルのサイズは以下の通りでした。
deep@ubuntu:~$ ls -lh | grep hello
-rw-rw-r-- 1 deep deep 200M May 10 05:04 hello-rootfs.ext4
-rw-rw-r-- 1 deep deep 8.2M May 10 05:02 hello-vmlinux.bin
Firecracker 起動
Firecracker を起動します。非常に単純なコマンドですね。
./firecracker --api-sock /tmp/firecracker.socket
このコマンドを実行すると、次に述べる API コールを待つ状態になります。
API 呼び出しによる MicroVM の起動
Getting Started 該当箇所
set the guest kernel (assuming you are in the same directory as the above script was run)
および
set the guest rootfs
のコードブロック
Firecracker は ソケット越しに API を呼び出すことで VM を起動できるようです。
下記手順を別コンソールで行います。
カーネルをロードするための API コール
arch=`uname -m`
kernel_path=$(pwd)"/hello-vmlinux.bin"
if [ ${arch} = "x86_64" ]; then
curl --unix-socket /tmp/firecracker.socket -i \
-X PUT 'http://localhost/boot-source' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d "{
\"kernel_image_path\": \"${kernel_path}\",
\"boot_args\": \"console=ttyS0 reboot=k panic=1 pci=off\"
}"
elif [ ${arch} = "aarch64" ]; then
curl --unix-socket /tmp/firecracker.socket -i \
-X PUT 'http://localhost/boot-source' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d "{
\"kernel_image_path\": \"${kernel_path}\",
\"boot_args\": \"keep_bootcon console=ttyS0 reboot=k panic=1 pci=off\"
}"
else
echo "Cannot run firecracker on $arch architecture!"
exit 1
fi
rootfs をロードするための API コール
rootfs_path=$(pwd)"/hello-rootfs.ext4"
curl --unix-socket /tmp/firecracker.socket -i \
-X PUT 'http://localhost/drives/rootfs' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d "{
\"drive_id\": \"rootfs\",
\"path_on_host\": \"${rootfs_path}\",
\"is_root_device\": true,
\"is_read_only\": false
}"
Guest 起動のための API コール
curl --unix-socket /tmp/firecracker.socket -i \
-X PUT 'http://localhost/actions' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"action_type": "InstanceStart"
}'
上記 3つの API コールを実行すると、 ./firecracker
を実行したコンソールで Guest の起動ログやログインシェルが見れるようになるはずです。
perf で kvm のイベントとる
最後に、ちゃんと KVM が動いているのか確認してみたかったので、 perf
コマンドで kvm のイベント回数を表示してみました。
(これがこの記事唯一の新規ポイントです。)
perf コマンドのインストール
sudo apt install linux-tools-common
sudo apt install linux-tools-5.3.0-1014-raspi2
kvm 関連のイベントを取得、表示
sudo perf stat -e 'kvm:*' -a sleep 1
結果は以下の通りでした。
ちなみに、ゲストで何も負荷をかけないと何もイベント起きなかったので、適当なコマンドを実行しながら測るといいと思います。
deep@ubuntu:~/firecracker_ws$ sudo perf stat -e 'kvm:*' -a sleep 1
libtraceevent: No such file or directory
[kvm:kvm_sys_access] bad op token :
Performance counter stats for 'system wide':
118 kvm:vgic_update_irq_pending
3 kvm:kvm_wfx_arm64
0 kvm:kvm_hvc_arm64
60 kvm:kvm_arm_setup_debug
60 kvm:kvm_arm_clear_debug
120 kvm:kvm_arm_set_dreg32
0 kvm:kvm_arm_set_regset
0 kvm:trap_reg
0 kvm:kvm_handle_sys_reg
0 kvm:kvm_sys_access
0 kvm:kvm_set_guest_debug
60 kvm:kvm_entry
60 kvm:kvm_exit
51 kvm:kvm_guest_fault
0 kvm:kvm_access_fault
0 kvm:kvm_irq_line
0 kvm:kvm_mmio_emulate
0 kvm:kvm_unmap_hva_range
0 kvm:kvm_set_spte_hva
0 kvm:kvm_age_hva
0 kvm:kvm_test_age_hva
0 kvm:kvm_set_way_flush
0 kvm:kvm_toggle_cache
58 kvm:kvm_timer_update_irq
120 kvm:kvm_get_timer_map
56 kvm:kvm_timer_save_state
56 kvm:kvm_timer_restore_state
0 kvm:kvm_timer_hrtimer_expire
56 kvm:kvm_timer_emulate
51 kvm:kvm_userspace_exit
3 kvm:kvm_vcpu_wakeup
60 kvm:kvm_set_irq
0 kvm:kvm_ack_irq
66 kvm:kvm_mmio
0 kvm:kvm_fpu
0 kvm:kvm_age_page
0 kvm:kvm_halt_poll_ns
1.005360291 seconds time elapsed
おわりに
今回は ARM で動く VMM として Firecracker を試してみました。
KVM が有効な OS とサンプルの VM イメージが手に入るので簡単に試すことができました。
今後は自分で ARM の仮想化支援機能に触れてみたいところです。