KVM、QEMU、Firecrackerの違いと役割:マイクロVMの核心を理解する

KVM、QEMU、Firecrackerの関係性は、初見では混乱しやすいものです。

  • KVMは仮想マシンなのか?
  • QEMUとKVMはどう関係しているのか?
  • FirecrackerはQEMUを「置き換えた」ものなのか?
  • もし自分でmini Firecrackerを作るなら、最低限何が必要なのか?

これらの疑問を追いかけていく中で、最終的に気づいたのは、これらの技術は同一レイヤーの概念ではない、ということです。一言でまとめると、その関係は次のようになります。

  • KVM:カーネル内のハードウェア仮想化実行バックエンド
  • QEMU:ユーザースペースの汎用VMM / デバイスエミュレータ
  • Firecracker:ユーザースペースの極簡VMM、KVMの上に構築

つまり、QEMUとFirecrackerはどちらもKVMを使えます。 両者の真の違いは「KVMがあるかないか」ではなく、誰がVMMを担い、そのVMMがどれだけ大きく複雑かにあるのです。本記事では、この点を明確に解説します。

一、問題の起点:なぜFirecrackerが必要なのか?

Firecrackerは空から現れたわけではありません。非常に現実的な問題を解決するために生まれました。それは、クラウドコンピューティングのシナリオ、例えばAWS LambdaやFargate、Serverlessプラットフォームにおいて、1台の物理サーバー上で多くのユーザーからのコードを同時に実行するケースです。

コンテナは軽いが、隔離が不十分

Dockerやcontainerdというアプローチには明らかな利点があります。

  • 起動が速い
  • リソースオーバーヘッドが小さい
  • オーケストレーションが成熟している

しかし、コンテナはホストカーネルを共有します。これは、コンテナの隔離境界が本質的にハードウェアレベルではないことを意味します。カーネル、namespace、cgroup、ランタイムのいずれかのレイヤーに問題が発生すると、コンテナ間の境界が破られる可能性があります。

従来の仮想マシンは隔離が強いが、重すぎる

もう一方の従来のVMは、

  • 独自のカーネルを持つ
  • 隔離境界が強い
  • 「本当に独立したマシン」に近い

という利点がありますが、問題点も明確です。

  • 起動が遅い
  • メモリオーバーヘッドが大きい
  • デバイスモデルが複雑
  • 歴史的な互換性の負担が大きい

小さな関数や短いライフサイクルのタスクを実行するだけなら、従来のVMは過剰な場合が多いです。

Firecrackerの目標

Firecrackerの目標は、最小限のオーバーヘッドでVMレベルの隔離を得ることです。完全なPCをエミュレートすることを追求するのではなく、次のようなものを追求します。

  • デバイスを最小限に
  • 機能を最小限に
  • 攻撃対象領域を最小限に
  • 高速な起動
  • 小さいメモリ使用量

したがって、Firecrackerは本質的に、KVMの上に構築された極簡なマイクロVM VMMです。

二、まず関係性を明確に:KVM、QEMU、Firecrackerそれぞれの役割

ここが最も混同しやすいポイントです。

1. KVMとは何か?

KVM(Kernel-based Virtual Machine)は、Linuxカーネル内に組み込まれた仮想化機能です。これは完全な仮想マシンでも、デバイスエミュレータでもありません。その核心的な役割は単純です。

  • VMを作成する
  • vCPUを作成する
  • ゲストコードを物理CPUで直接実行させる
  • ゲストがVM Exitを発生させた際、制御をユーザースペースのVMMに戻す

したがって、KVMは、カーネルが提供する「仮想CPU実行エンジン」と理解できます。それは、「ゲストコードをハードウェア上で効率的に実行するにはどうすればよいか?」という問題を解決します。

2. QEMUとは何か?

QEMUはユーザースペースのVMM/エミュレータです。その役割は以下の通りです。

  • カーネル/initrd/BIOSを読み込む
  • ゲストメモリを配置する
  • ディスク、ネットワークカード、シリアルポート、タイマーなどのデバイスをエミュレートする
  • ゲストのI/O動作を処理する
  • 完全なブートプロセスを提供する

もしKVMを使わない場合、QEMUは純粋なソフトウェアエミュレーションとして動作し、非常に遅くなります。もしKVMを使う場合、例えば以下のコマンドのように:

qemu-system-x86_64 -accel kvm ...

これは、

  • QEMUがマシンを管理し
  • KVMがCPUの実行を管理する

という分担になります。したがって、QEMUはKVMの代替品ではなく、KVMの上層で最も一般的なVMMです。

3. Firecrackerとは何か?

FirecrackerもユーザースペースのVMMですが、QEMUとは全く異なる理念を持っています。

QEMUの目標は:

  • 汎用性
  • 多くのハードウェアとの互換性
  • 多数のデバイスのエミュレート
  • 様々な歴史的な負担をカバーする

Firecrackerの目標は:

  • マイクロVMで必要な最小限の機能セットのみに限定する
  • デバイスを極力少なくする
  • ブートパスを極力短くする
  • セキュリティ面積を極力小さくする

したがって、Firecrackerは「KVMがない別のもの」ではなく、Firecracker = より小さいVMM + KVMです。

三、最も重要な一句话:境界線はどこにあるのか?

仮想マシンを粗雑に二層に分けると、以下のようになります。

下層:ゲストCPUを動かす

これはKVMが解決します。

上層:それをマシンとして見せる

これはQEMU / Firecrackerが解決します。

つまり、

  • KVMが「実行」を管理する
  • VMMが「マシンの振る舞い」を管理する

非常に役立つ一句话として覚えておくとよいでしょう。

KVMがCPUを、VMMがデバイスとブートプロセスを管理する。

四、Firecrackerの各層が解決する問題

エンジニアリングの視点から見ると、Firecrackerは「巨大なブラックボックス」ではなく、問題を一つずつ解決していきます。

1. Jailer:まずVMM自身を檻に入れる

問題は、ゲストがKVMによって隔離されていても、Firecrackerプロセス自体はホスト上で実行されていることです。もしVMM自体にバグがあり、攻撃された場合、それがホストマシンに影響を与える可能性があります。

そこで、Firecrackerは起動前にJailerを使って外部隔離を一層施します。

  • namespace
  • cgroup
  • chroot
  • 権限の引き下げ
  • 最小限のファイル可視性

これは、「誰がVMM自身を制限するのか?」という問題を解決します。

2. KVMの初期化:ゲストが本当に動き出せるようにする

問題は、ゲストがソフトウェアエミュレーションに頼らず、ハードウェアをできるだけ直接利用する方法が必要だということです。

KVMが提供する核心的なオブジェクトは通常3つのfdです。

  • KVM fd
  • VM fd
  • vCPU fd

そして、ユーザースペースのVMMは以下を担当します。

  • ゲストメモリを割り当てる
  • vCPUを作成する
  • レジスタを設定する
  • `KVM_RUN`を呼び出す

これは、「ゲスト命令はいかに実行されるのか?」という問題を解決します。

3. メモリマッピング:ゲストが見る物理メモリはどこから来るのか?

ゲストは自分が連続した物理メモリを持っていると思っています。実際には、これは通常ホスト上の`mmap`領域へのマッピングです。

KVMは以下をあなたのために処理します。

  • ゲスト物理アドレス
  • ホストメモリへのマッピング

これは、「ゲストが見るRAMはどのように偽装されるのか?」という問題を解決します。

4. vCPUスレッド:ゲストCPUはどのように継続的に実行されるのか?

各vCPUは通常、ホストスレッドに対応します。VMMの典型的なメインループは以下のようになります。

  1. `KVM_RUN`を呼び出す
  2. ゲストが実行する
  3. VM Exitに遭遇する
  4. VMMが退出原因を処理する
  5. 再度ゲストに入る

これは、「ゲストがホスト上で継続的に実行され、同時に一時停止、再開、管理が可能になるにはどうすればよいか?」という問題を解決します。

5. デバイスエミュレーション:ゲストはI/Oをどうするのか?

ゲストはホストのハードウェアに直接触れることはできません。そのため、VMMがデバイスを提供する必要があります。

Firecrackerは最小限のデバイスセットを採用しています。

  • `virtio-net`
  • `virtio-blk`
  • `serial`
  • `rng`
  • `vsock`
  • `balloon`
  • など、少数の必須デバイス

ここでの重要な点は、Firecrackerは伝統的なPCハードウェアをエミュレートするより、VirtIOを好むことです。なぜなら、VirtIOはよりシンプル、高速、攻撃対象領域が小さいからです。

これは、「ゲストはネットワーク、ディスク、シリアルポートなどの周辺機器にどのようにアクセスするのか?」という問題を解決します。

6. APIサーバー:外部はどのようにマイクロVMを制御するのか?

プラットフォームが手動でコマンドを入力してVMを制御することはできません。制御インターフェースが必要です。

FirecrackerはUnixソケット上のAPIを提供しており、以下のように分かれています。

  • ブート前の設定
  • ランタイム操作

例えば:

  • カーネルを設定する
  • ドライブをマウントする
  • ネットワークカードを設定する
  • start
  • pause
  • snapshot
  • restore

これは、「プラットフォームの制御面がVMのライフサイクルをどのように管理するのか?」という問題を解決します。

7. MMDS:ゲストはどのように自分のメタデータを取得するのか?

クラウド環境では、ゲストはしばしば以下を知る必要があります。

  • 自分が誰なのか
  • 設定は何か
  • どのトークン/クレデンシャルを取得すべきか

FirecrackerはMMDS(Machine Metadata Service)を提供し、クラウドプラットフォームのメタデータサービスを模倣します。

これは、「ゲストはどのように自分自身の設定メタデータを取得するのか?」という問題を解決します。

8. Seccomp:最後の防衛線

すでにJailerやKVMがあるにもかかわらず、Firecrackerプロセスがシステムコールを乱用するのを防ぐ必要があります。

そのため、異なるスレッドには異なるseccompのホワイトリストが適用されます。

これは、「もしVMMに問題が発生した場合、被害の範囲をさらに制限するにはどうすればよいか?」という問題を解決します。

9. Snapshot / Restore:コールドスタートを解決する

これはServerlessシナリオにおいて、Firecrackerが非常に重要な役割を果たす部分です。

毎回以下のプロセスから始めるのではなく、

  • カーネルを起動する
  • OSを起動する
  • ランタイムを起動する
  • アプリケーションをロードする

まず一度起動し、スナップショットを保存し、その後直接復元します。

これは、「コールドスタートの遅延をミリ秒レベルに圧縮するにはどうすればよいか?」という問題を解決します。

10. イベントループ:API、デバイス、タイマー、vCPUをどのように連携させるのか?

VMMは多くのイベントを同時に処理する必要があります。

  • APIリクエスト
  • デバイスI/O
  • 割り込み
  • タイマー
  • ゲスト退出

Firecrackerは`epoll`ベースのイベントループを使ってこれらのことを整理します。

これは、「VMM内部のイベントフローをどのように効率的に調整するのか?」という問題を解決します。

五、もし自分でmini Firecrackerを作るなら、最低限何が必要なのか?

この問題は「Firecrackerの全機能を実装する」よりも、より意義があります。なぜなら、多くの人が初めてマイクロVMランタイムを作る場合、一気に以下のすべてを手作りしようとすると、複雑さに溺れてしまうからです。

  • Jailer
  • Seccomp
  • Snapshot
  • MMDS
  • virtio-blk
  • 複数vCPU
  • ホットプラグ

本当に「最小限で動作する」目標は何でしょうか?例えば、以下のループは非常に良いでしょう。

  1. ローカルで`N`個のマイクロVMを起動する
  2. 各ゲストがネットワークに接続できる
  3. 各ゲストが起動後にホストAPIにアクセスする
  4. ホストAPIがリクエストを受け取った後、`count + 1`する

この目標を逆算すると、最低限必要なものは以下の通りです。

  • Linuxホスト + `/dev/kvm`
  • 1つのvCPU
  • ゲストメモリ
  • カーネル
  • initramfs
  • シリアルポート
  • virtio-net
  • tap / bridge
  • ホストカウンタAPI
  • シンプルなランチャー

最も重要な簡略化:まずディスクを考えない

第一版で最も行うべき簡略化は、ルートディスク、virtio-blkを考えず、直接initramfsを使うことです。こうすれば、ゲスト起動後にすぐに`/init`を実行し、最小限のユーザースペースプログラムをinitramfsに組み込めばよいのです。

ゲストの`/init`は三つのことを行います。

  1. ネットワークを設定する
  2. `http://host/incr`を呼び出す
  3. ログを出力してシャットダウンする

これにより、多くの複雑さを削減できます。

六、なぜ最初に「QEMU + KVM」を選択し、直接VMMを手作りしなかったのか?

なぜなら、もし目標がビジネスのクローズドループをまず実現することであるなら、QEMUは非常に適した移行層となるからです。

例えば、以下を検証したいだけなら:

  • ゲストが起動できる
  • ゲストがvirtio-net経由で外部ネットワークに出られる
  • ゲストがホストAPIにリクエストを送れる
  • ホストのカウントが`N`になる

最初から自作VMMに手を出すと、すぐに以下の問題に直面します。

  • Linuxブートプロトコル
  • ゲストメモリのレイアウト
  • vCPUの初期化
  • シリアルポートのエミュレート
  • virtio-net
  • TAP転送
  • VM Exitの処理

これらはすべて正しいですが、同時にやるべきではないのです。

したがって、当時のエンジニアリング判断は以下の通りでした。

まずQEMUをVMMとして使って、control plane、network、guest workloadを動かし、ビジネスのクローズドループを成立させる。 その後、段階的にQEMUを置き換える。

このアプローチの利点は、

  • まずクローズドループを成立させる
  • デバッグのベンチマークをまず安定させる
  • 後でバックエンドを置き換える際、CLIとビジネスの受け入れ基準が変わらない

七、QEMUとKVMの関係で最も誤解されやすい点

多くの人が言うかもしれません。

「QEMUを使っているのなら、KVMとは何の関係があるのか?」

答えは、非常に大きいです。

もし以下のコマンドを実行している場合:

qemu-system-x86_64 -accel kvm ...

ゲストCPUは実際にはKVM上で実行されています。ただし、あなた自身が`/dev/kvm`のioctlを書くのではなく、QEMUがそれを代行しているだけです。

したがって、正確には:

  • QEMU without KVM:ソフトウェアエミュレーション、遅い
  • QEMU + KVM:QEMUがマシンを管理し、KVMが実行を管理する
  • Firecracker + KVM:Firecrackerがマシンを管理し、KVMが実行を管理する

言い換えれば、FirecrackerとQEMUの真の違いはVMM層にあり、KVM層ではないのです。

八、QEMUから「自作FirecrackerスタイルのVMM」へ進むべき道

これは、今思うと最も現実的なエンジニアリングの道筋です。

第一歩:まず外部体験を安定させる

実際にバックエンドを置き換える前に、まずこれらを安定させます。

  • 設定ファイル
  • 起動コマンド
  • 環境診断
  • ログ
  • ランタイムディレクトリ
  • エラーメッセージ

例えば、私たちは後に以下を追加しました。

  • `doctor`
  • `minivm.toml`
  • 対話式`init`ガイド
  • バックエンドの抽象化

このような変更は表面上KVMとは関係ないように見えますが、非常に重要です。なぜなら、これらが決まることで、新しいバックエンドをデバッグする際、外部の世界が安定しているかどうかが決まるからです。

第二歩:バックエンドのシームを抽出する

もし`launcher`が直接QEMUのコマンドラインに依存していると、後で置き換えるのが非常に難しくなります。

したがって、明確なバックエンドの境界が必要です。

  • launcherがオーケストレーションを管理する
  • backendがゲストランタイムを管理する

こうすれば、その後:

  • `qemu`バックエンドは引き続き使用可能
  • `kvm`バックエンドを段階的に追加できる

第三歩:ブートのみのKVMバックエンドを作る

本当に自作VMMの第一歩は、ネットワークではなく、

ゲストカーネルが起動し、シリアルポートに出力できるようにする

ことです。なぜなら、シリアルポートが安定すれば、最も基本的なデバッグ手段を手に入れるからです。

目標は以下の通りです。

  • VMを作成する
  • メモリを割り当てる
  • 1つのvCPUを作成する
  • カーネル/initramfsを読み込む
  • ゲストのシリアルポートにログを出力する
  • ゲストを停止する

第四歩:次にvirtio-netを追加する

ブートとシリアルポートが安定したら、ネットワークを追加します。こうすれば、既存の観測可能なブートパスにデバイスを一つ追加するだけで済み、全体をデバッグする必要がなくなります。

九、Firecrackerへの最大の理解の転換

以前は、私はこう考えていました。

「Firecrackerは非常に優れた仮想マシンソフトウェアだ」

しかし、その後、この理解があまりに漠然としていることに気づきました。

より正確な理解は、

Firecrackerは新しいCPU仮想化の原理を発明しているのではなく、KVMの上で非常に抑制的で、非常にエンジニアリングされたVMMの切り詰めを行っているのです。

その優れている点は、

  • 何が必須であるかを知っている
  • 何を削減できるかを知っている
  • 何を後回しにできるかを知っている
  • どのように多層の隔離でリスクを低減するかを知っている
  • どのようにブートパスをクラウドシナリオに適したミリ秒レベルに短縮するかを知っている

ですから、もし自分でもmini Firecrackerを作りたいのであれば、本当に重要なのは「KVM ioctlを呼べるかどうか」ではなく、

問題を最短のクローズドループに分解する能力があるかどうかなのです。

十、結論

この記事を一句话に圧縮すると、それは次のようになります。

KVMは「ゲストをいかに効率的に実行するか」を解決し、QEMUとFirecrackerは「ゲストをマシンとしていかに存在させるか」を解決する。

そして、Firecrackerの核心的な価値は「KVMを置き換える」ことではなく、

KVMの上で、VMMを極簡、高速、制御可能、安全にすることにあります。

私にとっての真の収穫は、これらの用語を暗記することではなく、ようやくこの進化の道筋を見通せたことです。

  1. まずQEMU + KVMでビジネスのクローズドループを動かす
  2. 次にバックエンドの境界を抽出する
  3. その後、自作KVMブートパスに段階的に置き換える
  4. 最後にvirtio-net、スナップショット、セキュリティ隔離などのより複雑な機能に進む

タグ: KVM QEMU Firecracker microVM linux

8月4日 17:54 投稿