ライブマイグレーションが必要な場面 — VMをシャットダウンするだけでは済まない理由
VMをシャットダウンせずに移行しなければならない場面がある。物理ホストのRAM交換が必要な時、カーネルアップデートのためにリブートが必要な時、あるいは一方のホストのCPU負荷が高すぎて、もう一方のホストがアイドル状態の時などだ。「VMを止めて→移してから→再起動」という手順は一見シンプルに聞こえるが、トラフィックを受けているデータベースサーバーやWebサーバーにとって、たとえ30秒のダウンタイムでも実際には大きな問題になる。
KVMのライブマイグレーションはまさにこのような問題を解決するために生まれた。仕組みはなかなか興味深い:VMが通常通り動き続ける間に、RAMの状態が少しずつ移行先ホストへコピーされていく。最終段階で、元のVMはわずか数ミリ秒だけ停止し、残っているダーティページを同期させる。VMに接続しているユーザーはほとんど気づかない——pingロスは通常1〜2パケット程度だ。
私はProxmox VEで12台のVMとコンテナを管理するホームラボを運用している——本番環境に投入する前に何でもテストできる遊び場だ。ProxmoxにはライブマイグレーションのUIが備わっていてとても便利だが、実際にはその裏側でvirsh migrateを呼び出しているに過ぎない。本記事ではボタンを押すだけでなく、各ステップで何が起きているかを理解するためにraw KVM + libvirtに焦点を当てて解説する。
最初に区別しておくべきライブマイグレーションの種類が2つある:
- シェアドストレージマイグレーション:VMのディスクがホスト間のNFS/Ceph共有ストレージ上にある → RAMの状態のみ転送するため、高速でVMへの影響が少ない。
- ブロックマイグレーション(非共有):RAMとディスクの両方を転送する → かなり遅くなるが、共有ストレージの準備が不要。
環境の準備
ハードウェアとネットワークの要件
設定に入る前に、以下の条件を確認しておく必要がある——1つでも欠けるとマイグレーションはすぐに失敗する:
- 両方のホストにKVM/libvirtがインストールされ、動作していること
- CPUは同じベンダーであること——IntelからIntel、AMDからAMD。Intel/AMD間のマイグレーションは理論上ワークアラウンドがあるが、実際にはほとんど失敗する
- 2つのホスト間のネットワーク疎通が取れていること、特にTCPポート16509(libvirtd)とポート範囲49152〜49215(QEMUマイグレーションチャンネル)
- ブリッジネットワークの名前が両方のホストで同一であること——host1が
br0を使い、host2がvirbr0を使っていると、マイグレーション後にVMはネットワーク接続を失う
# 各ホストのブリッジネットワーク名を確認する
ip link show type bridge
必要なパッケージのインストール
両方のホストで実行する:
# Ubuntu/Debian
sudo apt install -y qemu-kvm libvirt-daemon-system libvirt-clients \
virtinst bridge-utils
# RHEL/CentOS/Rocky Linux
sudo dnf install -y qemu-kvm libvirt virt-install bridge-utils
sudo systemctl enable --now libvirtd
# libvirtdが起動していることを確認する
sudo systemctl status libvirtd
sudo virsh list --all
ライブマイグレーションを動作させるための詳細設定
ステップ1 — libvirtdをリモート接続に対応させる
デフォルトでは、libvirtdはローカルソケットのみでリッスンしている。2つのホスト間でマイグレーションを機能させるには、TCPトランスポートを有効にする必要がある。
両方のホストで/etc/libvirt/libvirtd.confを編集する:
sudo nano /etc/libvirt/libvirtd.conf
以下の行を追加するか、コメントを解除する:
# TCPトランスポートを有効にする
listen_tcp = 1
listen_addr = "0.0.0.0"
tcp_port = "16509"
# 内部テスト用に認証を無効にする
# (本番環境ではsaslまたはTLSを使用する)
auth_tcp = "none"
デーモンに--listenフラグを有効にする:
# Ubuntu/Debian
sudo sed -i 's/#LIBVIRTD_ARGS=""/LIBVIRTD_ARGS="--listen"/' /etc/default/libvirtd
# RHEL/CentOS
sudo sed -i 's/#LIBVIRTD_ARGS="--listen"/LIBVIRTD_ARGS="--listen"/' /etc/sysconfig/libvirtd
sudo systemctl restart libvirtd
# ポートが開いていることを確認する
ss -tlnp | grep 16509
ステップ2 — SSH公開鍵認証の設定
SSH経由のvirsh migrateには、パスワードなしで接続できる環境が必要だ。ソースホストから実行する:
# マイグレーション専用のSSHキーを作成する
ssh-keygen -t ed25519 -f ~/.ssh/id_migration -N ""
# 宛先ホスト(192.168.1.102)にコピーする
ssh-copy-id -i ~/.ssh/id_migration.pub [email protected]
# 接続をテストし、宛先ホストのVMを確認する
ssh -i ~/.ssh/id_migration [email protected] "virsh list --all"
ステップ3 — NFSシェアドストレージのセットアップ
NFSシェアドストレージはこのセットアップ全体の基盤となる。VMのディスクは、両方のホストで同じパスにマウントされたNFS上に置かなければならない——この点が間違っていると、マイグレーション時にディスクが見つからないというエラーが発生する。
NFSサーバー(第3のホストでも、2つのKVMホストのどちらか一方でも可)でインストールとエクスポートを設定する:
sudo apt install -y nfs-kernel-server
sudo mkdir -p /srv/kvm-shared
# ディレクトリをLANネットワークにエクスポートする
echo "/srv/kvm-shared 192.168.1.0/24(rw,sync,no_root_squash,no_subtree_check)" | \
sudo tee -a /etc/exports
sudo exportfs -a
sudo systemctl restart nfs-kernel-server
両方のKVMホストにマウントする:
sudo apt install -y nfs-common
sudo mkdir -p /var/lib/libvirt/images/shared
# NFSをマウントする(192.168.1.100はNFSサーバーのIPアドレス)
sudo mount -t nfs 192.168.1.100:/srv/kvm-shared /var/lib/libvirt/images/shared
# 再起動後に自動マウントされるよう/etc/fstabに追加する
echo "192.168.1.100:/srv/kvm-shared /var/lib/libvirt/images/shared nfs defaults,_netdev 0 0" | \
sudo tee -a /etc/fstab
最初からNFS共有ストレージ上にディスクを配置したVMを新規作成する:
sudo virt-install \
--name testvm \
--ram 2048 \
--vcpus 2 \
--disk path=/var/lib/libvirt/images/shared/testvm.qcow2,size=20 \
--os-variant ubuntu22.04 \
--network bridge=br0 \
--graphics none \
--location /var/lib/libvirt/images/ubuntu-22.04-live-server-amd64.iso,kernel=casper/vmlinuz,initrd=casper/initrd
virshによるライブマイグレーションの実行
ソースホストをhost1 (192.168.1.101)、宛先をhost2 (192.168.1.102)とする。testvmという名前のVMがhost1上で稼働しているものとする。
SSH経由のマイグレーション
# 基本的な構文
virsh migrate --live testvm qemu+ssh://[email protected]/system
# --verboseを追加して進捗を確認する
virsh migrate --live --verbose testvm qemu+ssh://[email protected]/system
TCPトランスポート経由のマイグレーション(LAN内では高速)
virsh migrate --live --verbose \
testvm \
qemu+tcp://192.168.1.102/system
シェアドストレージがない場合のブロックマイグレーション
--copy-storage-allを使ってディスクも含めて転送する。ギガビットLAN環境では20GBのディスクで約3〜5分かかり、その間VMは動き続けるがI/Oパフォーマンスは低下する:
virsh migrate --live \
--copy-storage-all \
--verbose \
testvm \
qemu+ssh://[email protected]/system
最終ダウンタイムのチューニング
マイグレーションの最終段階で、VMは残りのダーティページを同期するために数ミリ秒停止しなければならない。デフォルトは300msだ——VMのワークロードが重い場合は、タイムアウトで失敗する代わりにマイグレーションを完了させるため、少し増やすとよい:
# 最終段階で最大500msのダウンタイムを許容する
virsh migrate --live \
--migrate-setmaxdowntime 500 \
testvm \
qemu+ssh://[email protected]/system
確認とモニタリング
VMの移行成功を確認する
# host1上 — testvmはここにはもういない
virsh list --all
# host2上 — testvmが稼働中
virsh list --all
# 期待される出力:
# Id Name State
# 1 testvm running
マイグレーション進捗のリアルタイムモニタリング
# マイグレーション中にソースホストでこのコマンドを実行する
watch -n 1 "virsh domjobinfo testvm"
# 出力内容:
# Job type: Unbounded
# Time elapsed: 3421 ms
# Data processed: 768 MiB
# Data remaining: 64 MiB
# Memory processed: 768 MiB
# Memory remaining: 64 MiB
ネットワーク切断がないことを確認する
# VMのIPアドレスを取得する
virsh domifaddr testvm
# マイグレーション実行中に外部マシンから継続的にpingを送る
ping -i 0.2 <testvmのIPアドレス>
# マイグレーションが成功した場合、最大でも1〜2パケットロスのみ発生する
よくあるエラーの対処法
# ソースホストのlibvirtログを確認する
sudo tail -f /var/log/libvirt/libvirtd.log
# 特定のVMのQEMUログ
sudo tail -f /var/log/libvirt/qemu/testvm.log
ホームラボでテストしているときによく遭遇するエラーをいくつか紹介する:
- “Unable to allow access for disk path”:VMのディスクがNFSではなくローカルストレージにある。先にディスクをNFS共有ストレージに移動する必要がある(VMを停止し、ファイルをコピーして、
virsh editでパスを更新してから再起動する)。 - “authentication failed”:
libvirtd.confのauth_tcp = "none"を確認し、デーモンを再起動する。 - マイグレーションがタイムアウトする:VMのI/Oワークロードが重く、マイグレーションがコピーできる速度よりも速くダーティページが生成されている。
--migrate-setmaxdowntimeを増やすか、マイグレーション前にVMの負荷を下げてみる。 - マイグレーション後にVMがネットワーク接続を失う:2つのホスト間でブリッジ名が異なる。ブリッジネットワークの名前を統一するのが根本的な解決策だ。

