LabHub

ブログ

ユーザーリクエストがVMIに変わる瞬間:virt-apiとadmissionフロー

한국어English日本語

はじめに

KubeVirtを「VMのためのKubernetes拡張」と呼ぶとき、その拡張の最も目に見える表面はAPIだ。ユーザーはkubectlやクライアントを通じてVM、VMI、migrationリクエストを送る。しかしこのリクエストはそのままノードに降りていかない。中間には検証、デフォルト値適用、subresource分岐、権限確認、作業オブジェクト作成がある。

このレイヤーを担当する代表コンポーネントがvirt-apiだ。docs/components.mdvirt-api-serverを仮想化フローのエントリーポイントとして説明する。

virt-apiは何をするのか

簡単に言えばvirt-apiは以下の役割を担う。

ここで重要な点はvirt-apiがVMを直接起動しないということだ。virt-apiユーザーの意図をKubernetesオブジェクト操作に翻訳することに集中する。

なぜ別のAPIレイヤーが必要なのか

Kubernetes CRDだけで十分に見えるかもしれない。しかし実際の運用には以下の要求がある。

このような理由でKubeVirtはCRD定義だけで終わらず、別のAPI処理レイヤーを置く。

ソースから見えるvirt-apiの性格

pkg/virt-api/rest/lifecycle.goを見ると、start、stop、migrate、reboot、backupなど様々なリクエストhandlerが実装されている。ここでのパターンはかなり一貫している。

  1. 対象VMまたはVMIを照会する。
  2. 現在の状態がアクションを許可するか検証する。
  3. 直接実行せず、ステータスパッチや作業オブジェクト作成を行う。
  4. 以降の実際の実行はcontrollerまたはvirt-handlerレイヤーに委譲する。

つまりvirt-apiはオーケストレーションの開始点であり、最終実行点ではない。

migrateリクエストは実際にどう変わるのか

最良の例はmigrate handlerだ。MigrateVMRequestHandlerを見ると内部フローは非常に明瞭だ。

1. VMとVMIを照会する

まずVMが存在するか、そのVMのVMIが存在するかを確認する。

2. 現在のVMIがRunning状態か検証する

実行中でないVMIはmigrationできないのでconflictを返す。

3. migration CRを作成する

核心はここだ。handlerは直接migrationを開始しない。代わりにVirtualMachineInstanceMigrationオブジェクトを作成する。

apiVersion: kubevirt.io/v1
kind: VirtualMachineInstanceMigration
metadata:
  generateName: kubevirt-migrate-vm-
spec:
  vmiName: demo-vm

実際のコードではAddedNodeSelectorなどのオプションも付くことがある。この瞬間からmigrationはAPIリクエストではなくcontrollerが消費するdeclarative work itemになる。

なぜこの方式が重要なのか

この構造の利点は大きい。

1. 作業がKubernetesオブジェクトとして残る

migrationが別のCRになるので監査追跡と状態確認が容易だ。

2. controllerが非同期的に処理できる

APIサーバーは作業を即座に終えずacceptedを返せる。

3. ポリシーと検証がさらに分離される

APIはリクエストの有効性を見て、実際の容量とポリシー判断はcontrollerが継続的に行う。

startやstopがなぜmigrationと違って見えるのか

すべてのアクションがmigration CR作成に翻訳されるわけではない。例えばstopリクエストは場合によってはVM statusパッチやstate change requestとして表現される。lifecycle.gopatchVMStatusStoppedgetChangeRequestJsonがこのパターンをよく示している。

つまりKubeVirtはアクションごとに最も自然なKubernetes表現を選択する。

subresourceが必要な理由

一般的なspec updateだけでは「今migrationを開始して」「今soft rebootして」「consoleに接続して」のようなリクエストを自然に表現しにくい。そのためKubeVirtはsubresourceを置く。

この時重要な設計ポイントは以下の通り。

これはKubernetes本体がscale、exec、log、evictionなどのsubresourceを置く理由と似ている。

virt-apivirt-handlerの違い

混乱しやすいポイントなので分離して見よう。

virt-api

virt-handler

どちらも「VMを制御する」と感じられるかもしれないが、virt-apiはcontrol planeのエントリーであり、virt-handlerはnode execution plane側に近い。

実践で見るべきAPIレイヤーの症状

migrateリクエストがすぐに失敗する

APIレイヤーconflictの可能性が大きい。代表的に:

リクエストはacceptedされたが何も起こらない

この場合はAPIは通過したがcontroller段階で容量、ポリシー、pending podの問題で止まっている可能性がある。

あるリクエストがパッチで別のリクエストがCR作成なのか混乱する

核心は「このリクエストが状態遷移なのか、独立した作業なのか」を基準に見ればよい。

運用者が覚えるべきデバッグ順序

  1. ユーザーが送ったリクエストがどのsubresourceに入るか見る。
  2. virt-apiがconflictを出したか見る。
  3. accepted以降ならどのオブジェクトが追加で作成されたか見る。
  4. その後にcontrollerやnode agentに降りていく。

この順序を逆に見ると問題を見逃しやすい。

まとめ

virt-apiの核心的な役割はVMを直接実行することではなく、ユーザーリクエストを検証可能なKubernetes操作に変えることだ。migrateリクエストはmigration CR作成に、stopリクエストは状態変更パッチに、一部のランタイムアクションはvirt-handler接続に翻訳される。このレイヤーを理解すると、KubeVirtがなぜKubernetesらしく動作するのか、また要求と実行がなぜ時間的に分離されるのかが鮮明に見える。

次の記事では、このリクエストとオブジェクトを実際のlauncher Pod作成に接続するvirt-controllerのinformer、queue、reconcile構造を見ていく。

コメント

まだコメントはありません。

ログインするとコメントできます