Skip to content
Published on

VMI specがlibvirt domain XMLに変換される過程

Authors

はじめに

ユーザーはKubernetesスタイルのYAMLでVMIを宣言する。しかしlibvirtとQEMUは結局domain specとXMLを基準にゲストを実行する。したがってKubeVirtには2つの世界を繋ぐ非常に重要なtranslation layerが必要だ。この役割を果たすのがpkg/virt-launcher/virtwrap/converterだ。

この記事の核心的な質問は次だ。VMI specはどのような過程を経てlibvirt domain XMLになるのか?

変換は単純なフィールドコピーではない

最初はspec.domain.cpuがlibvirt CPUフィールドに、spec.domain.devices.disksがlibvirtディスク項目にそのままコピーされそうだが、実際にはもっと複雑だ。

理由は3つだ。

  1. ゲストデバイス名を計算しなければならない
  2. Podに準備された実際のディスクパスを見つけなければならない
  3. アーキテクチャ、virtioモデル、セキュリティ、migrationなどのポリシーを反映しなければならない

つまりconverterはserializationレイヤーではなくポリシーを含む変換器だ。

核心進入点

converter.goで最も重要な関数名は以下の通りだ。

  • Convert_v1_VirtualMachineInstance_To_api_Domain
  • Convert_v1_Disk_To_api_Disk
  • Convert_v1_Volume_To_api_Disk
  • Convert_v1_BlockSize_To_api_BlockIO

この名前だけ見ても構造が見える。最終目標はVMI全体をapi.Domainに変えることであり、その中でディスク、ボリューム、ブロックIO、CPU、NICなどの詳細変換関数が階層的に呼び出される。

api.Domainはなぜ重要なのか

KubeVirt内部でapi.Domainはゲスト実行のための正規中間表現に近い。

  • VMIはKubernetes親和的な宣言モデル
  • api.Domainはlibvirt親和的な実行モデル
  • 最終的にXML marshalが可能

つまりapi.DomainはKubeVirt内部の「仮想化中間言語」のような役割を果たす。

ディスク変換で何が起こるのか

Convert_v1_Disk_To_api_Diskを見ると単純なマッピング以上のことが多く見える。

1. bus別デバイス名計算

virtio、scsi、cdrom、lunによってtarget busとデバイス名が変わる。例えばvirtioディスクはvdavdbのようなゲスト可視名が生成される。

2. PCIアドレス検証

一部のbusタイプではPCIアドレスを任意に割り当てることが許可されない。converterはこの制約を適用する。

3. shareable、cache、discardポリシー適用

共有ディスクならcacheがnoneでなければならないなど、単にYAML値をコピーするのではなく組み合わせ制約を検査する。

4. boot orderとalias追加

ゲストブート順序とユーザー定義aliasも変換過程で作られる。

つまりディスク一つだけ見てもconverterは単純なserializerではなくゲストハードウェアモデル設計器だ。

ボリュームとディスクはなぜ分離されているのか

Kubernetes側VMI specではvolumesdisksが分離されている。

  • volumes:データソースが何か
  • disks:ゲストにどのデバイスとして付けるか

例えば同じPVCでも:

  • 通常のディスクとして付けることも
  • cdromのように公開することも
  • boot orderを異なるように設定することも可能

converterはこの2つを結合して「このソースをゲストにどのlibvirtディスクとして付けるか」を計算する。

変換コンテキストが必要な理由

convertertypes.ConverterContext系を見るとconverterが単純なstateless関数ではないことが分かる。変換には周辺文脈が必要だ。

代表的に以下の情報がcontextに入る。

  • アーキテクチャ
  • エミュレーション許可可否
  • ハイパーバイザーデバイス利用可能性
  • ブロックPVCかfilesystem PVCか
  • ディスククリエーター
  • launch securityオプション
  • virtio transitionalモデル使用可否

これは同じVMI specでもノードアーキテクチャと実行環境によって最終domain specが異なりうるという意味だ。

ネットワークとCPUも同じ方式で変換される

ディスクだけが特別ではない。NIC、CPUトポロジー、NUMA、launch security、EFI、iothreadsなどもconverterがlibvirt domain representationに変換する。

例えば:

  • 専用CPUならpinning関連フィールドが変わる
  • launch securityが有効ならIOMMUなどのオプションが入る
  • アーキテクチャが変わればマシンタイプと一部のデバイス表現が変わる

つまりconverterはゲストハードウェア構成全体を担当する。

domain XMLはいつ完成するのか

実務的には以下の順序で理解すればよい。

  1. VMI specを読む。
  2. converterがapi.Domainを作る。
  3. 必要ならhookやsidecarがdomain specを追加修正する。
  4. 最終domain XMLがlibvirtに渡される。
  5. libvirtがこれを基にQEMUを実行する。

したがって「あるゲストデバイスがなぜそう見えるのか」を知りたければ、ほぼ常にconverterから読まなければならない。

migration時になぜdomain XMLを再度扱うのか

live-migration-source.goにはmigratableDomXMLのようなロジックがある。migration時にsource domain XMLをそのまま渡してはいけず、メタデータを整理したりtarget側のパスに合わせてディスクファイルパスを修正しなければならない場合がある。

つまりdomain XMLはラウンチ直前に一度だけ重要なのではなく:

  • 初期ブート時
  • migration時
  • 一部のhotplugとtarget準備時

複数の段階で継続的に核心データ構造として使用される。

なぜこのレイヤーがデバッグの核心なのか

多くの問題はYAMLとQEMUの間のどこかで起きる。その中間がまさにconverterだ。

こんな問題を説明してくれる

  • なぜゲストディスク名が予想と違うのか
  • なぜ特定のオプション組み合わせが拒否されるのか
  • なぜvirtioの代わりに別のモデルが入ったのか
  • なぜmigration時にディスクパスを再度変えるのか

もしlauncher Podは生きていてlibvirt define段階で失敗するなら、converter結果が最初に疑うべき対象だ。

よくある誤解

誤解1:VMI specはほぼlibvirt XMLと同じだ

違う。VMI specはKubernetes宣言モデルであり、libvirt XMLはハイパーバイザー実行モデルだ。両者の間にはKubeVirtが解釈すべきポリシーが多い。

誤解2:ディスクはボリュームパスさえ合えば終わりだ

違う。bus、boot order、discard、cache、reservation、block sizeなどのゲスト可視属性がすべて重要だ。

誤解3:converterはラウンチ時に一度だけ重要だ

違う。migrationとhotplug、target準備過程でもdomain specとXML変形が再び重要になる。

まとめ

KubeVirtのconverterはVMI specをlibvirt domainモデルに翻訳する最も重要な中間層だ。このレイヤーは単純なフィールドコピーではなく、ゲストハードウェアモデル、実行環境、セキュリティオプション、migration制約を合わせて反映する。そのためKubeVirtを深く理解するにはcontrollerの次に必ず読むべきパッケージがpkg/virt-launcher/virtwrap/converterだ。

次の記事では、この変換結果が実際のストレージソースとどのように出会い、PVC、DataVolume、container disk、hotplugディスクがゲストにどのような経路で付くか見ていく。