[レポート] マイクロサービスにおけるアプリケーション統合パターン #API303-R #reinvent

2023.05.04

アノテーション テクニカルサポートの川崎です。

本記事は AWS re:Invent 2022 のセッションレポートとなります。

概要

ほぼすべての統合アプローチにはトレードオフが伴います。ただし、疎結合の統合は、個別に開発および運用できる独立したシステムを設計するのに役立ち、特に非同期通信を使用して、システム全体の可用性と信頼性を向上させることもできます。統合とカンバセーションのシナリオには多くのアプローチがありますが、特定の状況に最適なアプローチが常に明確であるとは限りません。このセッションに参加して、疎結合と非同期通信に重点を置いた統合およびカンバセーション・シナリオの基本パターンについて学びます。クラウドネイティブ・サービスとサーバーレス・サービスで設計された実際のユースケースを調べ、統合テクノロジの選択に関するガイダンスを入手してください。

セッション動画

セッション資料

Application integration patterns for microservices

セッション情報

API303-R

アジェンダ

  • モチベーション
  • パターン
  • ユースケース
  • 学習リソース

はじめに

Dirk といいます。ドイツ出身の AWS ソリューションアーキテクトです。 このセッションでは、 まず、なぜ非同期通信について話すのか、その動機を説明します。 最近、顧客から役立つフィードバックをもらい、より広範囲の動機付けを紹介することにしました。 次に、非同期メッセージングに基づく基本的な統合パターンを紹介します。 具体的なユースケースやサンプルアーキテクチャも紹介します。 最後に、学習リソースや行動を促すフレーズ、関連セッションについて紹介します。 皆さんにとって有益な情報になることを願っています。

関連セッション

API202 で行われた分離されたマイクロサービスに関する実践的なワークショップと、API312 で特定のシナリオにおいて最適な統合サービスの選択方法を紹介するチョークトークが開催されました。これらのセッションでは、統合シナリオが多くの企業で関連する課題であり、さまざまな領域と層に影響を与えていることが指摘されました。また、フレンドリーなAWSソリューションアーキテクトに質問する機会も提供されていました。

かなり顕著な例

顕著な例として、企業が数年または数十年かけてモノリスアプリケーションを構築すると、バージョンの展開やメンテナンスが困難になることがよくあります。 これは、コードのクリーンさが欠けており、スパゲッティコード化やコンポーネントの結合が進んでいることが原因です。 そんな中、マイクロサービスアーキテクチャや自己完結型システムへの切り替えが企業や顧客のリファクタリング計画で検討されるようになります。 これは新たな統合の課題をもたらすことになります。 典型的な最初のステップとして、1つのサンプルサービスを作成し、問題解決や演習の実験として進めます。 しかし、マイクロサービスだけでなく、あらゆるタイプのシステムやアプリケーションが相互に連携し、 一部は他の外部システムや SaaS ソリューションと連携する必要があります。 それが私がこの引用が好きな理由です。同僚の Gregor Hohpe は、次のように述べています。

最新のクラウド アプリケーションでは、統合は後付けではありません。これは、アプリケーション アーキテクチャとソフトウェア配信ライフ サイクルの不可欠な部分です。

では、統合について、そもそもどのような選択肢があるのでしょうか?

一般的なアプリケーション統合オプション

一般的なアプリケーション統合オプションには、いくつかのアプローチがあります。 適切なシナリオで使用すると、それらは理にかなった結果をもたらしますが、使い方を間違えると全体的な経験に苦痛を加える可能性があります。 今回は、すべてのオプションについてお話できませんので、非同期メッセージングに焦点を当てます。

非同期メッセージングが提案される3つのオプションについて、その使用が適切な理由と、非同期通信の検討において重要な点を説明いたします。 小さなガイダンスとして、これらの情報を「ソフトウェアアーキテクチャへのライフハッカーズガイド」と呼んでいます。 最初のガイダンスは「常に真実であることが一番」です。

フェイスヒーラー(faith healer:宗教がかった信念に基づく治療者)に気をつけるべきです。 ここで言うフェイスヒーラーは、人であったり、道具であったり、あなたに方法論を説くものであったりします。 それらは「私を使ってください、そうすればあなたの痛みはすべてなくなり、すべてが解決されます」と言いますが、 ソフトウェアアーキテクチャの現実は、すべてを解決する特効薬は存在しないということです。

フェイスヒーラーに気をつけろ (Beware of the faith healer)

まず重要な点は、アーキテクチャの決定においてトレードオフのない完全な解決策は存在しないということです。 どのアーキテクチャを選んでも問題が生じる可能性があります。 目標は、最も苦痛が少ない選択肢を見つけることです。 また、チームにもこれを伝えることが大切です。 次に、二つ目のガイダンスは分割統治です。 分割統治を用いると、問題の多くが簡単に解決できるかもしれませんが、その代償も同時に払わなければならないということです。 モノリスの例に戻って考えると、このアプローチの利点と欠点がより明確になります。 以上のことを踏まえて、アーキテクチャの決定を行う際は、トレードオフを理解し、分割統治の考え方を取り入れながら、チーム全体で問題を解決することが重要です。

分割統治

分割統治の考え方を用いたシステムのランドスケープは、複雑な依存関係を持つことがあります。 例えば、2008年の amazon.com の依存関係グラフは複雑でしたが、現在の状況はもっと様々でしょう。 しかし、実際の企業で見られるシステムは、相互に統合して管理可能な範囲でつながっていることが一般的です。 このようなシステムの例として、Adrian Cockcroft の GitHub アカウントから拝借したグラフが参考になります。 このグラフでは、システム全体がどのように接続されているか、そしてどのようにリクエストが処理されているかが示されています。 例えば、オンラインショップのウェブサイトである amazon.com では、アイテムの価格や在庫数、配達の速さなどの情報が必要とされるため、 他のシステムを呼び出す必要があります。 オーケストレーション、統合、管理が必要なシステムが非常に多い場合、 また、私の経験によると、それらを統合すると非常に簡単になります。 非常に疎結合な方法で、これをよりよく理解するために、 同期通信と非同期通信の違いについて少し掘り下げてみましょう。

通信交換 (Communication exchange)

同期通信と個別のシステムに分割されたモノリスの例に戻ると、通常、使用する HTTP API があり、 できれば REST API などを使用して、以前はモノリスのコンポーネントであったものとの間でデータを交換します。 しかし、REST APIを使っても、疎結合の統合が実現できるわけではありません。 これは、REST APIがほとんどの場合、RESTlessであるためです。 ハイパーメディアが欠けているため、実際にはRESTfulではありません。 サーバーがクライアントにリンク関係やリンクを通じて次に何をすべきかを通知しなければ、 クライアントのコードはAPIの実装にバインドされているため、柔軟性が低くなります。 同期リクエスト応答パターンでは、リクエスターがリクエストを送信し、レスポンダーが処理し、HTTP接続を通じて応答が返されます。 しかし、リクエストが実行されている間にリソースが両端でバインドされるため、外部からのリクエストの処理が遅くなることがあります。 同期APIを非同期APIに変換することで、リソースをバインドする時間を短縮できます。 リクエスターはリクエストをレスポンダーに送信し、確認を取得します。 その後、レスポンダーがレスポンスを返すことができます。 しかし、依存関係が残っており、APIとメッセージングを区別する必要があります。 非同期リクエスト応答パターンを使用することで、より効率的な通信交換が可能になります。

API とメッセージング

APIは便利ですが、場所や可用性の依存関係などの問題があります。 これらの問題は、メッセージングシステムを導入することで解決できます。 メッセージングにより、リクエスタとレスポンダ間に追加のコンポーネントを追加することができ、相互の知識が必要なくなります。 さらに、クラウドネイティブ・サーバーレスメッセージングシステムを使用することで可用性とスケーラビリティが向上します。 メッセージングシステムに関しては、ソフトウェアアーキテクトがシナリオについて議論するために使用できるパターンがあります。 Enterprise Integration Patterns は統合に関するパターンのカタログで、現在も有効です。 簡単なパターンについて説明すると、メッセージチャネルが双方を分離し、互いに何も知る必要がなくなります。 また、コマンド、ドキュメント、イベントメッセージなど、メッセージの種類が存在します。 非同期リクエスト応答の場合、リクエスト部分と応答部分が分離されているため、リターンアドレスと相関IDパターンが必要です。 リターンアドレスはリクエストメッセージに追加できるメタ情報で、応答者に応答先を指示し、 相関IDパターンを使ってリクエスターは応答を以前の要求に割り当てることができます。 メッセージチャネルは、システム間の通信を円滑に行うために重要な役割を果たします。 メッセージチャネルの種類には、一対一通信やマルチキャスト、ブロードキャストなどがあります。 これらの選択肢を適切に使用することで、APIとメッセージングを効果的に組み合わせ、システムの効率性を向上させることができます。

メッセージチャネル

メッセージチャネルにはポイントツーポイントとパブリッシュ/サブスクライブの2種類があります。 ポイントツーポイントは一対一の通信であり、キューによって実装されます。 対象メッセージは利用可能な受信者のうち1つに送信され、ピーク負荷も平坦化できます。 パブリッシュ/サブスクライブは一対多の通信で、トピックによって実装されます。 それぞれのメッセージが全ての登録者に送信されますが、メッセージ負荷に対処できなくなった場合、追加プロセスを追加することはできません。 しかし、トピックキュー・チェーンパターンを使用することで、コンシューマー側でスケールアウトが可能です。 メッセージバスはパブリッシュ/サブスクライブ・メッセージングチャネルの一種で、より広範に使用されます。 メッセージバスはアプリケーションコンテキストで使用され、関係者はメッセージの生成と消費を行います。 また、制約がある場合や、サードパーティのアダプターが必要な場合もあります。 AWS では、ポイントツーポイントキューには Amazon SQS、トピックには Amazon SNS が提供されています。 また、他のオプションとして Amazon MQ や AWS IoT Core も利用できます。 メッセージバスには Amazon EventBridge が存在し、クラウドネイティブなサーバーレスで実装されています。 メッセージングとストリーミングは終わりのない議論であり、 宗教がかった話になることもあります。 ただし、それぞれの用途や状況に応じて最適な選択肢を理解することが重要です。

メッセージング vs. ストリーミング

メッセージングとストリーミングの意図は、アプリケーションにとって意味のある単位でデータをやり取りすることです。 メッセージングでは、各メッセージが一度消費されると消え、ストリーミングではメッセージのバッチが扱われます。 ストリーミングを実装する AWS サービスは、Amazon Kinesis ファミリーや Amazon Managed Streaming for Apache Kafka (Amazon MSK) サービスがあります。 また、メッセージングシステムにはデッドレターキューやインフライトメッセージなどのパターンがあります。 可視性タイムアウトやメッセージグループによって、消費者に対してメッセージの順序が保持されるようになり、スケーラビリティが向上します。 FIFOキューやトピックもあり、メッセージグループIDを使ってメッセージの順序を保持できます。 メッセージルーティングパターンを使って、アプリケーション間で効果的にデータをやり取りすることができます。

メッセージ ルーティング

メッセージルーティングには、メッセージフィルターという概念があります。 すべてのサブスクライバーがすべてのメッセージを受け取るわけではなく、関心のあるメッセージのサブセットだけを受け取ります。 メッセージフィルターパターンを使用すると、サブスクライバーごとに個別に構成でき、パブリッシャーはその詳細を知る必要がありません。 スキャッターギャザーパターンは、並列処理が必要なシナリオで使用され、リクエスタ・コンテキストからリクエストが送信され、 多数の潜在的なレスポンダが応答を返します。 リクエスタは、返された応答を集約し、最終処理を行います。 メッセージルーティングカテゴリのもう一つのパターンであるパイプとフィルターパターンでは、 メッセージングで実装されたフィルタ(処理ステップ)が、パイプで接続されます。

ここでわかることは、すべての処理ステップ、 すべてのフィルターは、次に何が起こるかについてある程度の知識を持っている必要があります。 ちなみに、それをとても簡単に実装できる AWS のサービスが Amazon EventBridge、メッセージバスサービスです。 その依存関係を持ちたくない場合は、各フィルターと次のステップの間に、サガ オーケストレーション パターンを使用できます。 サガ オーケストレーション パターンは、次に何が起こるかについてのすべての知識を外部化することでこれを解決します。

処理でどのように分岐するのか、これをオーケストレーター コンポーネントに外部化します。 次に何をすべきかを知っているのは、オーケストレータ コンポーネントだけです。 これらの各処理ステップのコードは、 それはただ行うことができ、1つの小さなことを非常にうまく行うことができますが、 その前に何が起こったのか、次に何が起こったのかについて、何も知る必要はありません。

そして、このパターンを実装する AWS サービスは、 クラウド ネイティブでサーバーレスなサービスである AWS Step Functions です。 ステート マシンを使用してそのようなワークフローを実装します。

以上がパターンの紹介でした。 より具体的にするために、サンプルユースケースを紹介します。 これは架空のテクノロジ スタートアップでのユースケースです。

コンテキスト: Wild Rydes 社

Wild Rydes 社は、従来のタクシーに代わるユニコーンを使った新しい輸送サービスを提供しています。 このサービスはサーバーレスのマイクロサービスを実行する最新のソフトウェアアーキテクチャが使われています。 Wild Rydes 社での、一つ目のユースケースとして「ライド完了の送信」を紹介します。

このシナリオでは、ユニコーンが乗客を目的地に降ろした後、Wild Rydes 社にその情報を伝えることが求められます。 このため、ユニコーン用アプリを使用し、HTTP リクエストをバックエンドに送信します。 サーバー側には、API Gateway のような適切な管理サービスが存在し、 リクエストを受け取り、コードにルーティングする役割を担っています。 データベースにも情報が永続化され、クライアントにリソースが生成された旨が通知されます。 このシステムでは、他にも複数のサービスが存在することが想像できます。 例えば、Wild Rydes 社のシステム全体や他のサービスに対して、 情報を提供する役割を持つ顧客通知サービスや顧客会計サービスなどが考えられます。

ライド完了の送信

さまざまな統合方法が検討されていますが、データベースを介した統合は避けるべきで、REST APIやメッセージングによる統合が考えられます。 特に、Amazon SNS を使ったメッセージング統合が効果的なアプローチです。 SNSのメッセージフィルター機能を利用することで、興味のあるメッセージだけを受け取ることができます。 また、キューを追加してアーキテクチャ全体に回復力を持たせることもできます。 別の使用例として、事前予約キャンペーンを見てみましょう。 Wild Rides 社のマーケティングも、様々なキャンペーンを実施しており、 現在実施しているのはCTA (Call-To-Action:行動喚起) を使ったキャンペーンです。

事前予約キャンペーン

事前予約キャンペーンでは、顧客に来週の乗車予約を促します。 Wild Rydes アプリを使った顧客からのリクエストは、API Gateway を介して処理され、データベースに保存されます。 マーケティングチームは、さらなるキャンペーンを追加することでトラフィックを増やそうとしています。 このため、1つのサービス内で処理を分離する方法が必要です。 マネージドサービスの API Gateway を利用し、事前予約前処理リソースとして機能させます。 この段階で前処理やサニティチェックが行われ、リクエストが転送されます。 その後、実際の予約処理が行われます。 データレイク取り込みサービスを導入することで、データベースに十分なスペースがない場合でもデータを保存できます。 また、ピーク負荷時にキューを使って API Gateway を効率的に運用することができます。 Wild Rydes 社では、インスタントライドRFQ(見積もりのリクエスト)という機能があります。 これは、顧客が近くのユニコーンから見積もりを要求し、その中から希望に沿ったものを選ぶことができる機能です。 このRFQシナリオには、スキャッターギャザーパターンが活用されています。

インスタントライドの見積依頼

アーキテクチャを構築する過程でここで確認できることは、 内部のすべてが非同期であるにもかかわらず、 REST API を介してクライアントとバックエンドの間で同期通信を行う方法です。 したがって、Wild Rydes 社の顧客は、基本的にどこからどこへ行きたいかという RFQ の詳細を送信します。 RFQ インテーク プロセッサがあり、RFQ をデータベースに保存して RFQ 共有トピックに発行し、クライアントに再度応答して受け入れることができます。

顧客はRFQの期限を設定でき、ユニコーン企業はそれに応じて見積もりを提出します。 見積もりはRFQ応答キューに送り返され、アグリゲーターコンポーネントがデータベースに保存します。 クライアントは、ステータスの更新や結果の取得が可能です。 RFQが終了したら、クライアントにプッシュ通知が送信されます。

まとめ

このプロセスを効率的に行うためには、サーバーレス学習やリソースの共有が活用されます。 また、統合に関するブログやフレンドリーなソリューションアーキテクトによるアドバイスが提供されます。 本セッションでは、インスタントライドの見積依頼プロセスの概要と、それに関連するリソースやサポートについて説明しました。 これらを参考に、効率的なシステムを構築していきましょう。

本セッションの内容に興味を持たれた方は、上部のリンクからセッション動画、セッション資料をご覧ください。