メインコンテンツにスキップ
ブログライノードLinodeでのライブマイグレーション

Linode でのライブマイグレーション

Linode ライブマイグレーション

開発者がクラウド・コンピューティング・プラットフォームにワークロードをデプロイするとき、サービスが動作する基礎となるハードウェアについてじっくり考えることはあまりない。理想化された「クラウド」のイメージでは、ハードウェアのメンテナンスや物理的な制限は目に見えない。しかし残念ながら、ハードウェアは時折メンテナンスを必要とし、ダウンタイムの原因となることがある。このようなダウンタイムを回避し、クラウドの約束を果たすために、Linodeではライブマイグレーションというツールを導入しています。

Live Migrationsは、Linodeインスタンスがサービスを中断することなく、物理マシン間を移動することを可能にする技術です。LinodeがLive Migrationsで移動されるとき、そのLinodeのプロセスからは移行が見えません。ホストのハードウェアのメンテナンスが必要な場合、Live Migrationsを使用して、そのホストのすべてのLinodeを新しいホストにシームレスに移行することができます。この移行が終了した後、物理的なハードウェアを修理することができ、ダウンタイムがお客様に影響を与えることはありません。

私にとって、この技術の開発は決定的な瞬間であり、クラウド技術と非クラウド技術の分岐点でもありました。私はこの技術に1年以上を費やしたので、Live Migrationsの技術には思い入れがあります。今、そのストーリーを皆さんと共有することができます。

ライブマイグレーションの仕組み

LinodeのLive Migrationsは、多くの新しいプロジェクトと同様に、多くの研究、一連のプロトタイプ、そして多くの同僚や管理者からの支援によって始まりました。最初の前進は、QEMUがLive Migrationsをどのように処理するかを調査することでした。QEMUはLinodeで使われている仮想化技術で、Live MigrationsはQEMUの機能の1つです。そのため、私たちのチームの焦点は、この技術をLinodeに導入することであり、発明することではありませんでした。 

では、QEMUが実装しているLive Migration技術はどのように機能するのでしょうか。その答えは、4つのステップからなる。

  1. デスティネーションqemuインスタンスは、ソースqemuインスタンスに存在するものと全く同じパラメータでスピンアップされます。
  2. ディスクはLive Migrateで転送されます。この転送が実行されている間、ディスクに対するあらゆる変更も同様に伝達されます。
  3. RAMはLive Migrateされています。RAMページへのあらゆる変更は、同様に伝達されなければなりません。このフェーズでディスクデータに変更があった場合、その変更も移行先のQEMUインスタンスのディスクにコピーされます。
  4. カットオーバポイントが実行されます。QEMUが、確実にカットオーバーできるRAMページが十分に少ないと判断すると、ソースおよびデスティネーションQEMUインスタンスが一時停止されます。QEMUは、RAMの最後の数ページとマシン状態をコピーオーバーします。マシン状態には、CPUキャッシュと次のCPU命令が含まれます。その後、QEMUはデスティネーションに開始を指示し、デスティネーションはソースの中断したところからすぐに再開します。

これらの手順は、QEMUによるLive Migrationを実行する方法を高いレベルで説明しています。しかし、移行先のQEMUインスタンスをどのように起動させたいかを正確に指定することは、非常に手作業が多いプロセスです。また、プロセス内の各アクションは、適切なタイミングで開始する必要があります。

Linodeでのライブマイグレーションの実装方法

QEMUの開発者がすでに作成したものを見た後、これをLinodeでどのように活用するか?この質問に対する答えが、私たちのチームにとって仕事の大部分を占めるところでした。 

ライブマイグレーションワークフローのステップ1に従い、移行先のQEMUインスタンスをスピンアップして、送られてくるライブマイグレーションを受け入れます。このステップを実装するとき、最初に考えたのは、現在のLinodeの設定プロファイルを移行先のマシンでスピンアップすることでした。これは理論的には簡単ですが、さらに考えてみるともっと複雑なシナリオが見えてきます。特に、設定プロファイルはLinodeがどのように起動したかを教えてくれますが、必ずしも起動後のLinodeの完全な状態を表しているわけではありません。例えば Block StorageデバイスをホットプラグでLinodeに接続することができます。

インストール先のホストでQEMUインスタンスを作成するためには、現在動作しているQEMUインスタンスのプロファイルを取得する必要がありました。この現在稼働中のQEMUインスタンスのプロファイルは、QMPインターフェイスを検査することで取得しました。このインターフェースは、QEMUインスタンスがどのように配置されているかについての情報を与えてくれます。ゲストの視点からインスタンス内部で何が起こっているかについての情報は提供されません。ローカルSSDとブロックストレージの両方について、ディスクがどこに接続されているか、仮想ディスクがどの仮想化PCIスロットに接続されているかを教えてくれます。QMPに問い合わせ、QEMUインスタンスを検査、イントロスペクションした後、このマシンをデスティネーションで再現する方法を正確に記述したプロファイルが構築されます。

宛先のマシンでは、ソース・インスタンスがどのようなものであるかの完全な記述を受け取ることができます。そして、1つの違いを除き、そのインスタンスをここで忠実に再現することができます。その違いとは、宛先のQEMUインスタンスは、QEMUに着信マイグレーションを受け入れるように指示するオプション付きで起動されることです。

この時点で、Live Migrationsのドキュメントから離れ、QEMUがどのようにこれらの偉業を達成しているかを説明することに切り替える必要があります。QEMUのプロセスツリーは、制御プロセスおよび複数のワーカープロセスで構成されています。ワーカープロセスの1つは、QMPコールのリターンやLive Migrationの処理などを担当する。他のプロセスは、ゲストのCPUに1対1でマッピングされます。ゲストの環境は、QEMUのこの側面から分離されており、独自の独立したシステムとして動作します。 

その意味で、私たちが扱っているのは3つのレイヤーです。 

  • レイヤー1は管理層です。
  • レイヤー2は、QEMUプロセスの一部で、これらのアクションをすべて処理しています。
  • レイヤー3は、Linodeのユーザーが実際に接するゲスト層です。

デスティネーションが起動し、受信したマイグレーションを受け入れる準備ができたら、デスティネーションのハードウェアはソースのハードウェアに、ソースがデータの送信を開始すべきことを知らせます。送信元はこの信号を受信すると起動し、我々はソフトウェアでQEMUにディスクのマイグレーションを開始するよう指示します。ソフトウェアが自律的にディスクの進捗を監視し、完了したタイミングを確認します。ディスクが完了すると、ソフトウェアは自動的にRAMマイグレーションに切り替わります。その後、ソフトウェアは再び自律的にRAMの移行を監視し、RAMの移行が完了すると自動的にカットオーバーモードに切り替わります。これらはすべてLinodeの40Gbpsネットワーク上で行われるので、ネットワーク面はかなり高速です。

カットオーバークリティカルセクション

カットオーバーステップは、Live Migrationのクリティカルセクションとも呼ばれ、このステップを理解することがLive Migrationsを理解する上で最も重要な部分となります。 

カットオーバポイントで、QEMUはカットオーバして宛先マシン上で実行を開始する準備ができたと判断しています。移行元のQEMUインスタンスは、双方に一時停止するように指示します。これは、いくつかのことを意味します。

  1. ゲストによって時間が停止します。ゲストが Network Time Protocol(NTP) のような時刻同期サービスを実行している場合、Live Migration が完了すると NTP が自動的に時刻を再同期します。これは、システムクロックが数秒遅れるためです。
  2. ネットワーク要求が停止します。これらのネットワーク要求がSSHや HTTPのようなTCPベースのものであれば、接続性の損失は認識されません。これらのネットワーク要求がライブストリーミングビデオのようなUDPベースのものである場合、いくつかのフレームがドロップされる可能性があります。

時間とネットワークの要求が停止しているため、カットオーバーはできるだけ早く行いたい。しかし、カットオーバーを成功させるために、まず確認しなければならないことがいくつかあります。

  • Live Migrationがエラーなく完了したことを確認します。もしエラーがあった場合は、ロールバックしてソースのLinodeの一時停止を解除し、それ以上進めないようにします。この点は、特に開発中に試行錯誤を重ね、苦労の連続でしたが、最終的に解決することができました。 
  • ネットワークが送信元でオフになり、送信先で起動することを確認する。 
  • 私たちの残りのインフラ 、このLinodeが現在どのような物理的なマシンに存在するのかを正確に知らせます。 

カットオーバーにはタイムリミットがあるため、これらの作業は手早く終わらせたい。これらの点に対応した後、カットオーバーを完了させます。ソースのLinodeは自動的に完了した信号を取得し、デスティネーションに開始を指示します。移行先のLinodeは、前回終了したところからすぐに再開します。ソースとデスティネーションに残っているアイテムは、すべてクリーンアップされます。将来、宛先のLinodeを再びLive Migrateする必要がある場合、このプロセスを繰り返すことができます。

エッジケースの概要

このプロセスのほとんどは簡単に実装できましたが、Live Migrationsの開発はエッジケースによって拡張されました。このプロジェクトを完了できたのは、完成したツールのビジョンを見て、タスクを完了するためのリソースを割り当てた経営陣と、プロジェクトを最後までやり遂げた従業員の功績が大きいです。

ここでは、エッジケースが発生した箇所を紹介します。

  • Linodeのカスタマーサポートとハードウェア運用チームのために、Live Migrationsをオーケストレーションするための内部ツールを構築する必要がありました。これは、当時私たちが持っていて使っていた他の既存のツールに似ていましたが、構築するために大規模な開発作業が必要なほど異なっていました。
    • このツールは、データセンター内のハードウェア全体を自動的に調べ、どのホストが各Live Migrated Linodeの移行先になるべきかを判断する必要があります。この選択を行う際に関連する仕様は、利用可能なSSDストレージスペースとRAMの割り当てを含みます。
    • 接続先のマシンの物理プロセッサは、接続先のLinodeと互換性がある必要があります。特に、CPUはユーザーのソフトウェアが利用できる機能(CPUフラグとも呼ばれる)を持っていることがあります。例えば、そのような機能の1つはaesで、ハードウェアアクセラレーションによる暗号化を提供します。Live Migrationの移行先のCPUは、移行元のマシンのCPUフラグをサポートする必要があります。これは非常に複雑なエッジケースであることが判明し、次のセクションでこの問題の解決策を説明します。
  • ライブマイグレーション中のエンドユーザーの介入やネットワークの損失などの障害ケースを潔く処理する。これらの障害ケースは、この投稿の後のセクションでより詳細に列挙されています。
  • Linodeプラットフォームの変更に対応すること、これは継続的なプロセスです。現在、そして将来的にLinodeでサポートするすべての機能について、その機能がLive Migrationsと互換性があるかどうかを確認する必要があります。この課題については、この記事の最後に記載しています。

CPUフラグ

QEMUには、ゲストOSにCPUを提示する方法として、さまざまなオプションがあります。そのオプションの1つは、ホストCPUのモデル番号と機能(CPUフラグとも呼ばれる)をゲストに直接渡すことです。このオプションを選択することで、ゲストはKVM 仮想化システムが可能にする制約のないパワーをフルに利用することができるのです。 KVM が Linode で最初に採用されたとき (Live Migrations に先行した)、このオプションはパフォーマンスを最大化するために選択されました。しかし、この決定は、後にLive Migrationsの開発中に多くの困難をもたらしました。

Live Migrations のテスト環境では、移行元と移行先のホストは 2 台の同一マシンでした。現実の世界では、私たちのハードウェア群は100%同じではありませんし、マシンによって異なるCPUフラグが存在することがあります。プログラムがLinodeのオペレーティングシステム内にロードされるとき、LinodeはそのプログラムにCPUフラグを提示し、プログラムはそれらのフラグを利用するために、ソフトウェアの特定のセクションをメモリにロードするので、これは重要です。もしLinodeがこれらのCPUフラグをサポートしない宛先マシンにLive Migrateされると、プログラムはクラッシュします。これはゲストオペレーティングシステムのクラッシュにつながり、Linodeがリブートされる可能性があります。 

マシンのCPUフラグがゲストにどのように表示されるかに影響を与える3つの要因があることがわかりました。

  • CPUの購入時期によって、細かい違いがあります。年末に購入したCPUは、CPUメーカーが新しいハードウェアをリリースする時期によって、年初に購入したものと異なるフラグを持つことがあります。Linodeは容量を増やすために常に新しいハードウェアを購入しており、2つの異なるハードウェアの注文のCPUモデルが同じであっても、CPUフラグが異なる場合があります。
  • 異なるLinuxカーネルは、QEMUに異なるフラグを渡すことがあります。特に、ライブ マイグレーションのソース マシンのLinuxカーネルは、デスティネーション マシンのLinuxカーネルと異なるフラグをQEMUに渡す可能性があります。ソースマシンのLinuxカーネルを更新するには再起動が必要なため、Live Migrationを実行する前にカーネルをアップグレードしても、この不一致を解決することはできず、そのマシン上のLinodeのダウンタイムにつながるからです。
  • 同様に、QEMUのバージョンが異なると、どのCPUフラグが表示されるかに影響することがあります。また、QEMUをアップデートするには、マシンの再起動が必要です。

そこで、Live Migrationsは、CPUフラグの不一致によるプログラムクラッシュを防ぐ方法で実装する必要がありました。選択肢は2つあります。

  • QEMUにCPUフラグをエミュレートするように指示することができます。この場合、以前は高速に動作していたソフトウェアが低速になり、その理由を調査することができなくなります。
  • 送信元でCPUフラグのリストを収集し、送信先が同じフラグを持っていることを確認してから処理を進めることができます。これはより複雑ですが、ユーザーのプログラムの速度を維持することができます。これは、Live Migrationsに実装されたオプションです。

送信元と送信先のCPUフラグを一致させることを決めた後、2つの異なる方法からなるベルト&サスペンダー方式でこのタスクを達成しました。

  • 最初の方法は、2つの方法のうち、より単純なものです。すべてのCPUフラグは、ソースから宛先ハードウェアに送信されます。移行先のハードウェアが新しいqemuインスタンスをセットアップするとき、少なくとも移行元のLinodeにあったすべてのフラグがあるかどうか確認します。それらが一致しない場合、Live Migrationは続行されません。
  • 2つ目の方法はもっと複雑ですが、CPUフラグの不一致に起因するマイグレーションの失敗を防ぐことができます。Live Migrationを開始する前に、CPUフラグの互換性があるハードウェアのリストを作成します。そして、このリストの中から移行先のマシンを選びます。

この2番目の方法は、迅速に実行する必要があり、多くの複雑さを伴います。場合によっては900台以上のマシンで最大226個のCPUフラグをチェックする必要があるのです。これら226のCPUフラグチェックをすべて記述するのは非常に困難であり、メンテナンスも必要です。この問題は、最終的にLinodeの創設者であるChris Akerが提案した素晴らしいアイデアによって解決されました。 

キーとなるアイデアは、すべてのCPUフラグのリストを作り、それを2進数の文字列として表現することでした。そして、ビット演算と演算で文字列を比較することができる。このアルゴリズムを実証するために、まず次のような簡単な例から始めます。ビット演算のandを使って2つの数値を比較する、このPython のコードを考えてみましょう。

>>> 1 & 1
1
>>> 2 & 3
2
>>> 1 & 3
1

なぜビット演算がこのような結果になるのかを理解するためには、数字を2進数で表現することが有効です。2進数で表現された2と3について、ビット演算と演算を調べてみましょう。

>>> # 2: 00000010
>>> # &
>>> # 3: 00000011
>>> # =
>>> # 2: 00000010

ビット演算は、2つの異なる数値の2進数(ビット)を比較するものです。上の数字の一番右の桁から始めて、左へ進みます。

  • 2と3の右端/第1ビットはそれぞれ0と1である。のビット単位と結果は 0 & 1 は0である。
  • 2と3の右から2番目のビットは、どちらの数字も1です。のビット単位と結果は 1 & 1 は1である。
  • これらの数値の他のビットはすべて0であり、0と0をビット演算した結果は0となる。

完全な結果に対する2進表現は次のようになります。 00000010であり、これは2に等しい。

Live Migrationsでは、CPUフラグの全リストはバイナリ文字列で表現され、各ビットは1つのフラグを表します。ビットが 0 の場合、そのフラグは存在せず、ビットが 1 の場合、そのフラグは存在する。例えば、あるビットがaesフラグに対応し、別のビットがmmxフラグに対応することがある。バイナリ表現におけるこれらのフラグの具体的な位置は、データセンター内のマシンで維持、文書化され、共有されています。 

このリスト表現を維持することは、CPUフラグの存在を仮想的にチェックするif文のセットを維持するよりもはるかに単純で効率的です。たとえば、追跡とチェックが必要な CPU フラグが全部で 7 つあったとします。これらのフラグは、8ビットの数値(将来の拡張用に1ビット残しておく)で保存することができます。文字列の例としては、次のようなものがあります。 00111011ここで、右端のビットはaesが有効であることを示し、右端の2番目のビットはmmxが有効であることを示し、3番目のビットは別のフラグが無効であることを示し、以下同様である。

次のコードに示すように、どのハードウェアがこのフラグの組み合わせをサポートし、1サイクルですべてのマッチを返すかを確認することができます。もし、これらのマッチングを計算するために一連の if ステートメントを使用した場合、この結果を得るためにもっと多くのサイクルを要するでしょう。例えば、4つのCPUフラグがソースマシンに存在するLive Migrationの場合、一致するハードウェアを見つけるために203,400サイクルを要します。

ライブマイグレーションコードは、移行元と移行先のマシンのCPUフラグ文字列に対してビット単位のアンドオペレーションを実行する。その結果が移行元マシンのCPUフラグ文字列と等しい場合、移行先マシンは互換性があることになります。このPython のコードスニペットを考えてみましょう。

>>> # The b'' syntax below represents a binary string
>>>
>>> # The s variable stores the example CPU flag 
>>> # string for the source:
>>> s = b'00111011'
>>> # The source CPU flag string is equivalent to the number 59:
>>> int(s.decode(), 2)
59
>>> 
>>> # The d variable stores the example CPU flag 
>>> # string for the source:
>>> d = b'00111111'
>>> # The destination CPU flag string is equivalent to the number 63:
>>> int(d.decode(), 2)
63
>>>
>>> # The bitwise and operation compares these two numbers:
>>> int(s.decode(), 2) & int(d.decode(), 2) == int(s.decode(), 2)
True
>>> # The previous statement was equivalent to 59 & 63 == 59.
>>>
>>> # Because source & destination == source, 
>>> # the machines are compatible

上記のコードでは、デスティネーションはソースより多くのフラグをサポートしていることに注意してください。送信元のCPUフラグがすべて送信先に存在するため、マシンは互換性があるとみなされます。これは、ビット単位の演算によって保証されていることです。

このアルゴリズムの結果は、社内ツールで互換性のあるハードウェアのリストを作成するために使用されます。このリストは、カスタマー・サポートとハードウェア・オペレーション・チームに表示されます。これらのチームは、このツールを使って、さまざまなオペレーションを実行することができます。

  • このツールは、与えられたLinodeに最適な互換性のあるハードウェアを選択するために使用することができます。
  • 移行先を指定せずに、LinodeのLive Migrationを開始することができます。同じデータセンター内にある互換性のある最適なハードウェアが自動的に選択され、移行が開始されます。
  • ホスト上のすべてのLinodeに対して、単一のタスクとしてLive Migrationsを開始することができます。この機能は、ホストのメンテナンスを実行する前に使用します。ツールは自動的にすべてのLinodeの移動先を選択し、各LinodeのLive Migrationをオーケストレートします。
  • メンテナンスが必要な複数のマシンのリストを指定すると、ツールは自動的にホスト間のすべてのLinodeのLive Migrationをオーケストレーションしてくれます。

 ソフトウェアを "ただ動く "ようにするためには、多くの開発時間が必要です......。

失敗例

ソフトウェアであまり語られない機能として、障害ケースを優雅に処理することが挙げられます。ソフトウェアは "ただ動く "ことが前提です。多くの開発時間はソフトウェアを "ただ動く "ようにするために費やされますが、Live Migrationsはまさにそのケースに当てはまりました。多くの時間は、このツールが動作しないすべての方法について考え、そのようなケースを優雅に処理することに費やされました。ここでは、そのようなシナリオのいくつかと、それに対する対処法を紹介します。

  • お客様がLinodeの機能にアクセスしたい場合、どのようになりますか? Cloud Manager?例えば、ユーザーがLinodeをリブートしたり、Block Storage ボリュームをアタッチしたりすることがあります。
    • 回答お客様にお任せしています。Live Migrationが中断され、続行されない。Live Migrationは後で試みることができるので、この解決策は適切です。
  • 接続先のLinodeが起動に失敗した場合はどうなりますか? 
    • 回答送信元のハードウェアに知らせ、データセンター内の別のハードウェアを自動的に選択するよう、内部ツールを設計します。また、運用チームにも通知し、元の移行先のハードウェアを調査できるようにします。これは実運用で起こったことで、私たちのLive Migrationsの実装によって処理されました。
  • マイグレーション途中でネットワークが使えなくなったらどうする?
    • 回答Live Migrationの進捗を自律的に監視し、直前まで進捗がない場合はLive Migrationをキャンセルし、運用チームに知らせます。テスト環境以外では起きていませんが、私たちの実装ではこのシナリオを想定しています。
  • インターネットの残りの部分がシャットダウンしても、送信元と送信先のハードウェアが動作して通信しており、送信元または送信先のLinodeが正常に動作している場合はどうなるのでしょうか。
    • 回答Live Migrationがクリティカルセクションでない場合、Live Migrationを停止してください。その後、再度試行してください。
    • クリティカルなセクションにいる場合は、Live Migrationを続行してください。これは、移行元のLinodeが一時停止しており、移行先のLinodeが動作を再開するために起動する必要があるため、重要です。
    • これらのシナリオをテスト環境でモデル化し、所定の動作が最適であることを確認しました。

変化への対応

何十万ものLive Migrationsを成功させた後、時々聞かれるのが "Live Migrationsはいつ終了するのか?"という質問です。Live Migrationsは時間とともに用途が拡大し、絶えず改良されていく技術なので、プロジェクトの終わりを示すのは必ずしも簡単ではありません。この質問に答える一つの方法は、このプロジェクトの作業の大部分がいつ完了するのかを考えることです。その答えは、「信頼性の高い、頼りになるソフトウェアにとって、仕事は長い間終わらない」です。

Linodeに新しい機能が開発されると、その機能に対するLive Migrationsの互換性を確保するための作業が必要になります。いくつかの機能を導入する際には、Live Migrationsに関する新たな開発作業は必要なく、Live Migrationsが依然として期待通りに動作することをテストするだけでよいのです。その他の機能については、新機能の開発初期にLive Migrationsの互換性確保作業がタスクとしてマークされます。

ソフトウェアの世界では何でもそうですが、研究によってより良い実装方法が常に発見されます。例えば、Live Migrationsの統合をよりモジュール化することで、長期的にメンテナンスが少なくなる可能性があります。また、Live Migrationsの機能を低レベルのコードにブレンドすることで、将来のLinodeの機能としてすぐに使えるようになる可能性があります。私たちのチームは、これらのオプションのすべてを考慮し、Linodeプラットフォームを駆動するツールは、進化し続ける生きた存在です。

コメント 

コメントを残す

あなたのメールアドレスは公開されません。必須項目には*印がついています。