編集

次の方法で共有


.NET 向け信頼性の高い Web アプリ パターン

Azure App Service
Azure Front Door
Azure Cache for Redis
.NET

この記事では、信頼性の高い Web アプリ パターンの実装に関するガイダンスを提供します。 このパターンでは、クラウド移行のために Web アプリを変更 (リプラットフォーム) する方法について説明します。 Well-Architected フレームワークの原則に沿った規範的なアーキテクチャ、コード、構成のガイダンスを提供します。

.NET 向け信頼性の高い Web アプリ パターンを使用する理由

信頼性の高い Web アプリ パターンは、クラウドへの移行時に Web アプリを変更する方法 (リプラットフォーム) を定義する一連の原則と実装手法です。 クラウドで成功するために行う必要がある最小限のコード更新に焦点を当てています。 次のガイダンスでは、参照実装を例として使用し、架空の会社 Relecloud のリプラットフォームの流れに従って、移行に関するビジネス コンテキストを説明します。 Relecloud では、.NET 向け信頼性の高い Web アプリ パターンを実装する以前は、ASP.NET フレームワークを使用するモノリシックなオンプレミスのチケット販売 Web アプリがありました。

ヒント

GitHub logo信頼性の高い Web アプリ パターンの参照実装 (サンプル) が用意されています。 これは、Relecloud という架空の会社での、信頼性の高い Web アプリの実装の最終状態を表します。 この記事で説明する、コード、アーキテクチャ、構成に対するすべての更新プログラムを取り入れた実稼働グレードの Web アプリです。 この参照実装をデプロイして使用し、信頼性の高い Web アプリ パターンの実装について案内します。

信頼性の高い Web アプリ パターンの実装方法

この記事には、信頼性の高い Web アプリ パターンを実装するための、アーキテクチャ、コード、構成のガイダンスが含まれます。 以下のリンクを使用して、必要な特定のガイダンスに移動します。

  • ビジネス コンテキスト: このガイダンスをビジネス コンテキストに合わせて調整し、リプラットフォームに関する意志決定を促進する短期目標と長期目標を定義する方法を学習します。
  • アーキテクチャのガイダンス: 適切なクラウド サービスを選択し、ビジネス要件を満たすアーキテクチャを設計する方法について説明します。
  • コードのガイダンス: クラウドでの Web アプリの信頼性とパフォーマンス効率を向上させる、再試行、サーキット ブレーカー、キャッシュ アサイドの 3 つの設計パターンを実装します
  • 構成のガイダンス: 認証と承認、マネージド ID、適正サイズの環境、コードとしてのインフラストラクチャ、監視を構成します。

ビジネス コンテキスト

Web アプリのリプラットフォームの最初の手順は、ビジネス目標を定義することです。 サービス レベル目標やコスト最適化の目標、Web アプリケーションの将来の目標など、短期目標を設定する必要があります。 これらの目標は、クラウド サービスの選択と、クラウド内の Web アプリケーションのアーキテクチャに影響します。 99.9% のアップタイムなど、Web アプリのターゲット SLO を定義します。 Web アプリの可用性に影響を与えるすべてのサービスについて、複合 SLA を計算します。

たとえば、Relecloud では売上予測は好調であり、チケット販売 Web アプリの需要の増加が予想されます。 この需要を満たすために、Web アプリケーションの目標を定義しました。

  • 低コストで価値の高いコード変更を適用
  • 99.9% のサービス レベル目標 (SLO) にリーチ
  • DevOps プラクティスを採用
  • コスト最適化環境を作成
  • 信頼性とセキュリティを改善

Relecloud のオンプレミス インフラストラクチャは、これらの目標を達成するためのコスト効率の高いソリューションではありませんでした。 そこで、Web アプリケーションを Azure に移行することが、即時および将来の目標を達成するための最もコスト効率の高い方法であると判断しました。

アーキテクチャのガイダンス

信頼性の高い Web アプリ パターンには、いくつかの重要なアーキテクチャ要素があります。 エンドポイント解決を管理するための DNS、悪意のある HTTP トラフィックをブロックする Web アプリケーション ファイアウォール、受信ユーザー要求を保護してルーティングするためのロード バランサーが必要です。 アプリケーション プラットフォームは、Web アプリ コードをホストし、仮想ネットワーク内のプライベート エンドポイントを介してすべてのバックエンド サービスに対する呼び出しを実行します。 アプリケーション パフォーマンス監視ツールは、Web アプリの状態を把握するためのメトリックとログをキャプチャします。

信頼性の高い Web アプリ パターンの重要なアーキテクチャ要素を示す図。

図 1. 信頼性の高い Web アプリ パターンの重要なアーキテクチャ要素。

アーキテクチャの設計

目標復旧時間 (RTO) や目標復旧時点 (RPO) などの復旧メトリックをサポートするようにインフラストラクチャを設計します。 RTO は可用性に影響し、SLO をサポートする必要があります。 目標復旧ポイント (RPO) を決定し、RPO を満たすようにデータ冗長性を構成します。

  • インフラストラクチャの信頼性を選択します。 可用性のニーズを満たすために必要な可用性ゾーンとリージョンの数を決定します。 複合 SLA が SLO を満たすまで、可用性ゾーンとリージョンを追加します。 信頼性の高い Web アプリ パターンは、アクティブ/アクティブ構成またはアクティブ/パッシブ構成で複数のリージョンをサポートします。 たとえば、参照実装では、アクティブ/パッシブ構成を使用して 99.9% の SLO を満たしています。

    マルチリージョンの Web アプリの場合は、ビジネス ニーズに応じてアクティブ/アクティブ構成またはアクティブ/パッシブ構成をサポートするように、2 つ目のリージョンにトラフィックをルーティングするようにロード バランサーを構成します。 2 つのリージョンでは同じサービスが必要ですが、一方のリージョンにはリージョンを接続するハブ仮想ネットワークを配置する点が異なります。 ハブアンドスポーク ネットワーク トポロジを導入して、ネットワーク ファイアウォールなどのリソースを一元化および共有します。 仮想マシンがある場合は、ハブ仮想ネットワークに Bastion ホストを追加して仮想マシンを安全に管理します (図 2 を参照)。

    2 つ目のリージョンを設定し、ハブアンドスポーク トポロジを採用した信頼性の高い Web アプリ パターンを示す図。

    図 2. 2 つ目のリージョンを設定し、ハブアンドスポーク トポロジを採用した信頼性の高い Web アプリ パターン。

  • ネットワーク トポロジを選択します。 Web とネットワークの要件に適したネットワーク トポロジを選択します。 複数の仮想ネットワークを使用する場合は、ハブアンドスポーク ネットワーク トポロジを使用します。 これにより、オンプレミスと仮想ネットワークに対するハイブリッド接続オプションを使用した、コスト、管理、セキュリティの利点がもたらされます。

適切な Azure サービスを選択する

Web アプリをクラウドに移行する場合は、ビジネス要件を満たし、オンプレミス Web アプリの現在の機能に合わせた Azure サービスを選択する必要があります。 この配置により、リプラットフォーム工数を最小限に抑えることができます。 たとえば、同じデータベース エンジンを維持し、既存のミドルウェアとフレームワークをサポートできるサービスを使用します。 次のセクションでは、Web アプリに適した Azure サービスを選択するためのガイダンスを提供します。

たとえば、クラウドに移行する以前は、Relecloud のチケット販売 Web アプリはオンプレミスのモノリシックな ASP.NET アプリでした。 2 つの仮想マシン上で実行され、Microsoft SQL Server データベースがありました。 Web アプリは、スケーラビリティと機能のデプロイに関する一般的な課題に苦しんでいます。 この出発点、ビジネス目標、SLO がサービスの選択を推進しました。

  • アプリケーション プラットフォーム: Azure App Service をアプリケーション プラットフォームとして使用します。 Relecloud では、次の理由からアプリケーション プラットフォームとして Azure App Service を選択しました。

    • 高いサービス レベル アグリーメント (SLA): 運用環境の SLO 99.9% を満たす高い SLA を備えています。
    • 管理オーバーヘッドの削減: これは、スケーリング、正常性チェック、負荷分散を処理するフル マネージド ソリューションです。
    • .NET サポート: アプリケーションが書き込まれる .NET のバージョンをサポートします。
    • コンテナー化機能: Web アプリはコンテナー化せずにクラウドに集約できますが、アプリケーション プラットフォームにより、Azure サービスを変更しないコンテナー化もサポートされています。
    • オートスケール: Web アプリは、ユーザーのトラフィックと設定に基づいて、自動的にスケールイン/アウトできます。 プラットフォームでは、さまざまなホスティング要件に対応するために、スケールアップまたはスケールダウンもサポートされています。
  • ID 管理: ID およびアクセス管理ソリューションとして Microsoft Entra ID を使用します。 Relecloud では、次の理由で Microsoft Entra ID を選択しました。

    • 認証と承認: アプリケーションは、コール センターの従業員を認証および承認する必要があります。
    • 拡張性: より大規模なシナリオをサポートするように拡張されます。
    • ユーザー ID 制御:コール センターの従業員は、既存のエンタープライズ ID を使用できます。
    • 承認プロトコルのサポート: マネージド ID に対して OAuth 2.0 をサポートします。
  • データベース: 同じデータベース エンジンを維持できるサービスを使用します。 データ ストアのデシジョン ツリーを使用します。 Relecloud の Web アプリでは、オンプレミスの SQL Server を使用していました。 そのため、既存のデータベース スキーマ、ストアド プロシージャ、関数を使用したいと考えていました。 Azure ではいくつかの SQL 製品を利用できますが、Relecloud では次の理由から Azure SQL Database を選択しました。

    • 信頼性: 汎用層は、高い SLA と複数リージョンの冗長性が提供します。 高いユーザー負荷をサポートできます。
    • 管理オーバーヘッドの削減: マネージド SQL データベース インスタンスを提供します。
    • 移行サポート: オンプレミス SQL Server からのデータベース移行をサポートします。
    • オンプレミス構成との整合性: 既存のストアド プロシージャ、関数、およびビューをサポートします。
    • 回復力: バックアップとポイントインタイム リストアをサポートします。
    • 専門知識と最小限のやり直し: SQL Database は社内の専門知識を活用し、導入に必要な作業は最小限です。
  • アプリケーション パフォーマンスの監視: Application Insights を使用して、アプリケーションのテレメトリを分析します。 Relecloud では、次の理由で Application Insights を使用することを選択しました。

    • Azure Monitor との統合: Azure Monitor との最適に統合します。
    • 異常検出: パフォーマンスの異常を自動的に検出します。
    • トラブルシューティング: 実行中のアプリの問題を診断するのに役立ちます。
    • 監視: ユーザーがアプリをどのように使用しているかに関する情報を収集し、カスタム イベントを簡単に追跡できるようにします。
    • 可視性のギャップ: オンプレミス ソリューションには、アプリケーション パフォーマンス監視ソリューションがありませんでした。 Application Insights は、アプリ プラットフォームとコードとの簡単な統合を提供します。
  • キャッシュ: Web アプリ アーキテクチャにキャッシュを追加するかどうかを選択します。 Azure Cache for Redis は、Azure のプライマリ キャッシュ ソリューションです。 これは、Redis ソフトウェアに基づくマネージド インメモリ データ ストアです。 Relecloud の Web アプリの負荷は、コンサートや会場の詳細の表示に大きく偏っており、次の理由から Azure Cache for Redis が追加されました。

    • 管理オーバーヘッドの削減: フル マネージド サービスです。
    • スピードとボリューム: 一般的にアクセスされ、変化が遅いデータでは、データ スループットが高く、読み取りの待機時間が短くなります。
    • 多様なサポート: Web アプリのすべてのインスタンスが使用する統合キャッシュの場所です。
    • 外部データ ストア: VM ローカル キャッシュを実行したオンプレミスのアプリケーション サーバー。 このセットアップでは、頻度の高いデータがオフロードされず、データを無効にできませんでした。
    • 非スティッキー・セッション: セッション・ステートを外部化すると、非スティッキー・セッションがサポートされます。
  • ロード バランサー: PaaS ソリューションを使用する Web アプリケーションでは、Web アプリのアーキテクチャと要件に基づいて、Azure Front Door、Azure Application Gateway、またはその両方を使用する必要があります。 ロード バランサーのデシジョン ツリーを使用して、適切なロード バランサーを選択します。 Relecloud では、複数のリージョン間でトラフィックをルーティングできるレイヤー 7 ロード バランサーが必要でした。 Relecloud は、SLO を 99.9 満たすためにマルチリージョンの Web アプリを必要としていました。 Relecloud は、次の理由から Azure Front Door を選択しました。

    • グローバル負荷分散: 複数のリージョン間でトラフィックをルーティングできるレイヤー 7 ロード バランサーです。
    • Web application firewall: Azure Web Application Firewall とネイティブに統合されます。
    • ルーティングの柔軟性: アプリケーション チームは、アプリケーションの将来の変更に対応するために必要なイングレスを構成できます。
    • トラフィックの高速化: エニーキャストを使用して、最も近い Azure のポイント オブ プレゼンスにリーチし、Web アプリへの最速ルートを見つけます。
    • Custom Domain: 柔軟なドメイン検証で Custom Domain 名をサポートします。
    • 正常性プローブ: アプリには、インテリジェントな正常性プローブの監視が必要です。 Azure Front Door は、プローブからの応答を使用して、クライアント要求をルーティングするための最適な配信元を決定します。
    • 監視サポート: Front Door とセキュリティ パターンの両方に対応するオールインワン ダッシュボードがある組み込みレポートをサポートします。 Azure Monitor と統合するアラートを構成できます。 アプリケーションが各要求と失敗した正常性プローブをログに記録できるようにします。
    • DDoS Protection:第 3 から 4 層の DDoS Protection が組み込まれています。
    • コンテンツ配信ネットワーク: Relecloud がコンテンツ配信ネットワークを使用するために位置づけます。 コンテンツ配信ネットワークは、サイト アクセラレーションを提供します。
  • Web アプリケーション ファイアウォール: Azure Web Application Firewall を使用して、Web での一般的な悪用や脆弱性からの一元的な保護を提供します。 Relecloud では、次の理由から、Azure Web Application Firewall を使用しました。

    • グローバル保護: パフォーマンスを犠牲にすることなく、グローバルな Web アプリ保護を向上させます。
    • ボットネット保護: チームは、ボットネットに関連するセキュリティ上の懸念事項に対応するために、設定を監視、構成できます。
    • オンプレミスとのパリティ: オンプレミス ソリューションは、IT 部門が管理する Web アプリケーション ファイアウォールの内側で実行されていました。
    • 使いやすさ: Web Application Firewall は Azure Front Door と統合されています。
  • ストレージの構成: アプリ構成ストレージを Web アプリに追加するかどうかを選択します。 Azure App Configuration は、アプリケーション設定と機能フラグを一元管理するためのサービスです。 App Configuration のベスト プラクティスを確認して、このサービスがアプリに適しているかどうかを判断します。 Relecloud では、ファイルベース構成を、アプリケーション プラットフォームおよびコードと統合されている一元的な構成ストアで置き換えたいと考えていました。 次の理由から、App Configuration をアーキテクチャに追加しました。

    • 柔軟性: 機能フラグをサポートします。 機能フラグを使用すると、ユーザーはアプリを再デプロイすることなく、運用環境で早期プレビュー機能をオプトインおよびオプトアウトできます。
    • Git パイプラインのサポート: 構成データの信頼できるソースは、Git リポジトリである必要があります。 パイプラインで、一元的な構成ストアのデータを更新する必要があります。
    • マネージド ID のサポート: マネージド ID をサポートして、構成ストアへの接続を簡素化し、セキュリティで保護しやすくします。
  • シークレット マネージャー: Azure での管理にシークレットを使用している場合は、Azure Key Vault を使用します。 ConfigurationBuilder オブジェクトを使用して、.NET アプリに Key Vault を組み込むことができます。 Relecloud のオンプレミスの Web アプリではシークレットをコード構成ファイルに格納していましたが、RBAC と監査制御をサポートする場所にシークレットを格納する方がセキュリティ対策として適しています。 マネージド ID は Azure リソースに接続するための推奨ソリューションですが、Relecloud には管理に必要なアプリケーション シークレットがありました。 Relecloud は、次の理由で Key Vault を使用しました。

    • 暗号化: 保存時と転送中の暗号化をサポートします。
    • マネージド ID のサポート:アプリケーション サービスは、マネージド ID を使用してシークレット ストアにアクセスできます。
    • 監視とログに記録: 監査アクセスを簡単にし、格納されているシークレットが変更されたときにアラートを生成します。
    • 統合: Azure 構成ストア (App Configuration) およびWeb ホスティング プラットフォーム (App Service) とのネイティブ統合が提供されます。
  • ストレージ ソリューション: Azure のストレージ オプションを確認し、要件に基づいて適切なストレージ ソリューションを選択します。 Relecloud のオンプレミスの Web アプリでは各 Web サーバーにディスク ストレージをマウントしていましたが、チームは外部データ ストレージ ソリューションを使用したいと考えていました。 Relecloud は、次の理由で Azure Blob Storage を選択しました。

    • セキュアなアクセス: Web アプリは、パブリック インターネットに公開されているストレージに匿名アクセスでアクセスするためのエンドポイントを排除できます。
    • 暗号化: 保存時と転送中のデータを暗号化します。
    • 回復性: ゾーン冗長ストレージ (ZRS) をサポートします。 ゾーン冗長ストレージでは、プライマリ リージョン内の 3 つの Azure 可用性ゾーンにまたがって、データが同期的にレプリケートされます。 各可用性ゾーンは、独立した電源、冷却装置、ネットワークがある別個の物理的な場所にあります。 この構成は、チケット画像の損失に対する回復性を高めます。
  • エンドポイント セキュリティ: Azure Private Link を使用して、仮想ネットワーク内のプライベート エンドポイント経由でサービスとしてのプラットフォーム ソリューションにアクセスします。 仮想ネットワークとサービスとの間のトラフィックは、Microsoft バックボーン ネットワークをまたがります。 Relecloud は、次の理由で Private Link を選択しました。

    • 強化されたセキュリティ通信: アプリケーションが Azure プラットフォーム上のサービスにプライベートにアクセスでき、データ ストアのネットワーク フットプリントが削減されるため、データ漏えいからの保護に役立ちます。
    • 最小工数: プライベート エンドポイントは、Web アプリが使用する Web アプリ プラットフォームとデータベース プラットフォームをサポートします。 どちらのプラットフォームも、変更が最小限になるように既存のオンプレミス構成をミラーリングします。
  • ネットワーク セキュリティ: Azure Firewall を使用して、ネットワーク レベルで受信トラフィックと送信トラフィックを制御します。 Azure Bastion を使用して、RDP/SSH ポートを公開することなく、仮想マシンに安全に接続します。 Relecloud では、ハブ アンド スポーク ネットワーク トポロジを採用し、共有ネットワーク セキュリティ サービスをハブに配置したいと考えていました。 Azure Firewall では、スポークからのすべての送信トラフィックを検査することでセキュリティとネットワーク セキュリティを強化しました。 DevOps サブネット内のジャンプ ホストから安全にデプロイするために、Relecloud には、Azure Bastion が必要でした。

コードのガイダンス

Web アプリをクラウドに正常に移行するには、再試行パターン、サーキット ブレーカー パターン、キャッシュアサイド設計パターンを使用して Web アプリ コードを更新する必要があります。

信頼性の高い Web アプリの重要なアーキテクチャにおける設計パターンのロールを示す図。

図 3。 設計パターンのロール。

各設計パターンは、Well-Architected フレームワークの 1 つ以上の柱に沿ったワークロード設計の利点を提供します。 実装する必要があるパターンの概要を以下に示します。

  1. 再試行パターン: 再試行パターンは、断続的に失敗する可能性のある操作を再試行することによって、一時的なエラーを処理します。 このパターンは、他の Azure サービスに対するすべての送信呼び出しに実装します。

  2. サーキット ブレーカー パターン: サーキット ブレーカー パターンは、一時的ではない操作をアプリケーションが再試行するのを防ぎます。 このパターンは、他の Azure サービスに対するすべての送信呼び出しに実装します。

  3. キャッシュアサイド パターン: キャッシュアサイド パターンは、データストアよりも頻繁に、キャッシュに対する追加とキャッシュからの取得を実行します。 このパターンは、データベースに対する要求に実装します。

設計パターン 信頼性 (RE) セキュリティ (SE) コスト最適化 (CO) オペレーショナル エクセレンス (OE) パフォーマンス効率 (PE) WAF の原則のサポート
再試行パターン RE:07
サーキット ブレーカー パターン RE:03
RE:07
PE:07
PE:11
キャッシュアサイド パターン RE:05
PE:08
PE:12

再試行パターンを実装する

一時的なサービスの中断に対処するために、再試行パターンをアプリケーション コードに追加します。 これらの中断は、一時的な障害と呼ばれます。 一時的な障害は、通常は数秒以内に解決されます。 再試行パターンを使用すると、失敗した要求を再送信できます。 また、要求の遅延と、障害と認めるまでの試行回数を構成することもできます。

  • 組み込みの再試行メカニズムを使用する ほとんどの Azure サービスで早急に実装を進める必要がある、組み込みの再試行メカニズムを使用します。 たとえば、参照実装では、Entity Framework Core の接続の回復性を使用して、Azure SQL Database に対する要求に再試行パターンを適用します (次のコードを参照してください)。

    services.AddDbContextPool<ConcertDataContext>(options => options.UseSqlServer(sqlDatabaseConnectionString,
        sqlServerOptionsAction: sqlOptions =>
        {
            sqlOptions.EnableRetryOnFailure(
            maxRetryCount: 5,
            maxRetryDelay: TimeSpan.FromSeconds(3),
            errorNumbersToAdd: null);
        }));
    
  • 再試行プログラミング ライブラリを使用します。 HTTP 通信の場合は、PollyMicrosoft.Extensions.Http.Resilience などの標準の回復性ライブラリを統合します。 これらのライブラリは、外部 Web サービスとの通信を管理するために不可欠な包括的な再試行メカニズムを提供します。 たとえば、参照実装では、IConcertSearchService オブジェクトを呼び出すオブジェクトがコードによって構築されるたびに、Polly を使用して再試行パターンを適用します (次のコードを参照してください)。

    private void AddConcertSearchService(IServiceCollection services)
    {
        var baseUri = Configuration["App:RelecloudApi:BaseUri"];
        if (string.IsNullOrWhiteSpace(baseUri))
        {
            services.AddScoped<IConcertSearchService, MockConcertSearchService>();
        }
        else
        {
            services.AddHttpClient<IConcertSearchService, RelecloudApiConcertSearchService>(httpClient =>
            {
                httpClient.BaseAddress = new Uri(baseUri);
                httpClient.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
                httpClient.DefaultRequestHeaders.Add(HeaderNames.UserAgent, "Relecloud.Web");
            })
            .AddPolicyHandler(GetRetryPolicy())
            .AddPolicyHandler(GetCircuitBreakerPolicy());
        }
    }
    
    private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
    {
        var delay = Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromMilliseconds(500), retryCount: 3);
        return HttpPolicyExtensions
          .HandleTransientHttpError()
          .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
          .WaitAndRetryAsync(delay);
    }
    

サーキット ブレーカー パターンを実装する

サーキット ブレーカー パターンを使用して、一時的な障害ではないサービスの中断を処理します。 サーキット ブレーカー パターンは、アプリケーションが応答しないサービスに継続的にアクセスすることを防ぎます。 これにより、アプリケーションが解放され、CPU サイクルの無駄が回避されるため、アプリケーションはエンド ユーザーに対するパフォーマンスの整合性を維持します。

たとえば、参照実装では、API に対するすべての要求にサーキット ブレーカー パターンが適用されます。 これは、HandleTransientHttpError ロジックを使用して、安全に再試行できる HTTP 要求を検出しますが、指定された期間にわたるエラーの総数が制限されます (次のコードを参照してください)。

private static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
{
    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
        .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30));
}

キャッシュアサイド パターンを実装する

キャッシュアサイド パターンを Web アプリに追加して、メモリ内データ管理を改善します。 このパターンにより、データ要求を処理し、キャッシュと永続的ストレージ (データベースなど) の間の整合性を確保する責任がアプリケーションに割り当てられます。 これにより、応答時間が短縮され、スループットが向上し、さらなるスケーリングの必要性が軽減されます。 また、プライマリ データストアの負荷が軽減され、信頼性とコスト最適化が向上します。 キャッシュアサイド パターンを実装するには、次の推奨事項に従います。

  • キャッシュを使用するようにアプリケーションを構成します。 実稼働アプリでは、分散 Redis Cache を使用する必要があります。これは、データベース クエリを減らすことでパフォーマンスが向上し、非スティッキー セッションが実現するため、ロード バランサーがトラフィックを均等に分散できるようになります。 たとえば、参照実装では、分散 Redis Cache を使用しています。 AddAzureCacheForRedis メソッドは、Azure Cache for Redis を使用するようにアプリケーションを構成します (次のコードを参照してください)。

    private void AddAzureCacheForRedis(IServiceCollection services)
    {
        if (!string.IsNullOrWhiteSpace(Configuration["App:RedisCache:ConnectionString"]))
        {
            services.AddStackExchangeRedisCache(options =>
            {
                options.Configuration = Configuration["App:RedisCache:ConnectionString"];
            });
        }
        else
        {
            services.AddDistributedMemoryCache();
        }
    }
    
  • 必要性の高いデータをキャッシュします。 必要性の高いデータにキャッシュアサイド パターンを適用して、その有効性を高めます。 Azure Monitor を使用して、データベースの CPU、メモリ、ストレージを追跡します。 これらのメトリックは、キャッシュアサイド パターンを適用した後に、より小さいデータベース SKU を使用できるかどうかを判断するのに役立ちます。 たとえば、参照実装では、Upcoming Concerts ページで使用される、必要性の高いデータがキャッシュされます。 GetUpcomingConcertsAsync メソッドは、SQL Database から Redis Cache にデータをプルし、キャッシュに最新のコンサート データを入力します (次のコードを参照してください)。

    public async Task<ICollection<Concert>> GetUpcomingConcertsAsync(int count)
    {
        IList<Concert>? concerts;
        var concertsJson = await this.cache.GetStringAsync(CacheKeys.UpcomingConcerts);
        if (concertsJson != null)
        {
            // There is cached data. Deserialize the JSON data.
            concerts = JsonSerializer.Deserialize<IList<Concert>>(concertsJson);
        }
        else
        {
            // There's nothing in the cache. Retrieve data 
            // from the repository and cache it for one hour.
            concerts = await this.database.Concerts.AsNoTracking()
                .Where(c => c.StartTime > DateTimeOffset.UtcNow && c.IsVisible)
                .OrderBy(c => c.StartTime)
                .Take(count)
                .ToListAsync();
            concertsJson = JsonSerializer.Serialize(concerts);
            var cacheOptions = new DistributedCacheEntryOptions {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
            };
            await this.cache.SetStringAsync(CacheKeys.UpcomingConcerts, concertsJson, cacheOptions);
        }
        return concerts ?? new List<Concert>();
    }
    
  • キャッシュ データを最新の状態に保ちます。 最新のデータベース変更と同期するように、定期的なキャッシュ更新をスケジュールします。 データ ボラティリティとユーザーのニーズに基づいて最適なリフレッシュ レートを決定します。 この方法により、アプリケーションはキャッシュ アサイド パターンを使用して、迅速なアクセスと最新の情報の両方を提供できます。 たとえば、参照実装では、1 時間だけデータをキャッシュし、データが変更されたときに CreateConcertAsync メソッドを使用してキャッシュ キーをクリアします (次のコードを参照してください)。

    public async Task<CreateResult> CreateConcertAsync(Concert newConcert)
    {
        database.Add(newConcert);
        await this.database.SaveChangesAsync();
        this.cache.Remove(CacheKeys.UpcomingConcerts);
        return CreateResult.SuccessResult(newConcert.Id);
    }
    
  • データの一貫性を確保します。 データベース書き込み操作の直後にキャッシュを更新するメカニズムを実装します。 イベント駆動の更新または専用のデータ管理クラスを使用して、キャッシュの一貫性を確保します。 キャッシュとデータベースの変更を一貫して同期することは、キャッシュ アサイド パターンの主要な部分です。 たとえば、参照実装では、UpdateConcertAsync メソッドを使用して、キャッシュ内のデータの整合性を維持します (次のコードを参照してください)。

    public async Task<UpdateResult> UpdateConcertAsync(Concert existingConcert), 
    {
       database.Update(existingConcert);
       await database.SaveChangesAsync();
       this.cache.Remove(CacheKeys.UpcomingConcerts);
       return UpdateResult.SuccessResult();
    }
    

構成ガイダンス

以降のセクションでは、構成の更新の実装に関するガイダンスを提供します。 各セクションは、Well-Architected フレームワークの 1 つ以上の柱と一致します。

構成 信頼性 (RE) セキュリティ (SE) コスト最適化 (CO) オペレーショナル エクセレンス (OE) パフォーマンス効率 (PE) WAF の原則のサポート
ユーザーの認証と承認を構成する SE:05
OE:10
マネージド ID を実装する SE:05
OE:10
適切なサイズの環境 CO:05
CO:06
自動スケーリングを実装する RE:06
CO:12
PE:05
リソースのデプロイの自動化 OE:05
監視を実装する OE:07
PE:04

認証と承認を構成する

Web アプリケーションを Azure に移行する場合は、ユーザーの認証と承認のメカニズムを構成します。 次の推奨事項に従ってください。

  • ID プラットフォームを使用します。 Microsoft ID プラットフォームを使用して、Web アプリ認証を設定します。 このプラットフォームでは、シングルテナント アプリケーションとマルチテナント アプリケーションの両方がサポートされており、ユーザーは自分の Microsoft ID またはソーシャル アカウントでサインインできます。

  • アプリの登録を作成します。 Microsoft Entra ID では、プライマリ テナントにアプリケーションを登録する必要があります。 アプリケーションを登録することで、Web アプリへのアクセス権を取得するユーザーがプライマリ テナントに ID を確保できます。

  • プラットフォームの機能を使用します。 ユーザー認証とデータ アクセスにプラットフォームの機能を使用することで、カスタムの認証コードの必要性を最小限に抑えます。 たとえば、App Services では組み込みの認証がサポートされているため、Web アプリで最小限のコードを記述するか、まったくコードを記述せずに、ユーザーのサインインとデータへのアクセスを実現できます。

  • アプリケーションで承認を強制します。 ロールベースのアクセス制御 (RBAC) を使用して、アプリケーションのロールに最小限の特権を割り当てます。 重複を回避し、明確にするために、さまざまなユーザー アクションに対して特定のロールを定義します。 ユーザーを適切なロールにマップし、必要なリソースとアクションにのみアクセスできるようにします。

  • ストレージに対しては一時的なアクセスを優先します。 一時的なアクセス許可を使用して、共有アクセス署名 (SAS) などの不正アクセスや違反から保護します。 ユーザー委任 SAS を使用して、一時的なアクセスを許可するときのセキュリティを最大化します。 これは、Microsoft Entra ID の資格情報を使用し、永続的なストレージ アカウント キーを必要としない唯一の SAS です。

  • Azure で承認を強制します。 Azure RBAC を使用して、ユーザー ID に最小限の特権を割り当てます。 Azure RBAC は、Azure のリソース ID がアクセスできる対象、それらのリソースに対して実行できる操作、アクセスできる領域を決定します。

  • 永続的で高度なアクセス許可は避けます。 Microsoft Entra Privileged Identity Management を使用して、特権操作の Just-In-Time アクセスを許可します。 たとえば、開発者は多くの場合、データベースの作成/削除、テーブル スキーマの変更、ユーザーのアクセス許可の変更を行うために、管理者レベルのアクセス権を必要とします。 Just-In-Time アクセス権を使用して、特権タスクを実行するための一時的なアクセス許可をユーザー ID に付与しします。

マネージド ID を実装する

マネージド ID をサポートするすべての Azure サービスにマネージド ID を使用します。 マネージド ID を使用すると、Azure リソース (ワークロード ID) は、資格情報を管理することなく、他の Azure サービスに対して認証し、やり取りすることができます。 ハイブリッド システムとレガシ システムでは、移行を簡略化するためにオンプレミスの認証ソリューションを維持できますが、できるだけ早くマネージド ID に移行する必要があります。 マネージド ID を実装するには、次の推奨事項に従います。

  • 適切な種類のマネージドIDを選択します。 同じアクセス許可のセットを必要とする複数の Azure リソースがある場合は、ユーザー割り当てのマネージド ID を優先します。 各リソースに対してシステム割り当てのマネージド ID を作成し、すべてのリソースに同じアクセス許可を割り当てるよりも、この設定のほうが効率的です。 そうでない場合は、システム割り当てのマネージド ID を使用します。

  • 最小特権を構成します。 Azure RBAC を使用して、データベースでの CRUD アクションやシークレットへのアクセスなど、操作に不可欠なアクセス許可のみを付与します。 ワークロード ID のアクセス許可は永続的であるため、ワークロード ID にジャスト・イン・タイムまたは短期のアクセス許可を提供することはできません。 Azure RBAC が特定のシナリオに対応していない場合は、Azure サービス レベルのアクセス ポリシーで Azure RBAC を補完します。

  • 残りのシークレットを保護します。 残りのシークレットを Azure Key Vault に格納します。 各 HTTP 要求中ではなく、アプリケーションの起動時に Key Vault からシークレットを読み込みます。 HTTP 要求内での高頻度のアクセスにより、Key Vault のトランザクション制限を超える可能性があります。 アプリケーション構成を Azure App Configuration に格納します。

たとえば、参照実装では、SQL データベース接続文字列の Authentication 引数を使用しているため、App Service はマネージド ID: Server=tcp:my-sql-server.database.windows.net,1433;Initial Catalog=my-sql-database;Authentication=Active Directory Default を使用して SQL データベースに接続できます。 DefaultAzureCredential を使用することで、Web API がマネージド ID を使用して Key Vault に接続できるようにします (次のコードを参照してください)。

    builder.Configuration.AddAzureAppConfiguration(options =>
    {
         options
            .Connect(new Uri(builder.Configuration["Api:AppConfig:Uri"]), new DefaultAzureCredential())
            .ConfigureKeyVault(kv =>
            {
                // Some of the values coming from Azure App Configuration
                // are stored in Key Vault. Use the managed identity
                // of this host for the authentication.
                kv.SetCredential(new DefaultAzureCredential());
            });
    });

適切なサイズの環境

Azure サービスのパフォーマンス レベル (SKU) を使用して、過剰にならないように各環境のニーズを満たします。 環境のサイズを適切に設定するには、次の推奨事項に従います。

  • コストを見積もる。 Azure 料金計算ツールを使用して、各環境のコストを見積もります。

  • 実稼働環境のコストを最適化します。 運用環境には、運用に必要なサービス レベル アグリーメント (SLA)、機能、スケールを満たす SKU が必要です。 リソースの使用状況を継続的に監視し、実際のパフォーマンス ニーズに合わせて SKU を調整します。

  • 実稼働前の環境のコストを最適化します。 実稼働前の環境では、低コストのリソースを使用し、不要なサービスを無効にして、Azure Dev/Test 料金設定などの割引を適用する必要があります。 実稼働前の環境が実稼働環境と十分に同等であることを確認し、リスクが発生しないようにします。 このバランスにより、不要なコストを発生させずにテストを効果的に維持できます。

  • コードとしてのインフラストラクチャ (IaC) を使用して SKU を定義します。 IaC を実装することで、環境に基づいて適切な SKU を動的に選択してデプロイします。 このアプローチにより、一貫性が向上し、管理が簡略化されます。

たとえば、参照実装では、Bicep パラメーターを使用して、よりコストの高いレベル (SKU) を実稼働環境にデプロイします。

    var redisCacheSkuName = isProd ? 'Standard' : 'Basic'
    var redisCacheFamilyName = isProd ? 'C' : 'C'
    var redisCacheCapacity = isProd ? 1 : 0

自動スケーリングを実装する

オートスケールにより、Web アプリの回復力と応答性が維持され、動的なワークロードを効率的に処理できます。 オートスケールを実装するには、次の推奨事項に従います。

  • スケールアウトを自動化します。Azure オートスケールを使用して、実稼働環境での水平スケーリングを自動化します。 アプリケーションがさまざまな負荷を処理できるように、主要なパフォーマンス メトリックに基づいてスケールアウトするようにオートスケール ルールを構成します。

  • スケーリング トリガーを調整します。 アプリケーションのスケーリング要件に慣れていない場合は、最初のスケーリング トリガーとして CPU 使用率から始めます。 スケーリング トリガーを調整して、RAM、ネットワーク スループット、ディスク I/O などの他のメトリックを含めます。 目標は、Web アプリケーションの動作を合わせることで、パフォーマンスを向上させることです。

  • スケールアウト バッファーを指定します。 最大容量に達する前にトリガーするように、スケーリングのしきい値を設定します。 たとえば、100% に達するまで待機するのではなく、CPU 使用率 85% で実行するようにスケーリングを構成します。 このプロアクティブなアプローチは、パフォーマンスを維持し、潜在的なボトルネックを回避するのに役立ちます。

リソースのデプロイの自動化

自動化を使用して、すべての環境に Azure のリソースとコードをデプロイおよび更新します。 次の推奨事項に従ってください。

  • コードとしてのインフラストラクチャを使用します。 継続的インテグレーションと継続的デリバリー (CI/CD) パイプラインを通じて、コードとしてのインフラストラクチャをデプロイします。 Azure では、すべての Azure リソース用に Bicep、ARM (JSON)、Terraform のテンプレートがあらかじめ用意されています。

  • 継続的インテグレーション/継続的デプロイ (CI/CD) パイプラインを使用します。 CI/CD パイプラインを使用して、ソース管理からさまざまな環境 (テスト、ステージング、運用など) にコードをデプロイします。 GitHub プロジェクト用の Azure DevOps または GitHub Actions を使用している場合は、Azure パイプラインを利用します。

  • 単体テストの統合。 App Services にデプロイする前に、パイプライン内のすべての単体テストの実行と受け渡しに優先順位を付けます。 SonarQube などのコード品質およびカバレッジ ツールを組み込んで、包括的なテスト カバレッジを実現します。

  • モック フレームワークを導入します。 外部エンドポイントを含むテストでは、モック フレームワークを利用します。 これらのフレームワークを使用すると、シミュレートされたエンドポイントを作成できます。 これにより、実際の外部エンドポイントを構成し、環境間でテスト条件を統一する必要がなくなります。

  • セキュリティ スキャンを実行します。 静的アプリケーション セキュリティ テスト (SAST) を使用して、ソース コードのセキュリティ上の欠陥とコーディング エラーを見つけます。 さらに、ソフトウェア構成分析 (SCA) を実行して、サードパーティのライブラリとコンポーネントのセキュリティ リスクを調べます。 これらの分析用のツールは、GitHub と Azure DevOps の両方に簡単に統合できます。

監視を実装する

アプリケーションとプラットフォームの監視を実装して、Web アプリのオペレーショナル エクセレンスとパフォーマンス効率を向上させます。 監視を実装するには、次の推奨事項に従います。

  • アプリケーション テレメトリを収集します。 Azure Application Insights で自動インストルメンテーションを使用して、コードを変更することなく、要求スループット、平均要求期間、エラー、依存関係の監視などのアプリケーション テレメトリを収集します。

    参照実装では、NuGet パッケージ Microsoft.ApplicationInsights.AspNetCore からの AddApplicationInsightsTelemetry を使用して、テレメトリの収集を有効にします (次のコードを参照してください)。

    public void ConfigureServices(IServiceCollection services)
    {
       ...
       services.AddApplicationInsightsTelemetry(Configuration["App:Api:ApplicationInsights:ConnectionString"]);
       ...
    }
    
  • カスタム アプリケーション メトリックを作成します。 カスタム アプリケーション テレメトリには、コードベースのインストルメンテーションを使用します。 コードに Application Insights SDK を追加し、Application Insights API を使用します。

    参照実装では、カートのアクティビティに関連するイベントに関するテレメトリが収集されます。 this.telemetryClient.TrackEvent は、カートに追加されたチケットをカウントします。 ここではイベント名 (AddToCart) が指定され、concertIdcount を持つディクショナリが指定されます (次のコードを参照)。

    this.telemetryClient.TrackEvent("AddToCart", new Dictionary<string, string> {
        { "ConcertId", concertId.ToString() },
        { "Count", count.ToString() }
    });
    
  • プラットフォームを監視します。 サポートされているすべてのサービスの診断を有効にし、関連付けのためにアプリケーション ログと同じ宛先に診断を送信します。 Azure サービスはプラットフォーム ログを自動的に作成しますが、診断を有効にした場合にのみ保存されます。 診断をサポートするサービスごとに、診断設定を有効にします。

参照実装をデプロイする

参照実装では、最初の導入フェーズで必要な変更に重点を置きながら、オンプレミスの ASP.NET アプリケーションから Azure への移行のシミュレーションを通じて開発者を案内します。 この例では、架空の会社である Relecloud のコンサート チケット販売アプリケーションを使用し、オンプレミスの Web アプリケーションを通じてチケットを販売しています。 Relecloud では、Web アプリケーションに次の目標を設定しています。

  • 低コストで価値の高いコード変更を実装する
  • 99.9% のサービス レベル目標 (SLO) を達成する
  • DevOps プラクティスを採用
  • コスト最適化環境を作成
  • 信頼性とセキュリティを向上させる

Relecloud では、オンプレミス インフラストラクチャは、これらの目標を達成するコスト効率の高いソリューションではないと判断しました。 そして、CAMS Web アプリケーションを Azure に移行することが、直近および将来の目標を達成するための最もコスト効率の高い方法であると決定しました。 次のアーキテクチャは、Relecloud の信頼性の高い Web アプリ パターンの実装の最終状態を表しています。

参照実装のアーキテクチャを示す図。図 3. 参照実装のアーキテクチャ。このアーキテクチャの Visio ファイルをダウンロードします。