ARM (Raspberry pi 4) 上で Firecracker を動かしてみる


## 初めに

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 のバイナリ取得

[Getting Started 該当箇所](https://github.com/firecracker-microvm/firecracker/blob/master/docs/getting-started.md#getting-the-firecracker-binary)

Firecracker の latest release タグの情報を取得

```bash
latest=$(basename $(curl -fsSLI -o /dev/null -w  %{url_effective}  https://github.com/firecracker-microvm/firecracker/releases/latest))
```

このワンライナーは個人的に面白いと思ったので少し説明を加えてみます(KVM や Firecracker とは直接関係ないですが&#x2026;)。
-   `curl -fsSLI -o /dev/null -w  %{url_effective} <URL>` でリダイレクト先の URL を取得
-   `basename` でURL のファイルパスの最下層の文字列を取得 (これ URL でも動くんですね)

あとは、以下の通りコマンドを実行し, `firecracker` コマンドを準備します。

```bash
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 の設定

[Getting Started 該当箇所](https://github.com/firecracker-microvm/firecracker/blob/master/docs/getting-started.md#appendix-a-setting-up-kvm-access)

`/dev/kvm` にアクセスできるよう拡張 ACL の設定を行います。

まず、ACL の設定に必要なコマンドのインストール

```bash
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 該当箇所](https://github.com/firecracker-microvm/firecracker/blob/1757ab621ff510aa44316de53ec6665a1624e6cc/docs/getting-started.md#prerequisites)
(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 です。

```bash

    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 該当箇所](https://github.com/firecracker-microvm/firecracker/blob/1757ab621ff510aa44316de53ec6665a1624e6cc/docs/getting-started.md#running-firecracker)
(`get the kernel and rootfs, if you don't have any available:` のコードブロック)

下記スクリプトを実行すると、Firecracker 上で動作するサンプルイメージを取得できます。

```bash

    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 起動

[Getting Started 該当箇所](https://github.com/firecracker-microvm/firecracker/blob/master/docs/getting-started.md#running-firecracker)

Firecracker を起動します。非常に単純なコマンドですね。

    ./firecracker --api-sock /tmp/firecracker.socket

このコマンドを実行すると、次に述べる API コールを待つ状態になります。

## API 呼び出しによる MicroVM の起動

[Getting Started 該当箇所](https://github.com/firecracker-microvm/firecracker/blob/1757ab621ff510aa44316de53ec6665a1624e6cc/docs/getting-started.md#running-firecracker)
`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 コール

```bash

    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 コール

```bash

    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 コール

```bash
    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 の仮想化支援機能に触れてみたいところです。