1. 仮想マシン移行時の悪夢:95%でスタックしクラスタ全体が輻輳
この状況を経験したことのあるシステム管理者は少なくないはずです。本番環境のPostgreSQLクラスタやECサイトのWebアプリケーションが稼働する物理サーバーで、故障したRAMモジュールを交換する必要が生じました。あなたはサービスが停止することなくスムーズに稼働し続けると確信し、自信を持ってNode AからNode BへのLive Migrationをクリックします。
しかし、現実は思い通りにはいきません。マイグレーションの進捗が95%まで進んだところで完全に停止してしまいます。その原因は、アプリケーションがRAMにデータを書き込む速度が、ネットワークカード経由で新しいホストへ転送する速度を上回っていることにあります(非収束現象)。
さらに悪いことに、RAMデータのトラフィックが共有ネットワークポートの帯域幅を使い果たしてしまいます。その結果、内部パケットロスが頻発し、社内スタッフはCRMへの接続を失い、外部ユーザーには即座に504 Gateway Timeoutエラーが返される事態に陥ります。
12台のProxmox VMで構成された私の小規模な検証環境でも、この問題が発生しました。1Gbpsポート経由で150〜200MB/sの書き込み頻度を持つRedisやPostgreSQLのVMをマイグレーションするたびに、クラスタネットワークが激しく輻輳しました。本番環境でシステムの安定性を保つには、この問題を根本から解決する必要があります。
2. 技術的な本質:なぜLive Migrationは失敗するのか?
わかりやすく例えると、Live Migrationは「元の家で家族が次々と新しい買い物を続けている間に、新しい家へ荷物を運び出そうとしている状態」に似ています。
Pre-copyの反復とダーティメモリ(Dirty Memory)
デフォルトでは、KVM/QEMUおよびProxmox VEは以下の3つのステップからなるPre-copyマイグレーションメカニズムを採用しています:
- フェーズ1(初期コピー):VMがリクエストを処理し続けている間に、VMのRAMデータ全体をNode AからNode Bへコピーします。
- フェーズ2(反復フェーズ):コピー中もVMは変更されたメモリ領域(ダーティページ / Dirty Pages)を生成し続けます。KVMはこれらのダーティページを収集してNode Bへ転送します。この処理が複数回繰り返されます。
- フェーズ3(切り替え / Cutover):残りのダーティページが十分に小さくなると、KVMはNode A上のVMを約50〜100ミリ秒一時停止(Pause)し、残りの数MBを転送した上でNode BでVMを起動(Resume)します。
トラブルが発生するのは、RAM書き込み速度(Dirty Rate) > ネットワーク転送速度(Network Bandwidth)となった場合です。KVMは無限ループに陥り、帯域幅が枯渇し、スイッチが過負荷となって、マイグレーション処理がいつまでも完了しなくなります。
ネットワーク競合(Network Contention)
マイグレーションのトラフィックをアプリケーションの通信やCorosync(ハートビート)クラスタ通信と同じネットワークカードで共有している場合、リスクはさらに深刻化します。マイグレーションが物理リンクの帯域を100%占有すると、Corosyncがタイムアウトを起こします。クラスタは該当ノードがダウンしたと判断し、フェンシング機構(サーバーの自動再起動)を発動してしまいます。その結果、ちょっとしたメンテナンス作業が一瞬にしてシステム全体の障害へと発展することになります。
3. よく使われる2つの一時的な解決策(とその限界)
方法1:マイグレーション速度の制限(Rate Limiting)
最も手軽な方法は、共有ネットワークを保護するためにマイグレーションの転送帯域を制限することです:
# KVMでマイグレーション速度を最大50 MB/sに制限
virsh migrate-setspeed <vm_name> 50
デメリット:内部ネットワークの安全性は確保できるものの、マイグレーションにかかる時間が大幅に延びます。RAM書き込み頻度が高いVMの場合、帯域を絞ると確実にマイグレーションが失敗します。
方法2:仮想マシンのCPUサイクル制限(Auto-Converge)
反復回数が減少しないことを検知すると、ハイパーバイザはVMの処理サイクル(vCPUスロットル)を抑制し、RAMへの書き込み量を強制的に減らします。
# virsh経由でKVMのauto-converge機能を有効化
virsh migrate --live --auto-converge <vm_name> qemu+ssh://10.10.10.2/system
デメリット:vCPUが20%から最大80%まで制限されます。内部のアプリケーション(特にデータベース)の応答が極端に遅くなり、エンドユーザーに直接的な悪影響を及ぼします。
4. 標準的な解決策:専用ネットワーク、データ圧縮、Post-copyの組み合わせ
安全かつダウンタイムゼロで、サービスに影響を与えずに移行するという目標を達成するためには、以下の標準アーキテクチャを導入することを推奨します:
ステップ1:専用マイグレーションネットワーク(Dedicated Network)の分離(10Gbps以上を推奨)
ノード間のデータ転送専用として、独立した物理NIC(または2ポートのBonding)を割り当てます。
eth1上に専用サブネット10.10.10.0/24を持つ2つのノードがあると仮定します:
- Node A:
10.10.10.1/24 - Node B:
10.10.10.2/24
Proxmox VEでは、GUIから直接設定するか、/etc/pve/datacenter.cfgファイルを編集します:
# datacenter.cfgファイルにマイグレーション専用ネットワーク範囲を定義
migration: type=secure,network=10.10.10.0/24
純粋なKVM(libvirt)環境では、マイグレーションのエンドポイントIPを明示的に指定します:
# 専用ネットワークのIPを直接指定してLive Migrationを実行
virsh migrate --live --verbose \
--migrateuri tcp://10.10.10.2:49152 \
my-production-vm qemu+ssh://10.10.10.2/system
ステップ2:データストリーム圧縮の有効化(ZSTD圧縮)
ネットワーク転送前にメモリデータを圧縮することで、実際の転送データ量を30%〜60%削減できます。この機能は、空きメモリ領域が多いVMや重複キャッシュを多く持つVMで特に高い効果を発揮します。
Proxmox VEでは、Datacenter -> Options -> Migration Settingsにアクセスし、圧縮タイプをZSTDに変更することで、CPU負荷と圧縮速度の最適なバランスを実現できます。
ステップ3:高負荷VMの切り札としてPost-copyを活用
書き込み頻度が非常に高いデータベース(MySQL、PostgreSQL、Redisなど)でPre-copyが完了しない場合、Post-copyが決定的な解決策となります。
Pre-copyとは異なり、Post-copyは以下のように動作します:
- 基本的なメモリデータの一部をPre-copyで転送します。
- 収束しない場合、ハイパーバイザはNode A上のVMを一時停止し、Node B側で即座にVMをアクティブ化します(切り替えのダウンタイムはわずか数十ミリ秒です)。
- VMはNode B上でそのまま稼働を開始します。まだ転送されていないRAMページへのアクセスが発生した場合、Node Bはネットワーク経由でUserfaultfd(ページフォールト)を発行し、Node Aからそのページを即座に取得します。
- Node Aは残りのRAMデータをバックグラウンドでストリーミング転送し続け、100%完了するまで転送します。
KVM/libvirtでの実行手順:
# 1. post-copyを許可するフラグを付けてLive Migrationを開始
virsh migrate --live --postcopy --verbose my-database-vm qemu+ssh://10.10.10.2/system
# 2. 数十秒経過しても反復が続く場合は、直ちにpost-copyを実行:
virsh migrate-postcopy my-database-vm
10Gbps専用ネットワーク、ZSTD圧縮アルゴリズム、そしてPost-copy技術を組み合わせることで、ネットワークの停止やサービスの中断を心配することなく、大規模な仮想マシンを確実に移行できるようになります。

