演習 - 高可用性アプリケーションをデプロイする

完了

設計の実装を始めて高可用性アプリケーションを展開できる状態になっています。 アプリケーションをテストするには、Fiddler をダウンロードしてインストールします。

Azure Storage にデータが保持されているプライマリ リージョンへの接続が失敗した場合、アプリケーションはセカンダリ ロケーションに自動的にフェールオーバーして、そこのストレージ アカウントを使用する必要があることを思い出してください。 サーキット ブレーカーによって、アプリケーションのこのような動作が強制的に行われます。 プライマリ ロケーションがオンラインに戻ると、アプリケーションはサーキット ブレーカーによってプライマリ リージョンに再ルーティングされます。

あなたは、医療アプリケーションの本格的な開発を始める前に、サンプル アプリケーションとダミー データを使用してこの方法をテストしようと考えます。

この演習では、RA-GRS ストレージ アカウントでサーキット ブレーカー パターンを使用する方法がわかるアプリケーションを実行します。 アプリケーションは、問題が検出されるとセカンダリ ストレージ アカウントに切り替わり、プライマリ ロケーションが再度使用可能になるとフェールバックします。 アプリケーションは、BLOB ストレージにファイルをアップロードした後、ループして、同じファイルを繰り返しダウンロードします。 プライマリ ロケーションからのストレージ アカウントの読み取りでエラーが発生した場合、アプリケーションは操作を再試行します。 繰り返し試しても再試行が失敗した場合、アプリケーションはセカンダリの場所のストレージ アカウントに切り替わります。 アプリケーションは、読み取り回数が指定されたしきい値を超えるまで、セカンダリ ロケーションからデータを読み取ります。 その後、アプリケーションはプライマリ ロケーションへの切り替えを試みますが、プライマリ ロケーションがまだ使用できない場合はセカンダリ ロケーションに戻ります。

Diagram of a scenario for configuring failover.

Fiddler とは

Fiddler は、アプリケーション (特に Web アプリケーション) のデバッグを支援するために使用されるサードパーティ製のツールです。 コンピューター間のネットワーク トラフィックがキャプチャされ、その結果に基づいて、イベントベースのスクリプト サブシステムを使用して接続が停止されます。

このユニットでは、Fiddler Classic を使用して、医療アプリケーションのストレージ アカウントへの接続を監視します。 コンサルタントがストレージから BLOB をダウンロードできなくなったことがアプリケーションで検出されると、セカンダリ ストレージ アカウントへのフェールオーバーが開始されます。 プライマリ接続が再び使用できるようになったことが検出されると、接続はプライマリ ロケーションにリダイレクトされます。 Fiddler では、さまざまなストレージ アカウントのエンドポイントに送信されるトラフィックを確認できます。

Fiddler Classic をまだインストールしていない場合は、Telerik Fiddler のホーム ページからダウンロードしてインストールします。

Note

Fiddler は、アプリケーションの新しいバージョンをリリースしました。 この演習では Fiddler Classic を使用しますが、これは現在 Windows でのみ使用できます。

Visual Studio をインストールしてサンプル コードをダウンロードする

アプリケーション コードは、デスクトップのローカル環境で実行されます。 アプリケーションのビルドには、Visual Studio が必要です。

  1. まだ Visual Studio 2019 をインストールしていない場合は、Visual Studio 2019 のホーム ページから無料バージョンをダウンロードできます。

  2. Git を使用してサンプル コードをダウンロードします。 Git のコマンド プロンプト ウィンドウを開き、次のコマンドを実行して、サーキット ブレーカー サンプル アプリケーションをお使いのコンピューターにダウンロードします。 <folder> は、ハード ドライブ上の便利な場所に置き換えます。

    git clone https://github.com/MicrosoftDocs/mslearn-ha-application-storage-with-grs <folder>
    

    Note

    このサンプル リポジトリでは .NET 3.1 を使います。 .NET 6.0 など、より新しいフレームワークのバージョンを使う必要がある場合は、circuitbreaker.csproj ファイルを開きます。 <PropertyGroup> の下にあるターゲット フレームワークの行を <TargetFramework>netcoreapp6.0</TargetFramework> に変更します。

Fiddler を構成する

  1. Fiddler を起動します。

  2. [Tools](ツール) メニューの [Options](オプション) を選択します。

  3. [Options](オプション) ウィンドウで、[HTTPS] タブを選択します。

  4. [HTTPS] タブで、[Decrypt HTTPS traffic](HTTPS トラフィックの暗号化解除) を選択します。 Fiddler でさらなる証明書のインストールを求めるメッセージが表示されたら、対応した後、Fiddler を閉じて再起動します。

    Screenshot of the Fiddler HTTPS configuration tab in the Options dialog box.

サンプル アプリケーションを調べる

  1. ブラウザーで Cloud Shell ウィンドウに切り替えます。

  2. 前の演習で作成したストレージ アカウントの接続文字列を取得するには、次のコマンドを実行します。

        az storage account show-connection-string \
            --name $STORAGEACCT \
            --resource-group <rgn>[Sandbox resource group]</rgn>
    
  3. 出力された接続文字列をクリップボードにコピーします。

  4. Visual Studio を起動し、GitHub からダウンロードした CircuitBreaker.sln ソリューションを開きます。 このアプリケーションでは、レプリケートされた Azure ストレージ アカウントへの接続を管理するためのサーキット ブレーカー パターンが実装されます。 サーキット ブレーカーでは、プライマリ ロケーションへの接続を使用できるかどうかが検出されます。 使用できない場合、サーキット ブレーカーでは、セカンダリ アカウントに短時間切り替えてから、プライマリ ロケーションが再試行されます。

  5. ソリューション エクスプローラー ウィンドウで、Program.cs ファイルをダブルクリックします。 このファイルには、アプリケーションの C# ソース コードが含まれています。

  6. Program クラスで、次のステートメントを見つけます。

        static string storageConnectionString = "<Add your storage connection string here>";
    

    <Add your storage connection string here> は、クリップボードの接続文字列に置き換えます。

  7. RunCircuitBreakerAsync メソッドの先頭まで下にスクロールします。

        /// <summary>
        /// Main method. Sets up the objects needed, then performs a loop
        ///   to perform a blob operation repeatedly, responding to the Retry and Response Received events.
        /// </summary>
        private static async Task RunCircuitBreakerAsync()
    
  8. このメソッドで、次のコード ブロックを見つけます。

        // Define a reference to the actual blob.
        CloudBlockBlob blockBlob = null;
    
        // Upload a BlockBlob to the newly created container.
        blockBlob = container.GetBlockBlobReference(ImageToUpload);
        await blockBlob.UploadFromFileAsync(ImageToUpload);
    

    このコードでは、サンプル データ (画像ファイル) がストレージ アカウントの BLOB にアップロードされます。

  9. これらのステートメントに続くコード ブロックを調べます。

        // Set the location mode to secondary so you can check just the secondary data center.
        BlobRequestOptions options = new BlobRequestOptions();
        options.LocationMode = LocationMode.SecondaryOnly;
    
        // Before proceeding, wait until the blob has been replicated to the secondary data center.
        // Loop and check for the presence of the blob once per second
        //   until it hits 60 seconds or it finds it.
        int counter = 0;
        while (counter < 60)
        {
            counter++;
    
            Console.WriteLine("Attempt {0} to see if the blob has replicated to secondary yet.", counter);
    
            if (await blockBlob.ExistsAsync(options, null))
            {
                    break;
            }
    
            // Wait a second, then loop around and try again.
            // When it's finished replicating to the secondary, continue on.
            await Task.Delay(1000);
        }
        if (counter >= 60)
        {
            throw new Exception("Unable to find the image on the secondary endpoint.");
        }
    

    このコードでは、データがセカンダリ ロケーションにレプリケートされたことが確認されます。 60 秒後に BLOB がこの場所に作成されない場合、コードは例外でタイムアウトします。

  10. 次のステートメントを調べます。

        // Set the starting LocationMode to PrimaryThenSecondary. 
        // Note that the default is PrimaryOnly. 
        // You must have RA-GRS enabled to use this.
        blobClient.DefaultRequestOptions.LocationMode = LocationMode.PrimaryThenSecondary;
    

    このステートメントでは、アプリケーションが最初にプライマリ ストレージの場所からの読み取りを試した後、プライマリ ロケーションが使用できない場合はセカンダリで試みるように指定されます。

  11. 下にスクロールして次のコード ブロックに移動します。

    for (int i = 0; i < 1000; i++)
    {
            if (blobClient.DefaultRequestOptions.LocationMode == LocationMode.SecondaryOnly)
            {
                    Console.Write("S{0} ", i.ToString());
            } else
            {
                    Console.Write("P{0} ", i.ToString());
            }
            ...
    }
    

    このコードでは、BLOB ストレージからのデータのダウンロードが 1000 回繰り返されます。 最初の if...else ステートメントでは、ダウンロード試行の繰り返し回数 (0 から開始) と、BLOB のダウンロードにプライマリまたはセカンダリどちらのストレージの場所が使われたかを示すプレフィックス ("P" または "S") が表示されます。

  12. 次のブロックを調べます。 このコードのロジックに注目するため、いくつかのステートメントは省略されています。

        // Set up an operation context for the downloading the blob.
        OperationContext operationContext = new OperationContext();
    
        try
        {
            // Hook up the event handlers for the Retry event and the Request Completed event.
            // These events are used to trigger the change from primary to secondary and back.
            operationContext.Retrying += OperationContextRetrying;
            operationContext.RequestCompleted += OperationContextRequestCompleted;
    
            // Download the file.
            Task task = blockBlob.DownloadToFileAsync(string.Format("./CopyOf{0}", ImageToUpload), FileMode.Create, null, null, operationContext);
            ...
            await task;
            ...
        }
        catch (Exception ex)
        {
            // If you get a gateway error here, check to ensure that your storage account redundancy is set to RA-GRS.
            Console.WriteLine(ex.ToString());
        }
        finally
        {
            // Unhook the event handlers so everything can be garbage collected properly.
            operationContext.Retrying -= OperationContextRetrying;
            operationContext.RequestCompleted -= OperationContextRequestCompleted;
        }
    }
    

    このブロックでは、サーキット ブレーカー パターンの部分が実装されます。 OperationContext オブジェクトには、失敗した要求を再試行するために使用できるイベントが用意されています。 Retrying イベントは、要求が失敗し、再試行されているときに発生します。 RequestCompleted イベントは、要求が完了したとき (成功または失敗のどちらでも) に発生します。 DownloadToFileAsync メソッドでは BLOB データがストレージからダウンロードされ、パラメーターとして OperationContext オブジェクトも受け取ります。 ダウンロードが失敗した場合、この操作によって OperationContextRetrying メソッドが実行されます。 ダウンロードが完了すると、OperationContextRequestCompleted メソッドが実行されます。

  13. OperationContextRetrying メソッドまで下にスクロールします。

    /// Retry Event handler 
    /// If it has retried more times than allowed, and it's not already pointed to the secondary location,
    ///   flip it to the secondary location and reset the retry count.
    /// If it has retried more times than allowed, and it's already pointed to the secondary location, throw an exception. 
    private static void OperationContextRetrying(object sender, RequestEventArgs e)
    {
        retryCount++;
        Console.WriteLine("Retrying event because of failure reading the primary. RetryCount = " + retryCount);
    
        // Check to see whether we've had more than n retries. In which case, switch to the secondary location.
        if (retryCount >= retryThreshold)
        {
    
            // Check to see whether we can fail over to the secondary location.
            if (blobClient.DefaultRequestOptions.LocationMode != LocationMode.SecondaryOnly)
            {
                blobClient.DefaultRequestOptions.LocationMode = LocationMode.SecondaryOnly;
                retryCount = 0;
            }
            else
            {
                throw new ApplicationException("Both primary and secondary are unreachable. Check your application's network connection. ");
            }
        }
    }
    

    このメソッドでは、要求が連続して失敗した回数が追跡されます。 この値が指定されたしきい値を超えた場合、そのメソッドで BLOB クライアントの LocationMode プロパティが変更されて、セカンダリ ロケーションからデータが強制的にダウンロードされます。

  14. OperationContextRequestCompleted メソッドを見つけます。

    /// RequestCompleted Event handler 
    /// If it's not pointing at the secondary location, let it go through. It either was successful 
    ///   or it failed with a nonretryable event (which we hope is temporary).
    /// If it's pointing at the secondary location, increment the read count. 
    /// If the number of reads has hit the threshold of the number of reads you want to do against the secondary location
    ///   before you switch back to primary location, switch back and reset the secondaryReadCount. 
    private static void OperationContextRequestCompleted(object sender, RequestEventArgs e)
    {
        if (blobClient.DefaultRequestOptions.LocationMode == LocationMode.SecondaryOnly)
        {
            // You're reading the secondary location. Let it read the secondary location [secondaryThreshold] times, 
            //    then switch back to the primary and see whether it's available now.
            secondaryReadCount++;
            if (secondaryReadCount >= secondaryThreshold)
            {
                blobClient.DefaultRequestOptions.LocationMode = LocationMode.PrimaryThenSecondary;
                secondaryReadCount = 0;
            }
        }
    }
    

    ダウンロードが正常に完了した場合は、このメソッドで、使用された場所 (プライマリまたはセカンダリ) が確認されます。 このメソッドでは、セカンダリ ロケーションを使用して実行される連続するダウンロード数が追跡されます。 この値がしきい値を超えると、コードによって強制的にプライマリ ロケーションに切り替えられます。 プライマリ ロケーションが使用できるようになっている場合、残りのダウンロードに対してはそれが使用されます。 プライマリ ロケーションがまだ使用できない場合は、ダウンロード操作によって OperationContext オブジェクトの Retrying イベントが生成され、これにより再びセカンダリ ロケーションに切り替わります。

アプリケーションをテストし、フェールオーバーをトリガーする

  1. [デバッグ] メニューの [デバッグの開始] を選択してアプリケーションを実行します。

    Azure ストレージ アカウントにファイルがアップロードされ、アプリケーションが開始されます。 アプリケーションでは、ファイルがセカンダリ ストレージ アカウントの場所にレプリケートされるまで待機してから、ループしてファイルが繰り返しダウンロードされます。 アプリケーションには、繰り返しの番号を含むメッセージと、ファイルがプライマリ ロケーションからダウンロードされたことを示すプレフィックスが表示されます。 たとえば、最初の繰り返しには "P0"、2 番目の繰り返しには "P1" などと表示されます。

    Screenshot of the output from the sample application, showing the messages displayed as the data is repeatedly downloaded.

  2. アプリが実行されている間に、Fiddler に切り替えます。 Fiddler では、ファイルがストレージ アカウントにアップロードされた後、データが再びダウンロードされたことを示す HTTP トラフィックが表示されます。 左側のウィンドウには、次の図に示すように、ストレージ アカウントに送信された要求の一覧が表示されます。

    Screenshot of the Fiddler application, showing the traffic sent to your Azure storage account by the sample application.

  3. アプリケーション ウィンドウに戻り、任意のキーを押して一時停止します。

  4. Fiddler の [Rules](ルール) メニューで、[Customize Rules](ルールのカスタマイズ) を選択します。

  5. OnBeforeResponse 関数を見つけます。 この関数の既存のステートメントの後に、次のコードを追加します。 <storage account name> は、前の演習で作成したストレージ アカウントの名前に置き換えます。

    if (oSession.hostname == "<storage account name>.blob.core.windows.net") {
        oSession.responseCode = 503;
    }
    

    Fiddler の OnBeforeResponse 関数に追加した JavaScript コードから、プライマリ ストレージ アカウントの場所への要求に対して HTTP 503 (サービス使用不能) エラーが返されます。 このエラーで、ストレージ エンドポイントのアクセス不能をシミュレートします。 サンプル アプリケーションのサーキット ブレーカー コードでは、このエラーが検出されて、セカンダリ ストレージの場所にフェールオーバーされます。 データは、以前に Azure によってプライマリからセカンダリ ストレージの場所にレプリケートされているため、データにアクセスできるはずです。

  6. [ファイル] メニューの [保存] をクリックします。

  7. アプリケーションに戻り、任意のキーを押して実行を続けます。

    Fiddler には、プライマリ ロケーションに対して生成された HTTP 503 エラーが表示されます。 アプリケーション ウィンドウに、"Retrying event because of error reading the primary" (プライマリの読み取り中のエラーによる再試行イベント) というメッセージが表示されます。 再試行が 5 回行われた後、アプリケーションのサーキット ブレーカーによってセカンダリ ロケーションに切り替えられ、代わりにそこからの読み取りが開始されます。 P (プライマリの意味) ではなく S というプレフィックス (セカンダリの意味) の付いたメッセージが表示されます。 サーキット ブレーカーでは、セカンダリ アカウントから読み取りを短時間行ってから、プライマリ ロケーションへの切り替えが試行されます。 この試行は失敗するため、サーキット ブレーカーはまたセカンダリ ロケーションに戻されます。 次の図に示すように、このプロセスは、プライマリ ロケーションが再び使用可能になるまで続行されます。

    Screenshot of the output from the sample application, showing the switch from the primary account to the secondary account.

  8. もう一度任意のキーを押してアプリケーションを一時停止します。

  9. Fiddler で、前に OnBeforeResponse 関数に追加したコードを削除し、スクリプトを保存します。

  10. アプリケーションに戻り、任意のキーを押して実行を続けます。 今度は、アプリケーションがプライマリ ストレージ アカウントの場所に正常に戻されます。

  11. アプリケーションを終了し、Visual Studio を閉じます。

  12. Fiddler の [Tools](ツール) メニューで [Options](オプション) を選択します。

  13. [Options](オプション) ウィンドウで、[HTTPS] タブを選択します。

  14. [HTTPS] タブで、[Actions] (アクション) を選択し、[Reset All Certificates] (すべての証明書のリセット) を選択します。 Fiddler が信頼されたルート リストとルート ストアからその証明書を削除できるようにします。 この操作では、Fiddler により以前にインストールされた HTTPS 検査証明書が削除されます。

  15. [OK] を選択して、Fiddler を閉じます。

Azure ストレージ アカウントにアップロードされたデータが、異なるリージョン間でレプリケートされていることを確認しました。 アプリケーションによって、サーキット ブレーカー パターンを使用して接続エラーがどのように処理され、プライマリからセカンダリのストレージ アカウントの場所に切り替えられるかを見ました。 アプリケーションは、接続が再び使用可能になったら、プライマリ ロケーションに戻ることができます。