LabHub

ブログ

Live Migration 3:migration proxy、ポート、TLS、ソケットはなぜ必要か

한국어English日本語

はじめに

ライブマイグレーションを考えると「ソースQEMUがターゲットQEMUにメモリをコピーする」で終わりがちである。しかし実際のKubernetes環境ではその間にはるかに複雑なtransportの問題がある。

この問題を解決するためにKubeVirtはpkg/virt-handler/migration-proxy/migration-proxy.goのmigration proxyレイヤーを設けている。

proxyがなぜ必要か

核心的な理由はローカルlibvirt制御ソケットとクラスタネットワーク経路を直接1:1で結びつけにくいからである。

launcher内部ではlibvirtとQEMUがUnixソケット、ローカルファイル、Pod namespaceベースで動作する。しかしマイグレーションにはソースノードとターゲットノード間のネットワーク接続が必要である。そのためKubeVirtは間にproxyを置いて:

する方式でtransportを整理する。

つまりmigration proxyはソースとターゲット間のアドレス翻訳器兼transport shimである。

ソースとターゲットでそれぞれがやること

ProxyManagerインターフェースを見ると役割が明確である。

ターゲット側

つまりターゲットノードは外部から入るTCP接続を受け取るリスナーを開く。

ソース側

つまりソースノードはローカルUnixソケットファイルを作り、それをターゲットアドレスとポートにフォワーディングする。

この構造のおかげでlauncher側からは馴染みのあるローカルエンドポイントに見えるが、実際のtransportはネットワークを経由してターゲットまで到達する。

ポートはどう使われるか

コードはデフォルトのマイグレーションポートを次のように定義している。

GetMigrationPortsListはブロックマイグレーションの有無に応じて必要なポートセットを決定する。つまりディスクを追加で移す必要があればポート要件が増える。

運用者がここで知るべき核心は、ライブマイグレーションが「見えない内部通信」ではなく、明確なポートとリスナーを持つネットワーク作業だということである。

ターゲットリスナーがランダムポートを使えるのはなぜか

StartTargetListenerはターゲットUnixファイルに対してproxyを作り、TCPバインドポートを0で渡してランダムポートを使用できる。その後GetTargetListenerPortsが実際にバインドされたポートをソース側に通知する。

この方式の利点はポート衝突を減らし、PodとノードのダイナミックE環境でより柔軟にリスナーを開けることである。

つまり固定論理ポートと実際のバインドポートを分離した構造である。

ソケットファイルがなぜ必要か

ソース側のSourceUnixFileを見ると、migration proxyはベースディレクトリの下にソースソケットファイルを作る。理由はlauncher内部のlibvirtが依然としてローカルUnixエンドポイントを基準に動作するフローとうまく合うからである。

これはmigration proxyがネットワークのために既存のローカルランタイムモデルを捨てるのではなく、Unixソケットモデルをネットワーク可能な形にラッピングしていることを意味する。

TLSはどこに付くか

migration-proxy.goはサーバーTLS config、クライアントTLS config、マイグレーションTLS configを別々に管理する。またクラスタマイグレーション設定にDisableTLSオプションがあればターゲットリスナーで追加TLSレイヤーをオフにできる。

しかしコードとAPI説明の両方が共通して示唆するのは明確である。DisableTLSは通常悪い考えである。

なぜならマイグレーショントラフィックはゲストメモリ状態と実行状態を含むため、ネットワーク経路の保護が重要だからである。

つまりmigration proxyは単なるフォワーダーではなくセキュリティ境界でもある。

マイグレーションネットワークをなぜ別にできるか

KubeVirt設定にはマイグレーショントラフィックをデフォルトPodネットワークではなく別のネットワーク経由にするオプションがある。これはマイグレーショントラフィックがアプリケーションPodネットワークと競合したり、帯域幅とセキュリティの面で分離が必要な場合があるからである。

この設定がある理由をproxy観点で見ると理解しやすい。proxyはどのみちソースとターゲット間のtransport shimなので、そのtransportをどのネットワークに送るか変更しやすい構造になっている。

ブロックマイグレーションとダイレクトマイグレーションがなぜ異なるか

ダイレクトマイグレーションは主にメモリと実行状態中心だが、ブロックマイグレーションはディスク関連データパスも含む可能性がある。そのためポートもより多く必要で、全体の帯域幅とタイムアウト計算もより敏感になる。

proxyレイヤーがこの二つを区別してポートマップを管理する理由がここにある。

運用者がproxyを見るべき瞬間

次のような状況ではmigration proxyをまず疑うべきである。

このときは単にlibvirtエラーだけ見るのでは不十分で、リスナーが開いているか、ソースソケットが作られたか、ターゲットポートマッピングが正しいかを一緒に見る必要がある。

よくある誤解

誤解1:ソースとターゲットのQEMUがそのまま直接接続する

Kubernetes環境では中間のtransport shimが必要である。その役割をmigration proxyが担う。

誤解2:マイグレーションポートは常に固定

論理ポートは決まっていても実際のバインドポートとマッピングは流動的な場合がある。

誤解3:TLSは選択事項なのでオフにしても大きな問題はない

マイグレーション状態はセンシティブである。通常は追加TLSレイヤーを維持するのが正しい。

まとめ

KubeVirtのmigration proxyはライブマイグレーションtransportをKubernetes環境に合わせて調整する核心レイヤーである。ターゲット側はTCPリスナーを開き、ソース側はUnixソケットを提供し、その間をTLSとポートマッピングで接続する。この構造のおかげでlauncher内部のローカルlibvirtモデルとクラスタ間ネットワーク移動モデルがきれいにつながる。

次の記事では、マイグレーションと直接つながるリソースの問題、すなわちCPU、メモリ、NUMA、hugepagesがPodスケジューリングとゲストハードウェアモデルでどう解釈されるかを見ていく。

コメント

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

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