ステータス:ドラフト(2026-10-16)、公式ドキュメント+一部実測。 コンポーネントの説明は、KubeVirtリポジトリのコンポーネントドキュメントとユーザーガイドを2026-10-16に確認しました。バージョンはKubeVirt v1.9.0です。「実測」と表示されている値はTT Labの実験用VMで測定したものです。作成ルール:実測データが基本であり、測定できなかったものは「未測定」、ドキュメントから見つからなかったものは「未確認」と表示します。
学生VMが立ち上がるまで2:KubeVirtの内部
目次
- 1. VirtualMachineとVirtualMachineInstance
- 2. 4つのコンポーネント
- 3. なぜVMをポッド内に入れるのか
- 4. KVM:ハードウェアアクセラレーション
- 5. ゲスト内でコマンドを実行できない
- 6. 実際に経験したこと:1つの機能ゲートでVMが全く立ち上がらなかった
- 7. 制限事項
- 参考
1編の⑥「VM → VMI → ポッド → QEMU」を開きます。
1. VirtualMachineとVirtualMachineInstance
KubeVirtを初めて見ると、似た名前の2つのオブジェクトで混乱します。
| VirtualMachine (VM) | VirtualMachineInstance (VMI) | |
|---|---|---|
| 意味 | 「このようなVMがあり、起動するかどうか」という希望する状態(Desired State) | 今実際に稼働している1つの実行インスタンス |
| ライフサイクル | 削除するまで残る | VMを停止すると消え、起動すると新しく作成される |
| 比喩 | Deployment | Pod |
TT LabのバックエンドはVMのみを作成します。マニフェストに running: true を記述して作成と同時に起動させます。KubeVirtには同じ処理をより詳細に定義する runStrategy(Always・RerunOnFailure・Once・Manual・Halted)があり、両者は併用できません。コードのコメントには running が非推奨予定と記載されていましたが、ラン戦略ガイドではその文章が見つかりませんでした(未確認)。すでに稼働しているVMと動作を合わせるため、まだ変更していません。状態を確認する際はVMIを読み取ります。VMIがない場合は「起動中」、VMすらない場合は「削除済み」と判断します。
2. 4つのコンポーネント
KubeVirtリポジトリのコンポーネントドキュメントが説明している役割を要約すると以下の通りです。
- virt-api。 仮想化関連のリクエストの入り口です。VMIのデフォルト値を埋めて検証し、consoleやvncなどのサブリソースを提供します。学生のウェブターミナルが接続するのはここです。
- virt-controller。 クラスター全体を監視するコントローラーです。VMIを確認してそのVMIを格納する ポッドを作成し、そのライフサイクルを管理します。1つのVMIは基本的に1つのポッドとペアですが、マイグレーションを行うとペアとなるポッドが変更されることがあります。
- virt-handler。 ノードごとに1つずつ(DaemonSetとして)動作します。自身のノードに割り当てられたVMIを確認し、libvirtのドメインをVMIの状態に合わせ、ドメインの状態をクラスターに通知します。
- virt-launcher。 VMIごとに作成されるポッドのメインコンテナです。VMプロセスが実行されるcgroupとネームスペースを提供し、ポッド内のlibvirtdを介してVMを起動し、VMプロセスを監視してそれが終了すると自身も終了します。Kubernetesがポッドを先に停止しようとするとシグナルをVMに転送し、VMがシャットダウンするまでポッドの終了を延期します。
作成手順はドキュメントに7つのステップとして記載されています。VMIが作成されるとvirt-controllerがポッドを作成し、スケジューラーがノードに配置し、ポッドが開始されるとvirt-controllerがVMIにノード名を書き込み、その時点からそのノードのvirt-handlerが引き継いでポッド内のlibvirtdにドメインを作成させます。第1編の実測(Pending 0秒 → Scheduled 10秒 → Running 20秒)はこの間を測定したものです。
3. なぜVMをポッド内に入れるのか
最初は奇妙に思えます。VMはコンテナよりも重い隔離機構なのに、なぜそれを再びコンテナの中に入れるのでしょうか。答えは「Kubernetesがすでに得意としていることを再実装しないため」です。TT Labで実際に恩恵を受けたものを挙げると以下の通りです。
- スケジューリングとリソース計算。 VMのCPU・メモリはポッドのリソース要求となり、スケジューラーがそのまま計算します。2 vCPU・4GiのVM 1つのポッド要求を測定したところ、CPU 200m、メモリ 4372Miでした(実測、2026-08-29)。1つのvCPUに対してCPU 0.1個分しか要求しないのは、KubeVirtの
cpuAllocationRatioデフォルト値が10であるためであり(ユーザーガイド「Resources requests and limits」)、メモリはゲストの4Giに約280Miのオーバーヘッドが追加されました。 - ネットワークとポリシー。 ゲストはポッドのネットワークをそのまま使用します(masquerade)。そのため、実習用ポッドで使用していたネットワークポリシーをVMにも同じ方法で適用します。ポリシーが対象とするのはVMではなく virt-launcherポッドのラベルです。実際のネットワークエンドポイントがそのポッドであるためです(第6編)。
- ボリューム。 ゲストのディスクはポッドのボリュームです。PVCの接続、ノードローカルな一時ディレクトリの作成、Secretの挿入などはKubernetesが行います(第3・4編)。
- ライフサイクル。 ポッドが削除されるとVMも終了し、ポッドに付随する一時ボリュームも一緒に削除されます。クリーンアップが容易になります(第5編)。
代償もあります。
- ポッドが常に1つ追加されます。 上記の280Miのオーバーヘッドと、コンソールログを収集する小さなコンテナ(実測CPU 5m・メモリ 35M)がVMごとに必要になります。
- virt-launcherには特権が必要です。 そのため、VMが常駐するネームスペースは、Podセキュリティスタンダードの最も緩い
privilegedに設定します。この特権はKubeVirt自身のものであり、学生のものではありません。ただし、そのネームスペースに ポッドを作成できる権限は、そのままノードのrootにつながる扉になるため、バックエンドからポッド作成権限を除外しました(第6編)。
4. KVM:ハードウェアアクセラレーション
ゲストはQEMUがKVM(/dev/kvm)を使用してハードウェアアクセラレーションで動作します。KubeVirtはKVMが利用可能なノードにデバイスプラグインリソース(devices.kubevirt.io/kvm)を公開し、virt-launcherポッドがそのリソースを要求します。そのため、KVMが利用できないノードにはVMが配置されません。
ゲスト内でさらに仮想化を実行する必要がある実習(VM内でVMを起動する実習)では、CPUモデルを host-passthrough に変更します。デフォルトのCPUモデルはハードウェア仮想化フラグを隠すため、そのままだとゲスト内のQEMUがソフトウェアエミュレーションのみで動作し、起動に何分もかかります。 host-passthrough を有効にすると、ゲスト内に /dev/kvm が作成され、 lscpu に仮想化フラグが表示されました(実測、2026-08-29)。代償となるのは ライブマイグレーションです。ノードごとにCPUモデルが異なると移動できません。実習用VMはどうせ移動させないためこれを受け入れますが、不要な実習では有効化しません。
5. ゲスト内でコマンドを実行できない
ポッドには exec があるため、バックエンドはポッド内で採点コマンドを実行できます。KubeVirtのVMIサブリソースにはconsole、vnc、portforward、guestosinfoなどはあっても execはありません(実測によりサブリソースのリストを確認)。ゲストは独立したオペレーティングシステムであり、その中でコマンドを実行することは、ゲスト内に何らかのエージェントが存在する場合にのみ可能であるためです。
そのため、TT Labではゲストに小さな採点エージェントを埋め込みました(第1編 ③)。学生のシェルはシリアルコンソール(consoleサブリソース)で開きます。SSHを開く必要がないため、接続経路が1つ減ります。実際にプロトタイプでSSHを完全に無効にした状態でも採点が可能でした。
6. 実際に経験したこと:1つの機能ゲートでVMが全く立ち上がらなかった
KubeVirt v1.9.0を最初にインストールした際、VMが1つも立ち上がりませんでした。virt-launcherポッドが次のようなエラーで終了しました。
exec: "/container-disk-binary/usr/bin/container-disk": no such file or directory
コンテナランタイムの設定が壊れたのかと思いましたが、原因は別のところにありました。KubeVirt 1.9がベータ段階の機能ゲートをデフォルトで有効にしたことにより、 ImageVolume が自然と有効になりました。この機能はコンテナディスクをポッドのイメージボリュームとしてマウントしますが、 Kubernetes 1.34側ではそのゲートが無効になっていたため、APIサーバーがイメージボリュームとそのマウントを静かに削除しました。 ポッドは拒否されずに受け入れられるため、どこにも警告が残りません。実行中のポッドの仕様(Spec)からイメージボリュームが空になっているのを確認して初めて確信に至りました。コンテナランタイムも2つ目の関門でした。イメージボリュームをサポートするのはcontainerd 2.1以降であるため、Kubernetesのゲートを有効にしていたとしても、静的な削除がマウント失敗に置き換わるだけでした。
KubeVirt側のゲートを無効にして、以前の方式(空のディレクトリとコピー用initコンテナ)に戻しました。この設定はKubeVirtのカスタムリソースに含まれるため、 KubeVirtをアップデートするたびに再確認する必要が あります。記事を執筆している当日も、無効化されたゲートのリストに ImageVolume がそのまま残っていることを読み取って確認しました。
教訓は以下の通りです。「ポッド内のVM」は、Kubernetes、KubeVirt、コンテナランタイムという3つのバージョンの交点の上に成り立っています。1つだけアップデートすると、他の2つとの約束事が静かに破られることがあります。
7. 制限事項
- 上記のリソース要求量は、VMの1つのサイズ(2 vCPU・4Gi)で1回測定した値です。サイズ別のオーバーヘッドの表は 未測定です。
- VMの一時ストレージ(ノードローカルディスク)の使用量をスケジューラーに通知する方法はまだ確認できていません(第4編の制限事項の節)。
- ライブマイグレーションは使用していません。実習用VMは1時間ほど稼働して削除されるため、ノードを空ける必要がある場合は移動させずにセッションを終了します。
ティティの一言。 VMをポッド内に入れるのは最初は「箱の中のさらに大きな箱」のように思えますが、おかげでスケジューリングもネットワークポリシーもボリュームもKubernetesが代わりにやってくれます。その代わり、3つのバージョンが連動して動いていることを必ず覚えておいてください。1つだけアップデートしたせいで、VMが静かに立ち上がらなくなることがありますよ!
参考
ドキュメントはすべて2026-10-16に閲覧しました。
- KubeVirtコンポーネントドキュメント: https://github.com/kubevirt/kubevirt/blob/main/docs/components.md
- KubeVirt ユーザーガイド、リソース要求と制限(
cpuAllocationRatio): https://kubevirt.io/user-guide/compute/resources_requests_and_limits/ - KubeVirt ユーザーガイド、ディスクとボリューム: https://kubevirt.io/user-guide/storage/disks_and_volumes/
- KubeVirt ユーザーガイド、実行戦略: https://kubevirt.io/user-guide/compute/run_strategies/
- TT Lab コードと記録:
backend/app/vms.py(SIZES注釈によるリソース計測、vm_manifestのnested),backend/app/main.py(vm_console·vm_info),deploy/vms/README.md(ImageVolume ゲート)、docs/plans/2026-08-29-vm-labs-design.md