Azure Container Apps での ASP.NET Core アプリのデプロイとスケーリング

断続的な高需要を経験する Azure にデプロイされたアプリでは、需要を満たすためにスケーラビリティから利点が得られます。 スケーラブルなアプリでは、スケールアウトしてワークロードのピーク時に容量を確保し、ピークが下がったときに自動的にスケールダウンできるため、コストを削減できます。 水平スケーリング (スケール "アウト") では、VM やデータベース レプリカなど、リソースの新しいインスタンスが追加されます。 この記事では、次のタスクを実行して、水平方向にスケーラブルな ASP.NET Core アプリを Azure Container Apps にデプロイする方法について説明します。

  1. サンプル プロジェクトを設定する
  2. Azure Container Apps にアプリをデプロイする
  3. アプリのスケーリングとトラブルシューティングを行う
  4. Azure サービスを作成する
  5. Azure サービスを接続する
  6. アプリを構成して再デプロイする

この記事では Razor Pages を使用しますが、そのほとんどは他の ASP.NET Core アプリにも適用されます。

場合によっては、基本的な ASP.NET Core アプリは特別な考慮事項なしでスケーリングできます。 しかし、特定のフレームワーク機能またはアーキテクチャ パターンを利用するアプリには、次のような追加の構成が必要です。

  • 安全なフォーム送信: Razor Pages、MVC および Web API アプリは、多くの場合、フォームの送信に依存します。 既定では、これらのアプリではクロスサイト フォージェリ トークンと内部データ保護サービスを使用して、要求をセキュリティで保護します。 クラウドにデプロイした場合、これらのアプリは、安全な一元化された場所でデータ保護サービスに関する問題に対処するように構成する必要があります。

  • SignalR 回線: Blazor Server アプリでは、安全にスケーリングするために一元化された Azure SignalRサービス を使用する必要があります。 これらのサービスでも前述のデータ保護サービスが利用されます。

  • 一元化されたキャッシュまたは状態管理サービス: スケーラブルなアプリでは、Azure Cache for Redis を使用して分散キャッシュを提供する場合があります。 Microsoft Orleans などのフレームワークの状態を格納するために、Azure Storage が必要になる場合があります。これは、さまざまなアプリ インスタンス間で状態を管理するアプリを作成するのに役立つことがあります。

この記事の手順では、スケーラブルなアプリを Azure Container Apps にデプロイすることで、上記の問題に適切に対処する方法を示します。 このチュートリアルの概念のほとんどは、Azure App Service インスタンスをスケーリングするときにも適用されます。

サンプル プロジェクトを設定する

GitHub Explorer サンプル アプリを使用して、このチュートリアルに従います。 次のコマンドを使用して、GitHub からのアプリを複製します。

git clone "https://github.com/dotnet/AspNetCore.Docs.Samples.git"

/tutorials/scalable-razor-apps/start フォルダーに移動し、ScalableRazor.csproj を開きます。

サンプル アプリでは検索フォームを使用して、名前で GitHub リポジトリを参照します。 このフォームは、偽造防止の問題に対処するために組み込みの ASP.NET Core データ保護サービスに依存します。 既定では、アプリが Container Apps で水平方向にスケーリングされると、データ保護サービスによって例外がスローされます。

アプリケーションをテストする

  1. Visual Studio でアプリを起動します。 プロジェクトには Docker ファイルが含まれています。つまり、実行ボタンの横にある矢印を選択して、Docker Desktop セットアップまたは標準の ASP.NET Core ローカル Web サーバーを使用してアプリを起動できます。

検索フォームを使用して、名前で GitHub リポジトリを参照します。

A screenshot showing the GitHub Explorer app.

アプリを Azure Container Apps にデプロイする

Visual Studio は、アプリを Azure Container Apps にデプロイするために使用されます。 コンテナー アプリにより、コンテナー化されたアプリとマイクロサービスのホスティングを簡略化するように設計されたマネージド サービスが提供されます。

注意

アプリ用に作成されたリソースの多くには場所が必要になります。 このアプリでは、場所は重要ではありません。 実際のアプリでは、クライアントに最も近い場所を選択する必要があります。 お近くの場所を選択することができます。

  1. Visual Studio ソリューション エクスプローラーで、最上位レベルのプロジェクト ノードを右クリックし、[発行] を選択します。

  2. 発行ダイアログで、デプロイ ターゲットとして [Azure] を選んでから、[次へ] を選択します。

  3. 特定のターゲットについては、[Azure Container Apps (Linux)] を選んでから、[次へ] を選択します。

  4. デプロイする新しいコンテナー アプリを作成します。 緑色の + アイコンを選択して新しいダイアログを開き、次の値を入力します。

    A screenshot showing Visual Studio deployment.

    • コンテナー アプリ名: 既定値のままにするか、名前を入力します。
    • サブスクリプション名: デプロイするサブスクリプションを選択します。
    • リソース グループ: [新規] を選択し、msdocs-scalable-razor という新しいリソース グループを作成します。
    • コンテナー アプリ環境: [新規] を選択してコンテナー アプリ環境ダイアログを開き、次の値を入力します。
      • 環境名: 既定値のままにします。
      • 場所: 近くの場所を選択します。
      • Azure Log Analytics ワークスペースの: [新規作成] を選択して、Log Analytics ワークスペース ダイアログを開きます。
        • 名前: 既定値のままにします。
        • 場所: 近くの場所を選んでから、[OK] を選択してダイアログを閉じます。
      • [OK] を選択して、コンテナー アプリ環境ダイアログを閉じます。
    • [作成] 選択して、元のコンテナー アプリ ダイアログを閉じます。 Visual Studio によって、コンテナー アプリ リソースが Azure に作成されます。
  5. リソースが作成されたら、それがコンテナー アプリの一覧で選択されていることを確認してから、[次へ] を選択します。

  6. アプリの発行済みイメージ成果物を格納するには、Azure Container Registry を作成する必要があります。 コンテナー レジストリ画面で緑色の + アイコンを選択します。

    A screenshot showing how to create a new container registry.

  7. 既定値のままにして、[作成] を選択します。

    A screenshot showing the values for a new container registry.

  8. コンテナー レジストリが作成された後、それが選択されていることを確認し、[完了] を選択してダイアログ ワークフローを閉じ、発行プロファイルの概要を表示します。

    管理者ユーザーが発行された Docker コンテナーにアクセスできるようにするよう求めるメッセージが Visual Studio に表示された場合は、[はい] 選択します。

  9. 発行プロファイルの概要の右上にある [発行] を選択して、アプリを Azure にデプロイします。

デプロイが完了すると、Visual Studio によってブラウザーが起動され、ホストされているアプリが表示されます。 フォーム フィールドで Microsoft を検索すると、リポジトリの一覧が表示されます。

アプリのスケーリングとトラブルシューティングを行う

現在、アプリは問題なく動作していますが、トラフィック量が多いと予想して、より多くのインスタンスにわたってアプリをスケーリングしたいと考えています。

  1. Azure portal の最上位レベルの検索バーで razorscaling-app-**** コンテナー アプリを検索し、結果からそれを選択します。
  2. 概要ページで、左側のナビゲーションから [スケール] を選び、[+ 編集とデプロイ] を選択します。
  3. リビジョン ページで、[スケール] タブに切り替えます。
  4. 最小と最大の両方のインスタンスを 4 に設定してから、[作成] を選択します。 この構成変更により、アプリが確実に 4 つのインスタンス間で水平方向にスケーリングされるようになります。

アプリに戻ります。 ページが読み込まれると、最初はすべてが正常に動作しているように見えます。 しかし、検索語句を入力して送信すると、エラーが発生する可能性があります。 エラーが表示されない場合は、フォームをさらに数回送信します。

エラーのトラブルシューティング

検索要求が失敗する理由はすぐにはわかりません。 ブラウザー ツールには、400 要求が正しくありませんという応答が返されたことが示されます。 しかし、コンテナー アプリのログ機能を使用して、環境内で発生するエラーを診断できます。

  1. コンテナー アプリの概要ページで、左側のナビゲーションから [ログ] を選択します。

  2. [ログ] ページで、開いたポップアップを閉じ、[テーブル] タブに移動します。

  3. [カスタム ログ] 項目を展開して ContainerAppConsoleLogs_CL ノードを表示します。 このテーブルには、問題のトラブルシューティングのためにクエリを実行できるコンテナー アプリのさまざまなログが含まれています。

    A screenshot showing the container app logs.

  4. クエリ エディターで、基本的なクエリを作成し、次のスクリプトなどの最近の例外について ContainerAppConsoleLogs_CL Logs テーブルを検索します。

    ContainerAppConsoleLogs_CL
    | where Log_s contains "exception"
    | sort by TimeGenerated desc
    | limit 500
    | project ContainerAppName_s, Log_s
    

    上記のクエリでは、exception という単語を含む行について、ContainerAppConsoleLogs_CL テーブルを検索しています。 結果は生成時間で並べ替えられます (500 件の結果に制限)。結果を読みやすくするために、ContainerAppName_sLog_s 列のみが含まれます。

  5. [実行] を選択すると、結果の一覧が表示されます。 ログに目を通します。そのほとんどが偽造防止トークンと暗号化に関連していることに注意してください。

    A screenshot showing the logs query.

    重要

    アプリのエラーは、.NET データ保護サービスが原因で発生します。 アプリの複数のインスタンスが実行されている場合、フォームを送信する HTTP POST 要求が、HTTP GET 要求からページを最初に読み込んだのと同じコンテナーにルーティングされる保証はありません。 要求が異なるインスタンスによって処理された場合、偽造防止トークンは正しく処理されず、例外が発生します。

    前もって、Azure ストレージ サービス内のデータ保護キーを一元化し、Key Vault で保護することで、この問題は解決します。

Azure サービスを作成する

上記のエラーを解決するために、次のサービスが作成され、アプリに接続されます。

  • Azure ストレージ アカウント: Data Protection Services のデータの格納を担当します。 アプリのスケーリングに合わせてキー データを格納するための一元的な場所を提供します。 ストレージ アカウントを使用して、ドキュメント、キュー データ、ファイル共有、およびほぼすべての種類の BLOB データを保持することもできます。
  • Azure KeyVault: このサービスではアプリのシークレットを格納し、Data Protection Services の暗号化に関する問題に対処するのに役立つように使用されます。

ストレージ アカウント サービスを作成する

  1. Azure portal の検索バーに「Storage accounts」と入力し、一致する結果を選択します。
  2. ストレージ アカウントの一覧ページで、[+ 作成] を選択します。
  3. [基本] タブで、次の値を入力します。
    • サブスクリプション: コンテナー アプリに対して選択したのと同じサブスクリプションを選択します。
    • リソースグループ: 前に作成した msdocs-scalable-razor リソース グループを選択します。
    • ストレージ アカウント名: アカウントに scalablerazorstorageXXXX という名前を付けます。この X は任意の乱数です。 この名前は、すべての Azure で一意である必要があります。
    • リージョン: 以前に選択したのと同じリージョンを選択します。
  4. 残りの値は既定値のままにして、[確認] を選択します。 Azure によって入力が検証された後、[作成] を選択します。

Azure によって新しいストレージ アカウントがプロビジョニングされます。 タスクが完了したら、[リソースに移動] を選んで新しいサービスを表示します。

ストレージ コンテナーを作成する

アプリのデータ保護キーを格納するコンテナーを作成します。

  1. 新しいストレージ アカウントの概要ページで、左側のナビゲーションの [ストレージ ブラウザー] を選択します。
  2. [BLOB コンテナー] を選択します。
  3. [+ コンテナーの追加] を選択して、[新しいコンテナー] ポップアップ メニューを開きます。
  4. scalablerazorkeys という名前を入力し、残りの設定は既定値のままにして、[作成] を選択します。

新しいコンテナーがページ一覧に表示されます。

キー コンテナー サービスを作成する

BLOB ストレージ コンテナー内のデータを保護するキーを保持するキー コンテナーを作成します。

  1. Azure portal の検索バーに「Key Vault」と入力し、一致する結果を選択します。
  2. キー コンテナーの一覧ページで、[+ 作成] を選択します。
  3. [基本] タブで、次の値を入力します。
    • サブスクリプション: 以前に選択したのと同じサブスクリプションを選択します。
    • リソース グループ: 以前に作成した msdocs-scalable-razor リソース グループを選択します。
    • Key Vault 名: scalablerazorvaultXXXX という名前を入力します。
    • リージョン: ご自分の場所に近いリージョンを選びます。
  4. 残りの設定は既定値のままにして、[確認と作成] を選択します。 Azure によって設定が検証されるのを待ってから、[作成] を選択します。

Azure によって新しいキー コンテナーがプロビジョニングされます。 タスクが完了したら、[リソースに移動] を選択して新しいサービスを表示します。

キーを作成する

BLOB ストレージ アカウント内のデータを保護する秘密キーを作成します。

  1. キー コンテナーのメインの概要ページで、左側のナビゲーションから [キー] を選択します。
  2. [キーの作成] ページで、[+ 生成/インポート] を選択して、[キーの作成] ポップアップ メニューを開きます。
  3. [名前] フィールドに「razorkey」と入力します。 残りの設定は既定値のままにして、[作成] を選択します。 キーの一覧ページに新しいキーが表示されます。

Azure サービスを接続する

コンテナー アプリでは、データ保護エラーを解決し、正しくスケーリングするために、ストレージ アカウントとキー コンテナー サービスへの安全な接続が必要です。 新しいサービスは、次の手順を使用して相互に接続されます。

重要

サービス コネクタやその他のツールを使用したセキュリティ ロールの割り当ては、通常、反映されるのに 1 分から 2 分かかり、まれに最大 8 分かかる場合があります。

ストレージ アカウントを接続する

  1. Azure portal で、コンテナー アプリの概要ページに移動します。
  2. 左側のナビゲーションで、[サービス コネクタ] を選択します
  3. [サービス コネクタ] ページで、[+ 作成] を選択して [接続の作成] ポップアップ パネルを開き、次の値を入力します。
    • コンテナー: 前に作成したコンテナー アプリを選択します。
    • サービスの種類: [ストレージ - BLOB] を選びます。
    • サブスクリプション: 以前に使用したサブスクリプションを選択します。
    • 接続名: 既定値のままにします。
    • ストレージ アカウント: 前に作成したストレージ アカウントを選択します。
    • クライアントの種類: [.NET] を選択します。
  4. [次へ: 認証] を選択して、次の手順に進みます。
  5. [システム割り当てマネージド ID] を選択し、[次へ: ネットワーク] を選びます。
  6. 既定のネットワーク オプションは選択したままにし、[確認と作成] を選択します。
  7. Azure によって設定が検証された後、[作成] を選択します。

サービス コネクタでは、コンテナー アプリでシステム割り当てマネージド ID を有効にします。 また、ストレージ コンテナーでデータ操作を実行できるように、ストレージ BLOB データ共同作成者のロールをその ID に割り当てます。

キー コンテナーを接続する

  1. Azure portal で、コンテナー アプリの概要ページに移動します。
  2. 左側のナビゲーションで、[サービス コネクタ] を選択します。
  3. [サービス コネクタ] ページで、[+ 作成] を選択して [接続の作成] ポップアップ パネルを開き、次の値を入力します。
    • コンテナー: 前に作成したコンテナー アプリを選択します。
    • サービスの種類: [Key Vault] を選びます。
    • サブスクリプション: 以前に使用したサブスクリプションを選択します。
    • 接続名: 既定値のままにします。
    • キー コンテナー: 前に作成したキー コンテナーを選択します。
    • クライアントの種類: [.NET] を選択します。
  4. [次へ: 認証] を選択して、次の手順に進みます。
  5. [システム割り当てマネージド ID] を選択し、[次へ: ネットワーク] を選びます。
  6. 既定のネットワーク オプションは選択したままにし、[確認と作成] を選択します。
  7. Azure によって設定が検証された後、[作成] を選択します。

サービス コネクタにより、キー コンテナー キーでデータ操作を実行できるように、ID にロールが割り当てられます。

アプリを構成して再デプロイする

必要な Azure リソースが作成されました。 このセクションでは、新しいリソースを使用するようにアプリ コードを構成します。

  1. 次の NuGet パッケージをインストールします。

    • Azure.Identity: Azure ID およびアクセス管理サービスを操作するためのクラスが提供されます。
    • Microsoft.Extensions.Azure: コア Azure 構成を実行するための便利な拡張メソッドが提供されます。
    • Azure.Extensions.AspNetCore.DataProtection.Blobs: ASP.NET Core DataProtection キーを Azure Blob Storage に格納して、Web アプリの複数のインスタンス間でキーを共有できるようにします。
    • Azure.Extensions.AspNetCore.DataProtection.Keys: Azure Key Vault のキーの暗号化/ラップ機能を使用して保存中のキーを保護できます。
    dotnet add package Azure.Identity
    dotnet add package Microsoft.Extensions.Azure
    dotnet add package Azure.Extensions.AspNetCore.DataProtection.Blobs
    dotnet add package Azure.Extensions.AspNetCore.DataProtection.Keys
    
  2. 次の強調表示されているコードを使用して、Program.cs を更新します。

    using Azure.Identity;
    using Microsoft.AspNetCore.DataProtection;
    using Microsoft.Extensions.Azure;
    
    var builder = WebApplication.CreateBuilder(args);
    var BlobStorageUri = builder.Configuration["AzureURIs:BlobStorage"];
    var KeyVaultURI = builder.Configuration["AzureURIs:KeyVault"];
    
    builder.Services.AddRazorPages();
    builder.Services.AddHttpClient();
    builder.Services.AddServerSideBlazor();
    
    builder.Services.AddAzureClientsCore();
    
    builder.Services.AddDataProtection()
                    .PersistKeysToAzureBlobStorage(new Uri(BlobStorageUri),
                                                    new DefaultAzureCredential())
                    .ProtectKeysWithAzureKeyVault(new Uri(KeyVaultURI),
                                                    new DefaultAzureCredential());
    var app = builder.Build();
    
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }
    
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    
    app.UseRouting();
    
    app.UseAuthorization();
    
    app.MapRazorPages();
    
    app.Run();
    

上記の変更により、アプリでは一元化されたスケーラブルなアーキテクチャを使用してデータ保護を管理できるようになります。 DefaultAzureCredential では、アプリが再デプロイされたときに、先ほど有効にしたマネージド ID 構成が検出されます。

appsettings.json ファイルの AzureURIs セクションのプレースホルダーを更新して、次の内容を含めます。

  1. <storage-account-name> プレースホルダーを scalablerazorstorageXXXX ストレージ アカウントの名前に置き換えます。

  2. <container-name> プレースホルダーを scalablerazorkeys ストレージ コンテナーの名前に置き換えます。

  3. <key-vault-name> プレースホルダーを scalablerazorvaultXXXX キー コンテナーの名前に置き換えます。

  4. キー コンテナー URI の <key-name> プレースホルダーを、前に作成した razorkey 名に置き換えます。

    {
      "GitHubURL": "https://api.github.com",
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
        }
      },
      "AllowedHosts": "*",
      "AzureURIs": {
        "BlobStorage": "https://<storage-account-name>.blob.core.windows.net/<container-name>/keys.xml",
        "KeyVault": "https://<key-vault-name>.vault.azure.net/keys/<key-name>/"
      }
    }
    

アプリを再デプロイする

これで、以前に作成した Azure サービスを使用するようにアプリが正しく構成されました。 コード変更を適用するためにアプリを再デプロイします。

  1. ソリューション エクスプローラーでプロジェクト ノードを右クリックし、[発行] を選択します。
  2. 発行プロファイルの概要ビューで、右上隅にある [発行] ボタンを選択します。

Visual Studio により、前に作成したコンテナー アプリ環境にアプリが再デプロイされます。 プロセスが終了すると、ブラウザーが起動し、アプリのホーム ページが表示されます。

検索フィールドで Microsoft を検索して、アプリをもう一度テストします。 これで、送信するたびにページが正しい結果で再読み込みされるはずです。

ローカル開発用にロールを構成する

アプリの既存のコードと構成は、開発中にローカルで実行しているときにも機能します。 以前に構成した DefaultAzureCredential クラスでは、Azure サービスに対して認証するためにローカル環境の資格情報を取得できます。 認証を機能させるには、アプリのマネージド ID に割り当てられたのと同じロールを自分のアカウントに割り当てる必要があります。 これは、Visual Studio または Azure CLI へのログインに使用するのと同じアカウントである必要があります。

ローカル開発環境にサインインする

資格情報を DefaultAzureCredential で取得するには、Azure CLI、Visual Studio、または Azure PowerShell にサインインする必要があります。

az login

開発者アカウントにロールを割り当てる

  1. Azure portal で、前に作成した scalablerazor**** ストレージ アカウントに移動します。
  2. 左側のナビゲーションから [アクセス制御 (IAM)] を選択します。
  3. [+ 追加] を選択し、ドロップダウン メニューから [ロールの割り当ての追加] を選びます。
  4. [ロールの割り当ての追加] ページで、Storage blob data contributor を検索し、一致する結果を選んでから、[次へ] を選択します。
  5. [ユーザー、グループ、またはサービス プリンシパル] が選択されていることを確認してから、[+ メンバーの選択] を選びます。
  6. [メンバーの選択] ポップアップで、自分の user@domain アカウントを検索し、結果からそれを選択します。
  7. [次へ] を選んでから、[確認と割り当て] を選択します。 Azure によって設定が検証された後、もう一度 [確認と割り当て] を選択します。

前述のように、ロールの割り当てアクセス許可が反映されるまでに 1 分から 2 分かかる場合があり、まれに最大 8 分かかることもあります。

前の手順を繰り返して、キー コンテナー サービスとシークレットにアクセスできるように、アカウントにロールを割り当てます。

  1. Azure portal で、前に作成した razorscalingkeys キー コンテナーに移動します。
  2. 左側のナビゲーションから [アクセス制御 (IAM)] を選択します。
  3. [+ 追加] を選択し、ドロップダウン メニューから [ロールの割り当ての追加] を選びます。
  4. [ロールの割り当ての追加] ページで、Key Vault Crypto Service Encryption User を検索し、一致する結果を選んでから、[次へ] を選択します。
  5. [ユーザー、グループ、またはサービス プリンシパル] が選択されていることを確認してから、[+ メンバーの選択] を選びます。
  6. [メンバーの選択] ポップアップで、自分の user@domain アカウントを検索し、結果からそれを選択します。
  7. [次へ] を選んでから、[確認と割り当て] を選択します。 Azure によって設定が検証された後、もう一度 [確認と割り当て] を選択します。

このロールの割り当てが反映されるまで、もう一度待つ必要がある場合があります。

その後、Visual Studio に戻り、アプリをローカルで実行できます。 コードは引き続き期待どおりに機能するはずです。 DefaultAzureCredential では、Visual Studio または Azure CLI の既存の資格情報が使用されます。

信頼性の高い Web アプリ パターン

.NET 用の信頼性の高い Web アプリ パターンYouTube のビデオ記事を参照して、ゼロからでも既存のアプリをリファクタリングしても、信頼性が高く、パフォーマンスが高く、テスト可能で、コスト効率が高く、スケーラブルな ASP.NET Core アプリを作成する方法に関するガイダンスを参照してください。