Azure Storage などのクラウドベースのインフラストラクチャは、データとアプリケーションをホストするための高可用性で耐久性の高いプラットフォームを提供します。 クラウドベースのアプリケーションの開発者は、このプラットフォームを活用してユーザーにとってこれらの利点を最大限に活用する方法を慎重に検討する必要があります。 Azure Storage には、リージョンの停止中でも高可用性を確保するための geo 冗長オプションが用意されています。 geo 冗長レプリケーション用に構成されたストレージ アカウントは、プライマリ リージョンで同期的にレプリケートされた後、数百マイル離れたセカンダリ リージョンに非同期的にレプリケートされます。
Azure Storage には、geo 冗長レプリケーションのためのオプションがあります。geo 冗長ストレージ (GRS) と geo ゾーン冗長ストレージ (GZRS) の 2 つです。 Azure Storage の geo 冗長オプションを使用するには、ストレージ アカウントが読み取りアクセス geo 冗長ストレージ (RA-GRS) または読み取りアクセス geo ゾーン冗長ストレージ (RA-GZRS) 用に構成されていることを確認します。 そうでない場合は、 ストレージ アカウントのレプリケーションの種類を変更する方法の詳細を確認できます。
この記事では、プライマリ リージョンで大幅な停止が発生した場合でも、容量は限られていますが、引き続き機能するアプリケーションを設計する方法について説明します。 プライマリ リージョンが使用できなくなった場合、アプリケーションはシームレスに切り替えて、プライマリ リージョンが再び応答するまで、セカンダリ リージョンに対して読み取り操作を実行できます。
アプリケーションの設計に関する考慮事項
プライマリ リージョンからの読み取りに干渉する問題が発生した場合にセカンダリ リージョンから読み取ることで、一時的な障害や重大な障害を処理するようにアプリケーションを設計できます。 プライマリ リージョンが再び使用可能になると、アプリケーションはプライマリ リージョンからの読み取りに戻ることができます。
RA-GRS または RA-GZRS を使用して可用性と回復性を確保するためにアプリケーションを設計する場合は、次の重要な考慮事項に注意してください。
プライマリ リージョンに格納するデータの読み取り専用コピーは、セカンダリ リージョンに非同期的にレプリケートされます。 この非同期レプリケーションは、セカンダリ リージョンの読み取り専用コピーが最終的にプライマリ リージョンのデータと 一致 することを意味します。 ストレージ サービスは、セカンダリ リージョンの場所を決定します。
Azure Storage クライアント ライブラリを使用して、プライマリ リージョン エンドポイントに対して読み取りと更新の要求を実行できます。 プライマリ リージョンが使用できない場合は、読み取り要求をセカンダリ リージョンに自動的にリダイレクトできます。 必要に応じて、プライマリ リージョンが使用可能な場合でも、読み取り要求をセカンダリ リージョンに直接送信するようにアプリを構成することもできます。
プライマリ リージョンが使用できなくなった場合は、アカウントのフェールオーバーを開始できます。 セカンダリ リージョンにフェールオーバーすると、プライマリ リージョンを指す DNS エントリがセカンダリ リージョンを指すよう変更されます。 フェールオーバーが完了すると、GRS アカウントと RA-GRS アカウントの書き込みアクセスが復元されます。 詳細については、「ディザスター リカバリーとストレージ アカウントのフェールオーバー」を参照してください。
時間とともに一貫性を持つデータを扱う
提案されたソリューションでは、古い可能性のあるデータを呼び出し元のアプリケーションに返すことは許容されることを前提としています。 セカンダリ リージョン内のデータは最終的に一貫性があるため、セカンダリ リージョンへの更新がレプリケートを完了する前に、プライマリ リージョンにアクセスできなくなる可能性があります。
たとえば、顧客が更新プログラムを正常に送信したが、更新がセカンダリ リージョンに反映される前にプライマリ リージョンが失敗したとします。 顧客がデータの読み取りを要求すると、更新されたデータではなく、セカンダリ リージョンから古いデータを受け取ります。 アプリケーションを設計するときは、この動作が許容されるかどうかを決定する必要があります。 その場合は、ユーザーに通知する方法も検討する必要があります。
この記事の後半では、 最終的に一貫性のあるデータを処理 する方法と、プライマリ リージョンとセカンダリ リージョンのデータの不一致を評価するために [最終同期時刻 ] プロパティを確認する方法について説明します。
サービスを個別に、またはすべてまとめて処理する
可能性は低いですが、1 つのサービス (BLOB、キュー、テーブル、またはファイル) が使用できなくなりますが、他のサービスはまだ完全に機能しています。 サービスごとに再試行を個別に処理することも、すべてのストレージ サービスの再試行を一般的に処理することもできます。
たとえば、アプリケーションでキューと BLOB を使用する場合は、サービスごとに再試行可能なエラーを処理する別のコードを記述できます。 そうすることで、BLOB サービス エラーは BLOB を処理するアプリケーションの部分にのみ影響し、キューは通常どおりに実行され続けます。 ただし、すべてのストレージ サービスの再試行を一緒に処理することにした場合、いずれかのサービスが再試行可能なエラーを返した場合、BLOB サービスとキュー サービスの両方への要求が影響を受けます。
最終的に、この決定はアプリケーションの複雑さに依存します。 再試行の影響を制限するために、サービス別のエラーを処理することをお選びになる場合があります。 または、プライマリ リージョン内のストレージ サービスの問題を検出したときに、すべてのストレージ サービスの読み取り要求をセカンダリ リージョンにリダイレクトすることもできます。
読み取り専用モードでアプリケーションを実行する
プライマリ リージョンでの停止に効果的に備えるためには、失敗した読み取り要求と失敗した更新要求の両方をアプリケーションで処理できる必要があります。 プライマリ リージョンで障害が発生した場合は、読み取り要求をセカンダリ リージョンにリダイレクトできます。 ただし、セカンダリ リージョン内のレプリケートされたデータは読み取り専用であるため、更新要求をリダイレクトすることはできません。 このため、読み取り専用モードで実行できるようにアプリケーションを設計する必要があります。
たとえば、更新要求が Azure Storage に送信される前にチェックされるフラグを設定できます。 更新要求が完了したら、要求をスキップし、ユーザーに適切な応答を返すことができます。 問題が解決するまで特定の機能を完全に無効にし、機能が一時的に使用できないことをユーザーに通知することもできます。
サービスごとにエラーを個別に処理する場合は、サービスごとに読み取り専用モードでアプリケーションを実行する機能も処理する必要があります。 たとえば、各サービスに対して読み取り専用フラグを設定できます。 その後、必要に応じて、コード内のフラグを有効または無効にすることができます。
読み取り専用モードでアプリケーションを実行できるため、アプリケーションのメジャー アップグレード中に機能が制限されるようにすることもできます。 アプリケーションを読み取り専用モードで実行するようにトリガーし、セカンダリ データ センターをポイントすることで、アップグレードの実行中にプライマリ リージョンのデータに誰もアクセスできないようにすることができます。
読み取り専用モードで実行するときの更新プログラムの処理
読み取り専用モードで実行する場合、更新要求を処理する方法は多数あります。 このセクションでは、考慮すべきいくつかの一般的なパターンについて説明します。
ユーザーに応答し、更新要求が現在処理されていないことを通知できます。 たとえば、連絡先管理システムを使用すると、ユーザーは連絡先情報にアクセスできますが、更新することはできません。
別のリージョンで更新プログラムをエンキューできます。 この場合は、保留中の更新要求を別のリージョンのキューに書き込み、プライマリ データ センターが再びオンラインになったらそれらの要求を処理します。 このシナリオでは、後で処理するために更新要求がキューに登録されていることをユーザーに知らせる必要があります。
更新プログラムは、別のリージョンのストレージ アカウントに書き込むことができます。 プライマリ リージョンがオンラインに戻ったら、データの構造に応じて、それらの更新をプライマリ データにマージできます。 たとえば、名前に日付/時刻スタンプを含む個別のファイルを作成する場合は、それらのファイルをプライマリ リージョンにコピーし直すことができます。 このソリューションは、ログ記録や IoT データなどのワークロードに適用できます。
再試行の処理
クラウドで実行されているサービスと通信するアプリケーションは、計画外のイベントや発生する可能性がある障害に敏感である必要があります。 これらの障害は、接続の一時的な損失から自然災害による重大な停止まで、一時的または永続的である可能性があります。 可用性を最大化し、アプリケーションの全体的な安定性を向上させるために、適切な再試行処理を使用してクラウド アプリケーションを設計することが重要です。
読み取り要求
プライマリ リージョンが使用できなくなった場合は、読み取り要求をセカンダリ ストレージにリダイレクトできます。 前述のように、アプリケーションが古いデータを読み取ることを許容する必要があります。 Azure Storage クライアント ライブラリには、再試行を処理し、読み取り要求をセカンダリ リージョンにリダイレクトするためのオプションが用意されています。
この例では、BLOB ストレージの再試行処理は BlobClientOptions
クラスで構成され、これらの構成オプションを使用して作成した BlobServiceClient
オブジェクトに適用されます。 この構成は、プライマリ地域での読み取り要求の再試行がセカンダリ地域にリダイレクトされる「プライマリ・セカンダリ方式」です。 この方法は、プライマリ リージョンの障害が一時的であると予想される場合に最適です。
string accountName = "<YOURSTORAGEACCOUNTNAME>";
Uri primaryAccountUri = new Uri($"https://{accountName}.blob.core.windows.net/");
Uri secondaryAccountUri = new Uri($"https://{accountName}-secondary.blob.core.windows.net/");
// Provide the client configuration options for connecting to Azure Blob storage
BlobClientOptions blobClientOptions = new BlobClientOptions()
{
Retry = {
// The delay between retry attempts for a fixed approach or the delay
// on which to base calculations for a backoff-based approach
Delay = TimeSpan.FromSeconds(2),
// The maximum number of retry attempts before giving up
MaxRetries = 5,
// The approach to use for calculating retry delays
Mode = RetryMode.Exponential,
// The maximum permissible delay between retry attempts
MaxDelay = TimeSpan.FromSeconds(10)
},
// If the GeoRedundantSecondaryUri property is set, the secondary Uri will be used for
// GET or HEAD requests during retries.
// If the status of the response from the secondary Uri is a 404, then subsequent retries
// for the request will not use the secondary Uri again, as this indicates that the resource
// may not have propagated there yet.
// Otherwise, subsequent retries will alternate back and forth between primary and secondary Uri.
GeoRedundantSecondaryUri = secondaryAccountUri
};
// Create a BlobServiceClient object using the configuration options above
BlobServiceClient blobServiceClient = new BlobServiceClient(primaryAccountUri, new DefaultAzureCredential(), blobClientOptions);
プライマリ リージョンが長期間使用できない可能性が高いと判断した場合は、セカンダリ リージョンを指すすべての読み取り要求を構成できます。 この構成は、 セカンダリのみのアプローチです 。 前に説明したように、この期間中に更新要求を処理する戦略と、読み取り要求のみが処理されていることをユーザーに通知する方法が必要になります。 この例では、セカンダリ リージョン エンドポイントを使用する BlobServiceClient
の新しいインスタンスを作成します。
string accountName = "<YOURSTORAGEACCOUNTNAME>";
Uri primaryAccountUri = new Uri($"https://{accountName}.blob.core.windows.net/");
Uri secondaryAccountUri = new Uri($"https://{accountName}-secondary.blob.core.windows.net/");
// Create a BlobServiceClient object pointed at the secondary Uri
// Use blobServiceClientSecondary only when issuing read requests, as secondary storage is read-only
BlobServiceClient blobServiceClientSecondary = new BlobServiceClient(secondaryAccountUri, new DefaultAzureCredential(), blobClientOptions);
読み取り専用モードと セカンダリのみの 要求に切り替えるタイミングを知ることは、 サーキット ブレーカー パターンと呼ばれるアーキテクチャ 設計パターンの一部です。これについては、後のセクションで説明します。
更新要求
更新要求をセカンダリ ストレージ (読み取り専用) にリダイレクトすることはできません。 前に説明したように、プライマリ リージョンが使用できない場合は、アプリケーションが 更新要求を処理 できる必要があります。
サーキット ブレーカー パターンは、更新要求にも適用できます。 更新要求エラーを処理するために、10 件の連続するエラーなどのしきい値をコードに設定し、プライマリ リージョンへの要求の失敗数を追跡できます。 しきい値に達したら、アプリケーションを読み取り専用モードに切り替えて、プライマリ リージョンへの更新要求が発行されないようにすることができます。
サーキットブレーカーパターンの実装方法
復旧に可変の時間がかかる可能性があるエラーの処理は、 サーキット ブレーカー パターンと呼ばれるアーキテクチャ 設計パターンの一部です。 このパターンを適切に実装すると、失敗する可能性がある操作をアプリケーションが繰り返し実行することを防ぎ、アプリケーションの安定性と回復性が向上します。
サーキット ブレーカー パターンの 1 つの側面は、プライマリ エンドポイントに継続的な問題がある場合を特定することです。 この決定を行うために、クライアントが再試行可能なエラーを検出する頻度を監視できます。 各シナリオは異なるため、セカンダリ エンドポイントに切り替えて読み取り専用モードでアプリケーションを実行する決定に使用する適切なしきい値を決定する必要があります。
たとえば、プライマリ リージョンで連続して 10 個の障害が発生した場合に、スイッチを実行できます。 これは、コード内のエラーの数を保持することで追跡できます。 しきい値に達する前に成功した場合は、カウントをゼロに戻します。 カウントがしきい値に達した場合は、読み取り要求にセカンダリ リージョンを使用するようにアプリケーションを切り替えます。
別の方法として、アプリケーションにカスタム監視コンポーネントを実装することもできます。 このコンポーネントは、単純な読み取り要求 (小さな BLOB の読み取りなど) を使用してプライマリ ストレージ エンドポイントに継続的に ping を実行して、その正常性を判断できます。 この方法では、リソースの一部を占有しますが、かなりの量は必要ありません。 設定したしきい値に達するような問題が見つかったら、セカンダリのみの読み取り要求と読み取り専用モードに切り替えます。 このシナリオでは、プライマリ ストレージ エンドポイントへの ping が再度成功したときに、プライマリ リージョンに戻り、更新の許可を続けることができます。
切り替えを行うタイミングを決定するために使用されるエラーしきい値は、アプリケーション内のサービスごとに異なる可能性があるため、パラメーターを構成可能にすることを検討する必要があります。
もう 1 つの考慮事項は、アプリケーションの複数のインスタンスを処理する方法と、各インスタンスで再試行可能なエラーを検出した場合の対処方法です。 たとえば、同じアプリケーションが読み込まれた状態で 20 個の VM が実行されている場合があります。 各インスタンスを個別に処理しますか? 1 つのインスタンスで問題が発生し始める場合、そのインスタンスだけに応答を制限しますか? または、1 つのインスタンスに問題がある場合に、すべてのインスタンスが同じ方法で応答するようにしますか? インスタンスを個別に処理する方が、それらの間で応答を調整するよりもはるかに簡単ですが、アプローチはアプリケーションのアーキテクチャによって異なります。
最終的に一貫性のあるデータの処理
ジオ冗長ストレージは、プライマリリージョンからセカンダリーリージョンへトランザクションをレプリケートすることで機能します。 レプリケーション プロセスでは、セカンダリ リージョン内のデータの整合性が最終的に保証されます。 つまり、プライマリ リージョン内のすべてのトランザクションは最終的にセカンダリ リージョンに表示されますが、表示されるまでに遅延が発生する可能性があります。 また、トランザクションがプライマリ リージョンで最初に適用されたのと同じ順序でセカンダリ リージョンに到着する保証もありません。 トランザクションがセカンダリ リージョンに順不同で到着した場合は、サービスが追いつくまでセカンダリ リージョン内のデータが不整合な状態であると考える 場合があります 。
Azure Table Storage の次の例は、従業員の詳細を更新して 管理者ロールのメンバーにした場合の動作を示しています。 この例では、 従業員 エンティティを更新し、 管理者の合計数で管理者ロール エンティティを更新する必要があります。 セカンダリ リージョンで更新プログラムが順に適用されていないことに注意してください。
時間 | トランザクション | レプリケーション | [最終同期時刻] | 結果 |
---|---|---|---|---|
T0 | トランザクション A: 従業員を追加する プライマリのエンティティ |
トランザクション A がプライマリに挿入されました。 まだレプリケートされていません。 |
||
T1 | トランザクション A 〜にレプリケートされる 二次 |
T1 | トランザクション A がセカンダリにレプリケートされます。 最終同期時刻が更新されました。 |
|
T2 | トランザクション B: 更新 従業員エンティティ プライマリ |
T1 | プライマリに書き込まれたトランザクション B まだレプリケートされていません。 |
|
T3 | トランザクション C: 更新 管理者 役割エンティティ 主要な |
T1 | プライマリにトランザクション C が書き込まれました。 まだレプリケートされていません。 |
|
T4 | トランザクション C 〜にレプリケートされる 二次 |
T1 | セカンダリにレプリケートされたトランザクション C。 LastSyncTime が更新されない理由 トランザクション B はまだレプリケートされていません。 |
|
T5 | エンティティの読み取り セカンダリーから |
T1 | 従業員の古いデータを取得する トランザクション B が完了していないため、エンティティがありません。 まだレプリケートされていません。 の新しい値を取得します。 Cが管理者の役割エンティティを持っているため 複製された。 同期がまだ行われていないため、前回の同期時刻がありません。 トランザクション B により更新されました 複製されていません。 次の情報を伝えることができます。 管理者ロール エンティティに不整合がある エンティティの日付/時刻が以降であるため 最後の同期時刻。 |
|
T6 | トランザクション B 〜にレプリケートされる 二次 |
T6 |
T6 – C 経由のすべてのトランザクションには、次の条件があります。 複製済み、最後の同期時間 更新されています。 |
この例では、クライアントが T5 のセカンダリ リージョンからの読み取りに切り替わるとします。 現時点では 、管理者ロール エンティティを正常に読み取ることができますが、エンティティには、現時点でセカンダリ リージョンの管理者としてマークされている 従業員 エンティティの数と一致しない管理者の数の値が含まれています。 クライアントはこの値を表示する可能性があり、情報に矛盾が生じるリスクがあります。 または、更新プログラムが順不同で発生したために 、管理者ロール が一貫性のない状態にある可能性があることをクライアントが判断し、この事実をユーザーに通知することもできます。
ストレージ アカウントのデータに不整合が生じる可能性があるかどうかを判断するために、クライアントは [最終同期時刻 ] プロパティの値を確認できます。 [最終同期時刻 ] は、セカンダリ リージョンのデータが最後に一貫性があった時刻と、サービスがその時点より前にすべてのトランザクションを適用した時刻を示します。 上の例では、サービスがセカンダリ リージョンに 従業員 エンティティを挿入した後、最後の同期時刻が T1 に設定されます。 T6 に設定されている場合、サービスがセカンダリ リージョンの従業員エンティティを更新するまで、T1 に残ります。 クライアントが T5 でエンティティを読み取るときに最後の同期時刻を取得した場合は、エンティティのタイムスタンプと比較できます。 エンティティのタイムスタンプが前回の同期時刻より後の場合、エンティティは不整合の可能性がある状態になり、適切なアクションを実行できます。 このフィールドを使用するには、プライマリへの最後の更新が完了した日時を把握している必要があります。
最後の同期時刻を確認する方法については、 ストレージ アカウントの最終同期時刻プロパティを確認するを参照してください。
テスティング
再試行可能なエラーが発生したときにアプリケーションが期待どおりに動作することをテストすることが重要です。 たとえば、問題が検出されたときにアプリケーションがセカンダリ リージョンに切り替え、プライマリ リージョンが再び使用可能になったときに切り替わることをテストする必要があります。 この動作を適切にテストするには、再試行可能なエラーをシミュレートし、発生頻度を制御する方法が必要です。
1 つのオプションは、 Fiddler を使用して、スクリプト内の HTTP 応答をインターセプトして変更することです。 このスクリプトでは、プライマリ エンドポイントからの応答を識別し、HTTP 状態コードを、ストレージ クライアント ライブラリが再試行可能なエラーとして認識する応答に変更できます。 このコード スニペットは、 employeedata テーブルに対する読み取り要求への応答をインターセプトして 502 状態を返す Fiddler スクリプトの簡単な例を示しています。
static function OnBeforeResponse(oSession: Session) {
...
if ((oSession.hostname == "\[YOURSTORAGEACCOUNTNAME\].table.core.windows.net")
&& (oSession.PathAndQuery.StartsWith("/employeedata?$filter"))) {
oSession.responseCode = 502;
}
}
この例を拡張して、より広範な要求をインターセプトし、一部の 要求の responseCode のみを変更して、実際のシナリオをより適切にシミュレートすることができます。 Fiddler スクリプトのカスタマイズの詳細については、Fiddler ドキュメントの 要求または応答の変更 を参照してください。
アプリケーションを読み取り専用に切り替えるための構成可能なしきい値を設定している場合は、運用以外のトランザクション ボリュームで動作をテストする方が簡単になります。
次のステップ
プライマリ エンドポイントとセカンダリ エンドポイント間で切り替えを行う方法を示す完全なサンプルについては、「 Azure サンプル - RA-GRS ストレージでのサーキット ブレーカー パターンの使用」を参照してください。