ライブマイグレーションの深淵:Pre-copyと「終わらないメモリ同期」に挑むSREの視点
こんにちは。クラウドインフラの深淵を覗き続けて幾星霜、今日も今日とてパケットの海を泳ぐSREです。
皆さんが何気なく使っているAWSやGCPの「インスタンスのメンテナンス通知」。あるいはKubernetesのノード退避(Drain)。その裏側では、OSやアプリを停止させることなく、物理サーバーから別の物理サーバーへ丸ごとメモリを引っ越しさせる「ライブマイグレーション」という魔法が動いています。
今回は、その魔法の核心である「Pre-copy Migration(事前コピー方式)」と、現場でエンジニアを苦しめる「反復停止の条件」について、泥臭い実務の視点から紐解いていきましょう。
—
Pre-copy Migration:終わりのない「追いかけっこ」
ライブマイグレーションの基本戦術は、「稼働させながら、メモリを少しずつコピーし続ける」ことです。これをPre-copyと呼びます。
1. Iterative Phase(反復フェーズ): ソース側のメモリ全体をターゲットへコピーします。しかし、コピー中もゲストOSは動いているため、コピーが終わる頃には既に別のメモリ領域が書き換えられています(これを「ダーティページ」と呼びます)。
2. Next Iteration: 溜まったダーティページだけを再度コピーします。
3. Convergence(収束): ダーティページの転送速度が、ゲストの書き込み速度を上回ったとき、ようやく移行の「Goサイン」が出ます。
ここで問題になるのが、「いくらコピーしてもダーティページが減らない」という膠着状態です。高負荷なDBサーバーや、激しくメモリを触るWeb APIのコンテナをマイグレーションしようとすると、このフェーズでいつまでも終わらない「終わりのない追いかけっこ」が始まります。
—
停止の閾値:なぜライブマイグレーションは「止まる」のか
ハイパーバイザー(KVM/QEMUなど)は、ある一定の条件を満たしたとき、ようやく「よし、ゲストを数ミリ秒だけ止めて、残りを一気に送ろう」と決断します。この判定ロジックが非常に重要です。
反復停止(Stop-and-copy)のトリガー
主に以下のパラメーターが鍵を握ります。
downtime-limit: 許容する停止時間(ミリ秒)。これを超えるとサービス断とみなされるため、この時間内に転送が終わるかどうかが勝負です。dirty-rate-limit: ゲストOSがメモリを汚す速度を強制的に制限する機能。あまりに転送が収束しない場合、QEMUはCPUサイクルを奪って書き込み速度を落とさせます。
もし皆さんがAPIのレスポンスタイム監視で「一瞬のスパイク」を感じたら、それは裏でハイパーバイザーが「もう我慢できん!」と、強制的にゲストを停止させてメモリの最後の一撃を転送している証拠かもしれません。
—
実践:QEMU/KVMでのチューニング例
現場で「ライブマイグレーションが全然終わらない!」と泣き言を言いたくないなら、QEMUの設定を事前に把握しておく必要があります。以下は、管理用CLI(virsh)での設定確認とチューニング例です。
# 現在のマイグレーション設定を確認する
virsh qemu-monitor-command <ドメイン名> --hmp "info migrate_parameters"
# 停止時間の上限を500msに設定(デフォルトは通常300ms程度)
# ネットワーク帯域が細い場合は少し広げると成功率が上がることもある
virsh qemu-monitor-command <ドメイン名> --hmp "migrate_set_parameter downtime-limit 500"
# 書き込み制限(Auto-converge)を有効化して、収束を強制する
virsh qemu-monitor-command <ドメイン名> --hmp "migrate_set_capability auto-converge on"
—
API設計への教訓:インフラの都合に「強い」設計を
インフラエンジニアの視点から言わせてもらうと、開発者が書くアプリケーションのメモリ使用パターンは、そのままマイグレーションの難易度に直結します。
例えば、巨大な配列を頻繁に書き換えるような処理は、ライブマイグレーションの天敵です。もし、皆さんのAPIがそのような処理を行うなら、以下の対策を検討してください。
1. メモリフットプリントを抑える: 不要な巨大オブジェクトをメモリに常駐させない。
2. ヘルスチェックの猶予を持たせる: ライブマイグレーション中の数ミリ〜数秒の停止を許容できるよう、クライアント側(Fetch API等)でリトライ戦略を組み込む。
// Fetch APIでのリトライ戦略例(マイグレーション中の瞬間的な切断を想定)
async function fetchWithRetry(url, options, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url, options);
if (response.ok) return response;
} catch (err) {
// ネットワーク切断なら少し待ってからリトライ
if (i === retries - 1) throw err;
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
}
}
}
—
まとめ:トラブルシューティングの心得
ライブマイグレーションが「いつまで経っても終わらない」時、多くの現場では「ネットワークの帯域不足」を疑いますが、本質は「メモリのダーティ率」にあることが多いです。
virshやqemu-monitorを叩いて、今の転送速度とダーティ率をグラフ化してください。- Auto-converge が効いているか確認してください。
- どうしても収束しない場合は、ピークタイムを避けるか、メモリ負荷の高いプロセスを一時的に停止する勇気を持ってください。
インフラは魔法ではありません。すべては物理的な制約と、それを回避するための計算されたアルゴリズムの積み重ねです。この仕組みを理解していれば、クラウドの裏側で何が起きているか、パケットの挙動が手に取るように分かるはずです。
皆さんのインフラが、今日も安定して稼働し続けることを願っています。それでは、また。
コメント