- Authors

- Name
- Youngju Kim
- @fjvbn20031
- はじめに
- 最初に掴むべきメンタルモデル
- なぜわざわざPodをVM実行単位として使うのか
- Pod内部で実際に何が動いているのか
- KubernetesとKubeVirtの境界
- ソースコードでこの構造が現れる場所
- 「Pod上でVM」が成立する核心メカニズム
- よくある誤解
- 運用者がすぐに使えるデバッグチェックポイント
- まとめ
はじめに
KubeVirtを初めて見た時に最も違和感があるのはこの点だ。「Kubernetesは元々コンテナオーケストレーターなのに、どうやってVMをPod上で起動できるのか?」この質問に答えるには、まず一つの誤解を解く必要がある。KubeVirtはkubeletの中にVM機能を組み込まない。代わりにKubernetesが既に得意なことと、仮想化スタックが得意なことを分離する。
- Kubernetesが担うもの:APIストレージ、スケジューリング、Podネットワーク、ボリュームマウント、ノード配置、リトライ
- KubeVirtが担うもの:VM関連CRD、VM専用controller、ノードエージェント、libvirtとQEMUオーケストレーション
- Linuxとハイパーバイザーが担うもの:
cgroup、namespace、tap、netlink、/dev/kvm、QEMU仮想化実行
つまりKubeVirtの本質は「コンテナランタイム上でVMをエミュレーションする」ではない。正確にはPodを VM実行のためのサンドボックスと制御単位として使用し、その中でQEMUとlibvirtを動作させる構造だ。
最初に掴むべきメンタルモデル
KubeVirtの構造を一行でまとめると次の通りだ。
- ユーザーが
VirtualMachineまたはVirtualMachineInstanceを作成する。 virt-controllerがこれを見てvirt-launcherPodを作成する。- Kubernetesがそのpodを通常のPodと同様にスケジューリングする。
- 該当ノードの
virt-handlerがPodとVMIを見て実際のVMラウンチを指揮する。 virt-launcherPod内部のlibvirtとQEMUがVMプロセスを実行する。
ここで重要なのはVMがPodの中に「パッケージ」されているということであり、ゲストOSがコンテナになるという意味ではないという点だ。ゲストOSは依然としてQEMUが提供する仮想ハードウェア上で動作する。ただし、そのQEMUプロセスがPodのリソース境界内で実行される。
KubeVirt公式アーキテクチャドキュメントのdocs/architecture.mdとdocs/components.mdもこの構造を明確に示している。そこではKubeVirtはKubernetes上に載る追加的なコントロールプレーンとノードエージェントの集合として説明されている。
なぜわざわざPodをVM実行単位として使うのか
この設計は非常に実用的だ。KubeVirtが独自にスケジューラ、ボリュームアタッチロジック、ネットワークアロケーターを作り直す必要がないからだ。
1. スケジューリングを作り直す必要がない
VMも結局どのノードに配置されなければならない。Kubernetesはリソース要求、affinity、taint、topology spread、priorityに基づいてPodを適切に配置する。KubeVirtはこの機能を再利用する。
2. ネットワークを新しく作る必要がない
デフォルトモデルでは、まずvirt-launcher PodがCNIを通じてネットワークを受け取る。その後KubeVirtがそのPodのネットワークnamespace内部でbridge、masquerade、TAPなどの追加ワイヤリングを行い、ゲストNICを接続する。
3. ストレージを新しく作る必要がない
PVC、DataVolume、container disk、secret、config mapなどのリソースはすべてPodボリュームモデルを通じてvirt-launcherに渡される。KubeVirtはその上でこれらをディスクイメージやブロックデバイスとしてゲストに接続する。
Pod内部で実際に何が動いているのか
コアPodはvirt-launcher Podだ。このPodは「VM一台につき一つ」という感覚で理解すればよい。このPod内で重要なプロセスは以下の通り。
virt-launcher- libvirtdまたはvirtqemud系の制御コンポーネント
- QEMU
- 場合によってはsidecarやhookコンテナ
ユーザーは通常「VMが実行されている」と言うが、ノードの観点では実際にはQEMUプロセスがPodのcgroupとnamespace内で実行されている。これがKubeVirtがVMをPod上で実行できる最も実質的な理由だ。
docs/components.mdはvirt-launcher Podの目的を「VMIプロセスのためのcgroupとnamespaceを提供すること」と説明している。この表現は非常に重要だ。ここでのPodは単なるデプロイ単位ではなくVM実行境界だ。
KubernetesとKubeVirtの境界
KubeVirtを理解する際に最も混乱しやすい部分は「誰が何を担当するのか」だ。
Kubernetesが引き続き担当する部分
- Podスケジューリング
- ボリュームマウント準備
- Podネットワークアタッチ
- コンテナライフサイクル
- ノード状態反映
KubeVirtが追加で担当する部分
- VM関連API種類の提供
- VM specをlauncher Pod specに変換
- ノードでのVMプロセスライフサイクル調整
- ゲスト用ネットワークバインディングとDHCP補助
- ライブマイグレーションオーケストレーション
libvirtとQEMUが担当する部分
- ドメインXML解釈
- 仮想ハードウェアモデル構成
- CPU、メモリ、ディスク、NIC仮想化
- ライブマイグレーションデータ転送
この分離がうまくできているため、KubeVirtはKubernetesをフォークしたりkubeletを大規模に修正したりせずにVMワークロードを追加できる。
ソースコードでこの構造が現れる場所
このシリーズ全般で繰り返し見ることになる核心パッケージは以下の通りだ。
staging/src/kubevirt.io/api/core/v1
pkg/virt-controller/watch
pkg/virt-handler
pkg/virt-launcher/virtwrap
pkg/network
各レイヤーを一行でまとめると:
staging/src/kubevirt.io/api/core/v1:VM関連CRDスキーマpkg/virt-controller/watch:クラスタワイドのreconcileロジックpkg/virt-handler:ノード別VMエージェントpkg/virt-launcher/virtwrap:libvirt、QEMU制御pkg/network:PodネットワークをゲストNICに接続するコード
「Pod上でVM」が成立する核心メカニズム
さて質問に戻ろう。一体どうやってVM機能をPodで実現できたのか?
核心は3つだ。
第一に、Podは元々プロセス分離境界である
Podはネットワークnamespace、マウントnamespace、PID namespace、cgroupなどの境界を提供する。QEMUは結局Linuxプロセスなので、この境界内で実行できる。
第二に、/dev/kvmのようなホスト機能をPodに公開できる
ハードウェア仮想化の高速化にはKVMデバイスアクセスが必要だ。KubeVirtは適切なデバイスと権限をvirt-launcher側に接続してゲスト実行パフォーマンスを確保する。
第三に、Kubernetesリソースモデルと仮想化モデルの間にtranslation layerを作る
ユーザーはVMI specにCPU、メモリ、ディスク、NICを宣言する。KubeVirtはこれをPod spec、libvirtドメインspec、ゲスト可視デバイス構成へと順次変換する。つまりKubeVirtの本質はtranslation engineだ。
よくある誤解
誤解1:VMがコンテナ内で動くのだからコンテナと同じだ
違う。実行境界はPodを再利用するが、ゲストOSはQEMUが提供する仮想ハードウェア上で動作する。プロセスモデルとゲストOSモデルは異なる。
誤解2:KubeVirtがネットワークとストレージをすべて自前で実装している
違う。設計哲学上、KubernetesとCNI、ボリュームシステムを最大限再利用する。KubeVirtはその上にVM向けのワイヤリングを追加する。
誤解3:kubeletがVMライフサイクルを理解している
直接理解していない。kubeletはvirt-launcher Podを管理する。VMライフサイクルの詳細な状態はvirt-handlerとvirt-launcherが追加で調整する。
運用者がすぐに使えるデバッグチェックポイント
- VMIが作成されたのにPodがなければ、
virt-controller側のreconcileを確認する。 - Podは起動しているがVMがブートしなければ、
virt-handlerとvirt-launcherの通信を確認する。 - ゲストネットワークがおかしければ、Pod NIC、bridge、TAP、DHCPの順に確認する。
- マイグレーション問題が発生したら、controllerステージとlibvirtマイグレーションステージが分離されていることを覚えておく。
まとめ
KubeVirtが「Pod上でVM」を実現できた理由は、Kubernetesを変更したからではなく、Kubernetesの強みをそのまま活用し、VMに必要なtranslation layerを追加したからだ。Podは実行サンドボックスとなり、virt-controllerはオーケストレーションを担い、virt-handlerはノード別の実行を調整し、virt-launcher内のlibvirtとQEMUが実際のVMを作成する。
次の記事では、この構造を構成するオブジェクトモデル、すなわちVirtualMachine、VirtualMachineInstance、VirtualMachineInstanceMigrationがそれぞれ何を表現するのか、ソーススキーマ基準で整理する。