編集

次の方法で共有


Java 用の最新の Web アプリ パターン

Azure App Service
Azure Service Bus

この記事では、モダン Web アプリ パターンを実装する方法について説明します。 Modern Web App パターンでは、クラウド Web アプリを最新化し、サービス指向アーキテクチャを導入する方法を定義します。 このパターンは、Azure Well-Architected Frameworkの原則に沿った規範的なアーキテクチャ、コード、および構成のガイダンスを提供します。 このパターンは、Reliable Web App パターンに基づいています。

モダン Web アプリ パターンを使用する理由

モダン Web アプリ パターンは、Web アプリケーションの需要の高い領域を最適化するのに役立ちます。 これらの領域を分離して、コスト最適化のための独立したスケーリングを可能にする詳細なガイダンスを提供します。 この方法では、重要なコンポーネントに専用のリソースを割り当てることが可能になり、全体的なパフォーマンスが向上します。 分離可能なサービスを切り離すことで、アプリの一部の速度低下が他の部分に影響を与えないようにすることで、信頼性を向上させることができます。 また、個々のアプリ コンポーネントの独立したバージョン管理も可能になります。

モダン Web アプリ パターンの実装方法

この記事には、モダン Web アプリ パターンを実装するためのガイダンスが含まれています。 次のリンクを使用して、必要な特定のガイダンスに移動します。

  • アーキテクチャ ガイダンス。 Web アプリ コンポーネントをモジュール化し、適切なサービスとしてのプラットフォーム (PaaS) ソリューションを選択する方法について説明します。
  • Code ガイダンス。 分離されたコンポーネントを最適化するための 4 つの設計パターンを実装します。ストラングラー Fig、Queue-Based 負荷平準化、競合コンシューマー、正常性エンドポイント監視。
  • 構成ガイダンスの。 分離されたコンポーネントの認証、承認、自動スケール、コンテナー化を構成します。

ヒント

GitHub ロゴ Modern Web App パターンの 参照実装 (サンプル アプリ) があります。 これは、モダン Web アプリの実装の終了状態を表します。 これは、この記事で説明するすべてのコード、アーキテクチャ、および構成の更新プログラムを備える運用グレードの Web アプリです。 この参照実装をデプロイすることで、モダン Web アプリ パターンを実装するためのガイドとして使用できます。

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

モダン Web アプリ パターンは、信頼性の高い Web アプリ パターンに基づいています。 追加のアーキテクチャ コンポーネントがいくつか必要です。 次の図に示すように、メッセージ キュー、コンテナー プラットフォーム、ストレージ サービス、コンテナー レジストリが必要です。

モダン Web アプリ パターンのベースライン アーキテクチャを示す図。

より高いサービス レベル目標 (SLO) では、Web アプリ アーキテクチャに 2 つ目のリージョンを追加できます。 ビジネス ニーズに応じて、アクティブ/アクティブまたはアクティブ/パッシブのいずれかの構成をサポートするように、2 番目のリージョンにトラフィックをルーティングするようにロード バランサーを構成します。 2 つのリージョンには同じサービスが必要ですが、1 つのリージョンにハブ仮想ネットワークがある点が異なります。 ハブアンドスポーク ネットワーク トポロジを使用して、ネットワーク ファイアウォールなどのリソースを一元化して共有します。 ハブ仮想ネットワーク経由でコンテナー リポジトリにアクセスします。 仮想マシンがある場合は、要塞ホストをハブ仮想ネットワークに追加して、セキュリティを強化して管理します。 次の図は、このアーキテクチャを示しています。

2 番目のリージョンを持つモダン Web アプリ パターン アーキテクチャを示す図。

アーキテクチャを分離する

モダン Web アプリ パターンを実装するには、既存の Web アプリ アーキテクチャを分離する必要があります。 アーキテクチャを分離するには、モノリシック アプリケーションを、それぞれ特定の機能を担当する、より小規模で独立したサービスに分割する必要があります。 このプロセスには、現在の Web アプリの評価、アーキテクチャの変更、最後にコンテナー プラットフォームへの Web アプリ コードの抽出が含まれます。 目標は、分離されることで最もメリットのあるアプリケーション サービスを体系的に特定して抽出することです。 アーキテクチャを分離するには、次の推奨事項に従います。

  • サービスの境界を特定します。 ドメイン駆動型の設計原則を適用して、モノリシック アプリケーション内の境界付けられたコンテキストを識別します。 境界付けられた各コンテキストは論理境界を表し、分離の候補です。 個別のビジネス機能を表し、依存関係が少ないサービスが適しています。

  • サービスの利点を評価します。 独立したスケーリングにより最大のメリットが得られるサービスに焦点を当てます。 たとえば、LOB アプリケーションの電子メール サービス プロバイダーなどの外部依存関係では、障害からの分離が必要になる場合があります。 頻繁に更新または変更が行われるサービスを検討してください。 これらのサービスを切り離すことで、独立したデプロイが可能になり、アプリケーションの他の部分に影響するリスクが軽減されます。

  • 技術的な実現可能性を評価します。 現在のアーキテクチャを調べて、分離プロセスに影響する可能性がある技術的な制約と依存関係を特定します。 サービス間でデータを管理および共有する方法を計画します。 分離されたサービスでそれぞれ独自のデータを管理し、サービスの境界を越えた直接データベース アクセスを最小限に抑える必要があります。

  • Azure サービスをデプロイします。 抽出する Web アプリ サービスをサポートするために必要な Azure サービスを選択してデプロイします。 ガイダンスについては、「この記事の適切な Azure サービス を選択する」セクションを参照してください。

  • Web アプリ サービスを切り離します。 新しく抽出された Web アプリ サービスがシステムの他の部分との対話に使用できる明確なインターフェイスと API を定義します。 各サービスが独自のデータを管理できるが、一貫性と整合性を確保するデータ管理戦略を設計します。 この抽出プロセスで使用する特定の実装戦略と設計パターンについては、Code ガイダンスの セクションを参照してください。

  • 分離されたサービス用に独立したストレージを使用します。 バージョン管理とデプロイを簡略化するには、分離された各サービスに独自のデータ ストアがあることを確認します。 たとえば、参照実装では、電子メール サービスを Web アプリから分離し、サービスがデータベースにアクセスする必要がなくなります。 代わりに、サービスは Azure Service Bus メッセージを介して電子メール配信の状態を Web アプリに伝え、Web アプリはそのデータベースにメモを保存します。

  • 分離されたサービスごとに個別のデプロイ パイプラインを実装します。 個別のデプロイ パイプラインを実装する場合、各サービスは独自のスケジュールに従って更新できます。 社内のチームや組織が異なるサービスを所有している場合、個別のデプロイ パイプラインを使用すると、各チームが独自のデプロイを制御できます。 Jenkins、GitHub Actions、Azure Pipelines などの継続的インテグレーションおよび継続的デリバリー (CI/CD) ツールを使用して、これらのパイプラインを設定します。

  • セキュリティの制御を見直します。 ファイアウォール規則やアクセス制御など、新しいアーキテクチャを考慮してセキュリティ制御が更新されていることを確認します。

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

自社のアーキテクチャ内の各 Azure サービスについては、Well-Architected Framework の関連する Azure サービス ガイドを参照してください。 モダン Web アプリ パターンでは、非同期メッセージングをサポートするためのメッセージング システム、コンテナー化をサポートするアプリケーション プラットフォーム、コンテナー イメージ リポジトリが必要です。

  • メッセージ キューを選択します。 メッセージ キューは、サービス指向アーキテクチャの重要なコンポーネントです。 これによりメッセージの送信者と受信者が分離され、非同期メッセージングが有効になります。 Azure メッセージング サービスの選択に関するガイダンスを使用して、設計のニーズに対応した Azure メッセージング システムを選択します。 Azure には、Azure Event Grid、Azure Event Hubs、Service Bus の 3 つのメッセージング サービスがあります。 Service Bus から始めて、Service Bus がニーズを満たしていない場合は、他の 2 つのオプションのいずれかを使用します。

    Service ユース ケース
    Service Bus エンタープライズ アプリケーションで価値の高いメッセージを信頼性が高く、順序付けして、場合によってはトランザクション配信を行う場合は、Service Bus を選択します。
    Event Grid 多数の個別のイベントを効率的に処理する必要がある場合は、Event Grid を選択します。 Event Grid は、待機時間の短いパブリッシュ/サブスクライブ モデルで多数の小さな独立したイベント (リソース状態の変更など) をサブスクライバーにルーティングする必要があるイベント ドリブン アプリケーションに対してスケーラブルです。
    Event Hubs テレメトリ、ログ、リアルタイム分析など、大量の高スループットのデータ インジェストのために Event Hubs を選択します。 Event Hubs は、一括データを継続的に取り込んで処理する必要があるストリーミング シナリオ向けに最適化されています。
  • コンテナー サービスを実装します。 コンテナー化するアプリケーションの要素には、コンテナーをサポートするアプリケーション プラットフォームが必要です。 Azure コンテナー サービスの選択 ガイダンスは、いずれかを選択するのに役立ちます。 Azure には、Azure Container Apps、Azure Kubernetes Service (AKS)、Azure アプリ Service の 3 つの主要なコンテナー サービスがあります。 Container Apps から始めて、Container Apps がニーズを満たしていない場合は、他の 2 つのオプションのいずれかを使用します。

    Service ユース ケース
    コンテナー アプリ イベント ドリブン アプリケーションでコンテナーを自動的にスケーリングおよび管理するサーバーレス プラットフォームが必要な場合は、Container Apps を選択します。
    AKS Kubernetes の構成の詳細な制御や、スケーリング、ネットワーク、セキュリティの高度な機能が必要な場合は、AKS を選択します。
    Web App for Containers App Service で Web App for Containers を選択すると、最も簡単な PaaS エクスペリエンスが得られます。
  • コンテナー リポジトリを実装します。 コンテナー ベースのコンピューティング サービスを使用する場合は、コンテナー イメージを格納するリポジトリが必要です。 Docker Hub などのパブリック コンテナー レジストリや、Azure Container Registry などのマネージド レジストリを使用できます。 Azure のコンテナー レジストリの概要に関するガイダンスは、その選択に役立ちます。

コードのガイダンス

独立したサービスを正常に分離して抽出するには、Strangler Fig、Queue-Based Load Leveling、競合コンシューマー、正常性エンドポイントの監視、再試行という設計パターンで Web アプリ コードを更新する必要があります。 次の図は、これらのパターンの役割を示しています。

モダン Web アプリ パターン アーキテクチャにおけるデザイン パターンの役割を示す図。

  1. ストラングラー フィグ パターン: ストラングラー フィグ パターンは、分離されたサービスにモノリシック アプリケーションから段階的に機能を移行します。 このパターンをメイン Web アプリに実装し、エンドポイントに基づいてトラフィックを転送することで、独立したサービスに徐々に機能を移行します。

  2. キューベースの負荷平準化パターン: キュー ベースの負荷平準化パターンは、キューをバッファーとして使用して、プロデューサーとコンシューマーの間のメッセージのフローを管理します。 分離されたサービスのプロデューサー部分にこのパターンを実装し、キューを使用してメッセージ フローを非同期的に管理します。

  3. 競合コンシューマー パターン: 競合コンシューマー パターンを使用すると、分離されたサービスの複数のインスタンスが同じメッセージ キューから独立して読み取り、競合してメッセージを処理できるようになります。 分離されたサービスにこのパターンを実装し、複数のインスタンスにタスクを分散させます。

  4. 正常性エンドポイント監視パターン: 正常性エンドポイント監視パターンは、Web アプリのさまざまなコンポーネントの状態と正常性を監視するためのエンドポイントを公開します。 (4a) メイン Web アプリにこのパターンを実装します。 (4b) エンドポイントの正常性を追跡するために、分離されたサービスにも実装します。

  5. 再試行パターン: 再試行パターンは、断続的に失敗する可能性のある操作を再試行することによって、一時的なエラーを処理します。 (5a) メッセージ キューやプライベート エンドポイントの呼び出しなど、他の Azure サービスへのすべての送信呼び出しで、メイン Web アプリにこのパターンを実装します。 (5b) プライベート エンドポイントの呼び出しにおける一時的な障害を処理するために、分離されたサービスにもこのパターンを実装します。

各設計パターンには、Well-Architected Framework の 1 つ以上の柱に沿った利点があります。 次の表に詳細を示します。

設計パターン 実装の場所 信頼性 (RE) セキュリティ (SE) コスト最適化 (CO) オペレーショナル エクセレンス (OE) パフォーマンス効率 (PE) 適切に設計されたフレームワークの原則のサポート
ストラングラー フィグ パターン メイン Web アプリ RE:08
CO:07
CO:08
OE:06
OE:11
キューベースの負荷平準化パターン 分離されたサービスのプロデューサー RE:06
RE:07
CO:12
PE:05
競合コンシューマー パターン 分離されたサービス RE:05
RE:07
CO:05
CO:07
PE:05
PE:07
正常性エンドポイントの監視パターン メイン Web アプリと分離されたサービス RE:07
RE:10
OE:07
PE:05
再試行パターン メイン Web アプリと分離されたサービス RE:07

ストラングラー フィグ パターンを実装する

Strangler Fig パターンを使用して、モノリシック コード ベースから新しい独立したサービスに機能を段階的に移行します。 既存のモノリシック コード ベースから新しいサービスを抽出し、Web アプリの重要な部分を徐々にモダン化します。 ストラングラー フィグ パターンを実装するには、次の推奨事項に従います。

  • ルーティング層を設定します。 モノリシック Web アプリのコード ベースで、エンドポイントに基づいてトラフィックを転送するルーティング層を実装します。 必要に応じてカスタム ルーティング ロジックを使用し、トラフィックを転送するための特定のビジネス ルールを処理します。 たとえば、モノリシック アプリに /users エンドポイントがあり、その機能を分離されたサービスに移動すると、ルーティング層はすべての要求を新しいサービスに /users するように指示します。

  • 機能のロールアウトの管理.分離されたサービスを段階的にロールアウトするために機能フラグ段階的なロールアウトを行います。 既存のモノリシック アプリ ルーティングでは、分離されたサービスが受信する要求の数を制御する必要があります。 サービスの安定性とパフォーマンスに自信を持てるように、要求のごく一部から始めて、時間の経過と共に使用量を増やします。

    たとえば、参照実装では、電子メール配信機能がスタンドアロン サービスに抽出されます。 Contoso サポート ガイドを含む電子メールを送信する要求のより大きな割合を処理するために、サービスを段階的に導入できます。 新しいサービスがその信頼性とパフォーマンスを証明するにつれて、最終的には一連の電子メールの責任をモノリスから引き継ぎ、移行を完了できます。

  • ファサード サービスを使用します (必要な場合)。 ファサード サービスは、1 つの要求が複数のサービスとやり取りする必要がある場合や、基になるシステムの複雑さをクライアントから隠したい場合に便利です。 ただし、分離されたサービスに公開 API がない場合は、ファサード サービスは必要ない可能性があります。

    モノリシック Web アプリのコード ベースで、要求を適切なバックエンド (モノリスまたはマイクロサービス) にルーティングするファサード サービスを実装します。 新しい分離されたサービスがファサードを介してアクセスされたときに、要求を個別に処理できることを確認します。

キューベースの負荷平準化パターンを実装する

分離されたサービスのプロデューサー部分にQueue ベースの負荷平準化パターンを実装して、即時応答を必要としないタスクを非同期的に処理します。 このパターンでは、キューを使用してワークロードの分散を管理することで、システムの全体的な応答性とスケーラビリティが向上します。 これにより、分離されたサービスは一貫した速度で要求を処理できます。 このパターンを効果的に実装するには、次の推奨事項に従います。

  • 非ブロッキング メッセージ キューを使用します。 分離されたサービスがキュー内のメッセージを処理するのを待機している間、キューにメッセージを送信するプロセスが他のプロセスをブロックしないようにします。 分離されたサービス操作の結果がプロセスに必要な場合は、キューに登録された操作の完了を待機している間に状況を処理する別の方法を実装します。 たとえば、Spring Boot では、StreamBridge クラスを使用して、呼び出し元のスレッドをブロックすることなく、メッセージをキューに非同期的に発行できます。

    private final StreamBridge streamBridge;
    
    public SupportGuideQueueSender(StreamBridge streamBridge) {
        this.streamBridge = streamBridge;
    }
    
    // Asynchronously publish a message without blocking the calling thread
    @Override
    public void send(String to, String guideUrl, Long requestId) {
        EmailRequest emailRequest = EmailRequest.newBuilder()
                .setRequestId(requestId)
                .setEmailAddress(to)
                .setUrlToManual(guideUrl)
                .build();
    
        log.info("EmailRequest: {}", emailRequest);
    
        var message = emailRequest.toByteArray();
        streamBridge.send(EMAIL_REQUEST_QUEUE, message);
    
        log.info("Message sent to the queue");
    }
    

    この Java の例では、 StreamBridge を使用して非同期的にメッセージを送信します。 この方法により、メイン アプリケーションの応答性が維持され、分離されたサービスがキューに登録された要求を管理可能な速度で処理しながら、他のタスクを同時に処理できます。

  • メッセージの再試行と削除を実装します。 キューに登録されたメッセージのうち、正常に処理できないものの処理を再試行するメカニズムを実装します。 エラーが解決しない場合は、それらのメッセージをキューから削除する必要があります。 たとえば、Service Bus には再試行機能と配信不能キュー機能が組み込まれています。

  • べき等メッセージ処理を構成します。 キューからのメッセージを処理するロジックは、メッセージが複数回処理される可能性があるケースを処理するためにべき等である必要があります。 Spring Boot では、 @StreamListener または @KafkaListener を一意のメッセージ識別子と共に使用して、重複する処理を防ぐことができます。 または、Spring Cloud Stream を使用して機能的なアプローチで動作するようにビジネス プロセスを整理することもできます。ここで、 consume メソッドは、繰り返し実行されたときに同じ結果を生成する方法で定義されます。 メッセージの使用の動作を管理する設定の一覧については、「Service Busを使用した Spring Cloud Stream の 」を参照してください。

  • ユーザー エクスペリエンスに対する変更を管理します。 非同期処理を使用すると、タスクがすぐに完了しない可能性があります。 期待値を設定し、混乱を避けるために、タスクがまだ処理されているタイミングをユーザーが把握していることを確認します。 タスクが進行中であることを示すには、視覚的な手掛かりまたはメッセージを使用します。 メールやプッシュ通知など、タスクの完了時に通知を受け取るオプションをユーザーに提供します。

競合コンシューマー パターンを実装する

分離されたサービスに Competing Consumers パターン を実装して、メッセージ キューからの受信タスクを管理します。 このパターンでは、分離されたサービスの複数のインスタンスにタスクを分散する必要があります。 これらのサービスは、キューからのメッセージを処理します。 このパターンにより、負荷分散が強化され、同時要求を処理するためのシステムの容量が増えます。 競合コンシューマー パターンは、次の場合に有効です。

  • メッセージ処理のシーケンスが重要ではない。
  • キューが、形式に誤りがあるメッセージの影響を受けない。
  • 処理操作はべき等です。つまり、最初のアプリケーションの後に結果を変更しなくても複数回適用できます。

競合コンシューマー パターンを実装するには、次の推奨事項に従います。

  • 同時実行メッセージを処理します。 サービスがキューからメッセージを受信する場合は、システム設計に合わせてコンカレンシーを構成することで、システムが予測どおりにスケーリングされるようにします。 ロード テストの結果は、処理する同時実行メッセージの適切な数を特定するのに役立ちます。 1 つから開始して、システムのパフォーマンスを測定できます。

  • プリフェッチを無効にします コンシューマーが準備ができたときにのみメッセージをフェッチするように、メッセージのプリフェッチを無効にします。

  • 信頼できるメッセージ処理モードを使用します。 ピーク ロックなどの信頼性の高い処理モードを使用して、処理に失敗したメッセージを自動的に再試行します。 このモードでは、削除優先の方法よりも信頼性が高くなります。 あるワーカーがメッセージの処理に失敗した場合、そのメッセージが複数回処理されるとしても、別のワーカーがエラーなしでそのメッセージを処理できる必要があります。

  • エラー処理を実装します。 形式が正しくないメッセージまたは処理できないメッセージを別の配信不能キューにルーティングします。 この設計により、繰り返し処理を防ぐことができます。 たとえば、メッセージ処理中に例外をキャッチし、問題のあるメッセージを別のキューに移動できます。 Service Bus では、指定された数の配信試行の後、またはアプリケーションによる明示的な拒否時に、メッセージが配信不能キューに移動されます。

  • 順序が正しくないメッセージを処理します。 誤った順序で到着したメッセージを処理するようにコンシューマーを設計します。 複数の並列コンシューマーがある場合、メッセージが順序を外して処理される可能性があります。

  • キューの長さに基づいてスケーリングします。 キューからメッセージを使用するサービスは、キューの長さに基づいて自動スケーリングする必要があります。 スケール ベースの自動スケーリングにより、受信メッセージの急増を効率的に処理できます。

  • メッセージ応答キューを使用します。 システムでメッセージ後処理の通知が必要な場合は、専用の応答キューまたは応答キューを設定します。 このセットアップでは、操作メッセージングと通知プロセスを分離します。

  • ステートレス サービスを使用します。 ステートレス サービスを使用してキューからの要求を処理することを検討してください。 これにより、スケーリングが容易になり、リソースの効率的な使用が可能になります。

  • ログを構成します。 メッセージ処理ワークフロー内にログ記録と特定の例外処理を統合します。 シリアル化エラーをキャプチャし、これらの問題のあるメッセージを配信不能メカニズムに転送することに重点を置きます。 これらのログは、トラブルシューティングに役立つ貴重な分析情報を提供します。

参照実装では、Container Apps で実行されるステートレス サービスの競合コンシューマー パターンを使用して、Service Bus キューからの電子メール配信要求を処理します。

プロセッサは、トラブルシューティングと監視に役立つメッセージ処理の詳細をログに記録します。 逆シリアル化エラーをキャプチャし、デバッグ中に役立つ分析情報を提供します。 サービスはコンテナー レベルでスケーリングされ、キューの長さに基づいてメッセージスパイクを効率的に処理できます。 コードを次に示します。

@Configuration
public class EmailProcessor {

    private static final Logger log = LoggerFactory.getLogger(EmailProcessor.class);

    @Bean
    Function<byte[], byte[]> consume() {
        return message -> {

            log.info("New message received");

            try {
                EmailRequest emailRequest = EmailRequest.parseFrom(message);
                log.info("EmailRequest: {}", emailRequest);

                EmailResponse emailResponse = EmailResponse.newBuilder()
                        .setEmailAddress(emailRequest.getEmailAddress())
                        .setUrlToManual(emailRequest.getUrlToManual())
                        .setRequestId(emailRequest.getRequestId())
                        .setMessage("Email sent to " + emailRequest.getEmailAddress() + " with URL to manual " + emailRequest.getUrlToManual())
                        .setStatus(Status.SUCCESS)
                        .build();

                return emailResponse.toByteArray();

            } catch (InvalidProtocolBufferException e) {
                throw new RuntimeException("Error parsing email request message", e);
            }
        };
    }
}

正常性エンドポイントの監視パターンを実装する

メイン アプリ コードおよび分離されたサービス コードに正常性エンドポイント監視パターンを実装すると、アプリケーション エンドポイントの正常性を追跡できます。 AKS や Container Apps などのオーケストレーターは、これらのエンドポイントをポーリングしてサービスの正常性を確認し、異常なインスタンスを再起動できます。 Spring Boot Actuator には、正常性チェックのサポートが組み込まれています。 データベース、メッセージ ブローカー、ストレージ システムなどの主要な依存関係の正常性チェック エンドポイントを公開できます。 正常性エンドポイント監視パターンを実装するには、次の推奨事項に従います。

  • 正常性チェックを実装します。 Spring Boot Actuator を使用して、正常性チェック エンドポイントを提供します。 アクチュエータは、組み込みの正常性インジケーターと、さまざまな依存関係のカスタム チェックを含む /actuator/health エンドポイントを公開します。 正常性エンドポイントを有効にするには、pom.xml または build.gradle ファイルに spring-boot-starter-actuator 依存関係を追加します。

    <!-- Add Spring Boot Actuator dependency -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    

    参照実装に示すように、application.properties で正常性エンドポイントを構成します。

        management.endpoints.web.exposure.include=metrics,health,info,retry,retryevents
    
  • 依存関係を検証します。 Spring Boot Actuator には、データベース、メッセージ ブローカー (RabbitMQ または Kafka)、ストレージ サービスなどのさまざまな依存関係の正常性インジケーターが含まれています。 Azure Blob Storage や Service Bus などの Azure サービスの可用性を検証するには、これらのサービスの正常性インジケーターを提供する Azure Spring Apps や Micrometer などのテクノロジを使用します。 カスタム チェックが必要な場合は、カスタム HealthIndicator Bean を作成して実装できます。

    import org.springframework.boot.actuate.health.Health;
    import org.springframework.boot.actuate.health.HealthIndicator;
    import org.springframework.stereotype.Component;
    
    @Component
    public class CustomAzureServiceBusHealthIndicator implements HealthIndicator {
        @Override
        public Health health() {
            // Implement your health check logic here (for example, ping Service Bus).
            boolean isServiceBusHealthy = checkServiceBusHealth();
            return isServiceBusHealthy ? Health.up().build() : Health.down().build();
        }
    
        private boolean checkServiceBusHealth() {
            // Implement health check logic (pinging or connecting to the service).
            return true; // Placeholder. Implement the actual logic.
        }
    }
    
  • Azure リソースを構成します。 アプリの正常性チェック URL を使用して、ライブ性と準備状態を確認するように Azure リソースを構成します。 たとえば、Terraform を使用して、Container Apps にデプロイされているアプリの稼働状態と準備状況を確認できます。 詳細については、「 Health probes in Container Apps」を参照してください。

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

再試行パターン を使用すると、アプリケーションは一時的な障害から復旧できます。 このパターンは Reliable Web App パターンの中心であるため、Web アプリは既に再試行パターンを使用している必要があります。 再試行パターンを、Web アプリから抽出した分離されたサービスによって発行されたメッセージング システムと要求に要求に適用します。 再試行パターンを実装するには、次の推奨事項に従います。

  • 再試行オプションを構成します。 適切な再試行設定でメッセージ キューとの対話を担当するクライアントを構成してください。 再試行の最大数、再試行間の遅延、最大遅延などのパラメーターを指定します。

  • エクスポネンシャル バックオフを使用します。 再試行のための指数バックオフ戦略を実装します。 この戦略では、各再試行の間の時間を指数関数的に増やす必要があります。これにより、高い障害率の期間中にシステムの負荷を軽減できます。

  • SDK の再試行機能を使用します。 Service Bus や Blob Storage などの特殊な SDK を持つサービスの場合は、組み込みの再試行メカニズムを使用します。 これらの組み込みメカニズムは、サービスの一般的なユース ケースに合わせて最適化されており、再試行をより効果的に処理でき、必要な構成が少なくなります。

  • HTTP クライアントには標準の回復性ライブラリを使用します。 HTTP クライアントの場合は、Spring の RestTemplate または WebClient と共に Resilience4j を使用して、HTTP 通信の再試行を処理できます。 一時的な HTTP エラーを効果的に処理するために、Resilience4j の再試行ロジックで RestTemplate をラップできます。

  • メッセージのロックを処理します。 メッセージ ベースのシステムの場合は、データ損失のない再試行をサポートするメッセージ処理戦略を実装します。 たとえば、ピーク ロック モードは使用可能な場合に使用します。 失敗したメッセージが効果的に再試行され、失敗が繰り返し発生した後は配信不能キューに移動されるようにします。

構成ガイダンス

次のセクションでは、構成の更新を実装するためのガイダンスを提供します。 各セクションは、Well-Architected Framework の 1 つ以上の柱と一致します。

構成 信頼性 (RE) セキュリティ (SE) コスト最適化 (CO) オペレーショナル エクセレンス (OE) パフォーマンス効率 (PE) 適切に設計されたフレームワークの原則のサポート
認証と承認を構成する SE:05
OE:10
独立したオートスケールを実装する RE:06
CO:12
PE:05
サービスのデプロイをコンテナー化する CO:13
PE:09
PE:03

認証と承認を構成する

Web アプリに追加するすべての新しい Azure サービス (ワークロード ID) で認証と承認を構成するには、次の推奨事項に従います。

  • 新しいサービスごとにマネージド ID を使用します。 個々の独立したサービスに固有の ID を設定し、サービス間認証にマネージド ID を使用する必要があります。 マネージド ID を使用すると、コードで資格情報を管理する必要がなくなり、資格情報の漏洩のリスクが軽減されます。 これらは、接続文字列などの機密情報をコードや構成ファイルに含めないようにするのに役立ちます。

  • 新しい各サービスに最小限の特権を付与します。 新しいサービス ID ごとに必要なアクセス許可のみを割り当てます。 たとえば、ID がコンテナー レジストリにのみプッシュする必要がある場合は、プルアクセス許可を付与しないでください。 これらのアクセス許可を定期的に確認し、必要に応じて調整します。 デプロイやアプリケーションなど、ロールごとに異なる ID を使用します。 これにより、1 つの ID が侵害された場合の潜在的な損害が制限されます。

  • コードとしてのインフラストラクチャ (IaC) を使用します。 Bicep または Terraform などの同様の IaC ツールを使用して、クラウド リソースを定義および管理します。 IaC により、デプロイでのセキュリティ構成の一貫した適用が保証され、インフラストラクチャのセットアップのバージョン管理が可能になります。

ユーザー (ユーザー ID) の認証と承認を構成するには、次の推奨事項に従います。

  • ユーザーに最小限の特権を付与します。 サービスと同様に、ユーザーが自分のタスクを実行するために必要なアクセス許可のみを持っていることを確認します。 これらのアクセス許可を定期的に確認して調整します。

  • 定期的なセキュリティ監査を実施します。 セキュリティ設定を定期的に見直し、監査します。 誤った構成と不要なアクセス許可を探し、直ちに修正または削除します。

参照実装では、IaC を使用して、追加されたサービスにマネージド ID を割り当て、各 ID に特定のロールを割り当てます。 コンテナー レジストリのプッシュとプルのロールを定義することで、デプロイのロールとアクセス許可を定義します。 コードを次に示します。

resource "azurerm_role_assignment" "container_app_acr_pull" {
  principal_id         = var.aca_identity_principal_id
  role_definition_name = "AcrPull"
  scope                = azurerm_container_registry.acr.id
}

resource "azurerm_user_assigned_identity" "container_registry_user_assigned_identity" {
  name                = "ContainerRegistryUserAssignedIdentity"
  resource_group_name = var.resource_group
  location            = var.location
}

resource "azurerm_role_assignment" "container_registry_user_assigned_identity_acr_pull" {
  scope                = azurerm_container_registry.acr.id
  role_definition_name = "AcrPull"
  principal_id         = azurerm_user_assigned_identity.container_registry_user_assigned_identity.principal_id
}


# For demo purposes, allow the current user to access the container registry.
# Note: When running as a service principal, this is also needed.
resource "azurerm_role_assignment" "acr_contributor_user_role_assignement" {
  scope                = azurerm_container_registry.acr.id
  role_definition_name = "Contributor"
  principal_id         = data.azuread_client_config.current.object_id
}

独立したオートスケールを構成する

最新の Web アプリ パターンでは、モノリシック アーキテクチャの分割が開始され、サービスの切り離しが導入されます。 Web アプリ アーキテクチャを分離すると、分離されたサービスを個別にスケーリングできます。 Web アプリ全体ではなく、独立した Web アプリ サービスをサポートするように Azure サービスをスケーリングすることで、需要を満たしながらスケーリング コストが最適化されます。 コンテナーをオートスケールするには、次の推奨事項に従います。

  • ステートレス サービスを使用します。 サービスがステートレスであることを確認します。 Web アプリにインプロセス セッション状態が含まれている場合は、Redis などの分散キャッシュまたは SQL Server などのデータベースに外部化します。

  • オートスケール ルールを構成します。 サービスに対して最もコスト効率の高い制御を提供するオートスケール構成を使用します。 コンテナー化されたサービスの場合、Kubernetes Event-Driven Autoscaler (KEDA) などのイベント ベースのスケーリングでは、多くの場合、イベント メトリックに基づいてスケーリングできる細かい制御が提供されます。 Container Apps と AKS では KEDA がサポートされます。 App Service など、KEDA をサポートしていないサービスの場合は、プラットフォーム自体によって提供される自動スケール機能を使用します。 これらの機能には、多くの場合、メトリックベースのルールまたは HTTP トラフィックに基づくスケーリングが含まれます。

  • 最小レプリカを構成します。 コールド スタートを防ぐには、少なくとも 1 つのレプリカを維持するように自動スケール設定を構成します。 コールド スタートは、停止状態からのサービスの初期化です。 コールド スタートでは、多くの場合、応答が遅れます。 コストを最小限に抑えることが優先され、コールド スタートの遅延を許容できる場合は、自動スケールを構成するときに最小レプリカ数を 0 に設定します。

  • クールダウン期間を構成します。 適切なクールダウン期間を適用して、スケーリング イベント間の遅延を導入します。 目標は、一時的な負荷のスパイクによってトリガーされる過剰なスケーリング アクティビティを防ぐことです。

  • キューベースのスケーリングを構成します。 アプリケーションで Service Bus などのメッセージ キューを使用する場合は、要求メッセージ キューの長さに基づいてスケーリングするように自動スケール設定を構成します。 スケーラーは、キュー内のすべての N メッセージに対してサービスの 1 つのレプリカを維持しようとします (切り上げられます)。

たとえば、参照実装では、Service Bus KEDA スケーラーを使用して、Service Bus キューの長さに基づいてコンテナー アプリを自動的にスケーリングします。 service-bus-queue-length-ruleという名前のスケーリング 規則は、指定された Service Bus キュー内のメッセージ数に基づいてサービス レプリカの数を調整します。 messageCount パラメーターは 10 に設定され、キュー内の 10 個のメッセージごとに 1 つのレプリカを追加するようにスケーラーが構成されます。 レプリカの最大数 (max_replicas) が 10 に設定されています。 レプリカの最小数は、オーバーライドされない限り暗黙的に 0 です。 この構成により、キューにメッセージがない場合にサービスをゼロにスケールダウンできます。 Service Bus キューの接続文字列は、Service Bus に対するスケーラーの認証に使用される azure-servicebus-connection-stringという名前のシークレットとして Azure に格納されます。 Terraform コードを次に示します。

    max_replicas = 10
    min_replicas = 1

    custom_scale_rule {
      name             = "service-bus-queue-length-rule"
      custom_rule_type = "azure-servicebus"
      metadata = {
        messageCount = 10
        namespace    = var.servicebus_namespace
        queueName    = var.email_request_queue_name
      }
      authentication {
        secret_name       = "azure-servicebus-connection-string"
        trigger_parameter = "connection"
      }
    }

サービスのデプロイをコンテナー化する

コンテナー化は、幅広いホストに確実にデプロイできる軽量イメージ内のアプリで必要なすべての依存関係をカプセル化したものです。 デプロイをコンテナー化するには、次の推奨事項に従います。

  • ドメインの境界を特定します。 最初に、モノリシック アプリケーション内のドメイン境界を特定します。 これを行うと、アプリケーションのどの部分を個別のサービスに抽出できるかを判断するのに役立ちます。

  • Docker イメージを作成します。 Java サービスの Docker イメージを作成する場合は、公式の OpenJDK 基本イメージを使用します。 これらのイメージには、Java が実行する必要があるパッケージの最小セットのみが含まれています。 これらのイメージを使用すると、パッケージ サイズと攻撃対象領域の両方が最小限に抑えられます。

  • マルチステージの Dockerfile を使用します。 マルチステージ Dockerfile を使用して、ランタイム コンテナー イメージからビルド時アセットを分離します。 この種類のファイルを使用すると、運用イメージを小さく安全に保つことができます。 構成済みのビルド サーバーを使用し、JAR ファイルをコンテナー イメージにコピーすることもできます。

  • 非ルート ユーザーとして実行します。 (ユーザー名または UID $APP_UID を使用して) 非ルート ユーザーとして Java コンテナーを実行し、最小限の特権の原則に合わせます。 これにより、侵害されたコンテナーの潜在的な影響が制限されます。

  • ポート 8080 でリッスンします。 非ルート ユーザーとしてコンテナーを実行する場合は、ポート 8080 でリッスンするようにアプリケーションを構成します。 これは、非ルート ユーザーの一般的な規則です。

  • 依存関係をカプセル化します。 アプリに必要なすべての依存関係が Docker コンテナー イメージにカプセル化されていることを確認します。 カプセル化により、アプリをさまざまなホストに確実にデプロイできます。

  • 適切な基本イメージを選択します。 選択する基本イメージは、デプロイ環境によって異なります。 たとえば、Container Apps にデプロイする場合は、Linux Docker イメージを使用する必要があります。

このリファレンス実装では、Java アプリケーションをコンテナー化するための Docker ビルド プロセスを示します。 Dockerfile は、必要な Java ランタイム環境を提供する OpenJDK 基本イメージ (mcr.microsoft.com/openjdk/jdk:17-ubuntu) を使用したシングルステージ ビルドを使用します。

Dockerfile には、次の手順が含まれています。

  1. ボリュームの宣言。 一時ボリューム (/tmp) が定義されています。 このボリュームは、コンテナーのメイン ファイル システムとは別の一時ファイル ストレージを提供します。
  2. 成果物のコピー。 アプリケーションの JAR ファイル (email-processor.jar) は、監視に使用される Application Insights エージェント (applicationinsights-agent.jar) と共にコンテナーにコピーされます。
  3. エントリ ポイントの設定。 コンテナーは、Application Insights エージェントを有効にしてアプリケーションを実行するように構成されています。 このコードでは、java -javaagent を使用して、実行時にアプリケーションを監視します。

Dockerfile は、ランタイムの依存関係のみを含めることで、イメージを小さく保ちます。 Linux ベースのコンテナーをサポートする Container Apps などのデプロイ環境に適しています。

# Use the OpenJDK 17 base image on Ubuntu as the foundation.
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu

# Define a volume to allow temporary files to be stored separately from the container's main file system.
VOLUME /tmp

# Copy the packaged JAR file into the container.
COPY target/email-processor.jar app.jar

# Copy the Application Insights agent for monitoring.
COPY target/agent/applicationinsights-agent.jar applicationinsights-agent.jar

# Set the entrypoint to run the application with the Application Insights agent.
ENTRYPOINT ["java", "-javaagent:applicationinsights-agent.jar", "-jar", "/app.jar"]

参照実装をデプロイする

Modern Web App Pattern for Java の参照実装をデプロイ。 リポジトリに、開発と実稼働の両方のデプロイに関する手順が用意されています。 実装をデプロイした後、設計パターンをシミュレートして観察できます。

次の図は、参照実装のアーキテクチャを示しています。

参照実装のアーキテクチャを示す図。

このアーキテクチャの Visio ファイル をダウンロードします。