📖

re:Invent 2024: AWSのマルチテナントServerlessアーキテクチャ最適化

2024/01/01に公開

はじめに

海外の様々な講演を日本語記事に書き起こすことで、隠れた良質な情報をもっと身近なものに。そんなコンセプトで進める本企画で今回取り上げるプレゼンテーションはこちら!

📖 AWS re:Invent 2024 - Optimize multi-tenant serverless architectures for agility and scale (SVS322)

この動画では、SaaSやMulti-tenantアーキテクチャをAWS上でServerlessで実装する際のベストプラクティスについて解説しています。Tenant isolation、Noisy neighbor問題、Cost attribution、非効率なリソース利用、スケーリング統合といった主要な課題に対し、Amazon Verified Permissions、Lambda Layers、API Gateway Usage Plan、SQSのMessage Attributes、EventBridgeのワイルドカードパターンマッチングなど、具体的な解決策を示しています。特に、Lambda関数のReserved ConcurrencyとProvisioned Concurrencyを組み合わせたNoisy neighbor問題への対処や、テナントごとのコスト算出方法など、実践的な内容を扱っています。
https://www.youtube.com/watch?v=iUAPybpJ2ek
※ 動画から自動生成した記事になります。誤字脱字や誤った内容が記載される可能性がありますので、正確な情報は動画本編をご覧ください。
※ 画像をクリックすると、動画中の該当シーンに遷移します。

re:Invent 2024関連の書き起こし記事については、こちらのSpreadsheet に情報をまとめています。合わせてご確認ください!

本編

Serverlessアーキテクチャのベストプラクティス:イントロダクション

Thumbnail 0

みなさん、こんにちは。良い一日をお過ごしのことと思います。音声は問題なく聞こえていますでしょうか?ご確認ありがとうございます。私はEnterprise TransformationのSenior Specialist Partner Solution ArchitectのAnand Bilgaiyanです。本日はNishantと一緒に進行させていただきます。みなさん、こんにちは。お時間を取っていただき、ありがとうございます。私はNishant Dhimanと申します。オーストラリアのシドニーを拠点とするAWSのSenior Solutions Architectです。本日は充実したコンテンツをご用意しております。それでは、Serverlessアーキテクチャのベストプラクティスについてご説明させていただきます。

ありがとう、Nishant。簡単なアンケートから始めさせていただきます。Software as a Serviceモデルをすでに利用されている方は何人いらっしゃいますか?かなりの方がいらっしゃいますね。では、Serverlessについてご存知で、Software as a Serviceベースのモデルの実装に使用されている方は何人いらっしゃいますか?多くのお客様が、Software as a ServiceやMulti-tenancyベースのビジネスおよびソフトウェア提供モデルを採用し、自社の製品や機能をサービス中心の方法で提供しています。これにより、ビジネスの成長を加速し、コストと運用効率を最適化しながら、より迅速なイノベーションを実現することができます。AWSでSoftware as a ServiceやMulti-tenantアーキテクチャを実装する際、Serverlessは当然の選択肢となっています。

Thumbnail 140

Multi-tenantアーキテクチャについて話す際、それぞれに固有の課題や考慮事項があり、慎重な設計とアーキテクチャの計画が必要です。本日は、これらのMulti-tenantアーキテクチャを構築する際に念頭に置くべきベストプラクティスと設計・アーキテクチャ上の考慮事項についてご説明します。アジェンダを簡単に確認しましょう。まずMulti-tenantの課題について見た後、Serverlessの機能と、AWSでMulti-tenantソリューションを構築する方法について検討します。このセッションを通して一つのユースケースを取り上げ、SaaSとMulti-tenantの両方の視点から検討し、適切な設計とアーキテクチャの考慮事項とともに、これらの課題について理解を深めていきます。

マルチテナントアーキテクチャの課題とServerlessの利点

Thumbnail 190

Thumbnail 210

Thumbnail 220

Multi-tenantアーキテクチャを構築する際の特有の課題について説明しましょう。最も重要な課題の一つは、間違いなくTenant isolationです。Multi-tenantアーキテクチャでは、あるテナントのデータが他のテナントに漏洩することは、ビジネスの評判に重大な影響を及ぼす可能性があるため、絶対に避けなければなりません。 二つ目の課題は、Noisy neighbor問題です。これは、あるテナントの活動が他のテナントの活動に大きな影響を与える問題で、 ユーザー体験の低下につながります。Cost attributionも重要な課題の一つです。お客様がMulti-tenantソリューションを構築する際、各テナントのニーズや要求を適切に満たすリソースを提供しながら、コストを最適化したいと考えています。また、ビジネスの成長をサポートし、適切な課金を行うために、各テナントのリソース消費とコストを把握する必要があります。

Thumbnail 260

Thumbnail 280

次の課題は、非効率なリソース利用です。Multi-tenantアプリケーションにおいて、リソースの過剰プロビジョニングや過少プロビジョニングを避けたいと考えています。これは、コストとユーザー体験の両方に直接影響を与えるためです。 最後に重要な課題として、スケーリング統合があります。Multi-tenantアーキテクチャを考える際、ビジネスロジック、データベース、インフラベースのコンポーネントに注目しがちですが、Software as a Serviceモデルでは、本質的に複雑な分散システムを扱っています。サービスベースのアーキテクチャについて議論しているため、これらは間違いなく広範な統合を必要とします。

これらの統合は、多くの場合、無人で行われています。本日は、これらのサービスベースのアプリケーションパターンにおける、異なるアーキテクチャコンポーネント間のコミュニケーションについて、マルチテナントソリューションのベストプラクティスと設計上の考慮事項をご紹介します。

Thumbnail 340

AWSでマルチテナントソリューションを構築する際に、Serverlessが最適な選択肢の1つとなる理由について、その課題を理解してきました。このグラフをご覧ください。実線の青い線は、マルチテナント環境における実際のテナントの振る舞いを表しています。各テナントは異なる特性を持ち、それぞれ独自のスケーリング動作と消費パターンを示すため、マルチテナントアプリケーション環境には大きな変動性が生じます。点線で示されているのが、お客様が目指す理想的な状態です。これは、異なるテナントの行動パターンや消費パターンに合わせてリソースをプロビジョニングし、適切なコストスケールとパフォーマンスを実現したい状態を表しています。

Thumbnail 410

Thumbnail 430

Thumbnail 440

ビジネス機能を損なうことなく適切なユーザーエクスペリエンスを提供しながら、余分な支払いは避けたいというのが顧客の望みです。実線と点線の差が小さければ小さいほど、マルチテナントアーキテクチャの俊敏性、運用効率、コスト最適化が向上します。ここでServerlessが役立ちます。なぜなら、インフラストラクチャの管理が不要だからです。マルチテナントアーキテクチャを構築する際に、お客様が通常行わなければならない差別化されていない重労働が、Serverlessによって解消されます。Serverlessベースのアプローチを採用することで、テナントの消費パターン、トラフィック、スループットパターンに基づいて自動的にスケールします。時間単位の課金や専用リソースのプロビジョニングが不要で、Serverlessベースのアーキテクチャでは使用した分だけを支払えばよいのです。さらに、Serverlessアーキテクチャとサービスは高可用性とスケーラビリティを備えています。

Lambda関数を活用したマルチテナントアーキテクチャの設計

Thumbnail 480

ここからは、異なるマルチテナントモデルについて説明し、セッションを通じて最初に見た主要な課題を解決するためのベストプラクティスと設計手法を掘り下げていきましょう。最初のモデルはSiloモデルです。これは一般的でわかりやすいモデルです。テナントごとに異なるリソースをプロビジョニングします。テナントごとにリソースをデプロイする簡単なアーキテクチャの選択肢です。シンプルで優れたデータ分離を提供できる利点がありますが、環境内のテナント数が増加すると、当初は簡単に見えたこのアーキテクチャは時間とともに複雑になっていきます。テナントが増えるほどリソースも増え、運用オーバーヘッドとコストが増加するためです。

Thumbnail 530

Thumbnail 580

次のモデルはPoolモデルで、個々のテナントに専用リソースを提供する代わりに、テナント間でリソースを共有し、クラウドの力を活用して異なるテナントの消費パターンやトラフィックパターンに対応してスケールします。これにより、運用効率とコスト管理が向上しますが、適切なデータ分離を考慮する必要があります。多くのお客様は、より良い運用効率とコスト最適化を実現するためにPoolモデルを活用したいと考えています。しかし、すべての問題に対して単一のソリューションが適合するわけではありません。よく見られるのは、SiloモデルとPoolモデルを組み合わせたもので、一部のアーキテクチャコンポーネントはテナントごとに専用でプロビジョニングし、他のコンポーネントはテナント間で共有するというBridgeモデルと呼ばれるアプローチです。

Thumbnail 630

このセッションの残りの部分では、Pool モデルに焦点を当てていきます。なぜなら、このモデルこそがコスト効率と運用効率の向上につながるからです。Silo モデルや Bridge モデルについて言及する場合は、明確にその旨を示すことにします。

Thumbnail 680

マルチテナントの課題を軽減するための様々な設計・アーキテクチャ上の考慮事項を理解するため、eコマース小売業向けB2B SaaSプラットフォームという仮想的なユースケースを見ていきましょう。このプラットフォームでは、様々な小売業者が自社のビジネス機能をeコマースプラットフォーム上で展開できるようになっています。Shopifyのように、異なる規模の小売業者が素早くこのB2B SaaSプラットフォームにオンボードし、エンドユーザーにサービスを提供することができます。

Thumbnail 690

Thumbnail 720

Thumbnail 730

今回のセッションでは、注文管理ワークフローを取り上げます。この注文管理ワークフローには2つのサブタスクがあります。1つ目は注文作成で、これはOrder ServiceとProduct Serviceという2つのサービスを活用し、エンドユーザーが商品を閲覧して注文できるようにします。2つ目は注文履行と配送で、注文が完了すると、Fulfillment Service、Invoice Service、Shipment Serviceが連携してその注文の履行を行います。注文管理のこれら2つのパスは、疎結合なイベント駆動アーキテクチャを通じて連携します。

Thumbnail 750

Thumbnail 790

まずは注文管理の最初の部分、つまりProduct ServiceとOrder Serviceを含む注文作成に焦点を当てましょう。このパスをAWS上のサーバーレスアーキテクチャで実装する場合、その構成はかなりシンプルです。Lambda関数を使用します - ご存知の通り、AWS Lambdaはサーバーレスコンピューティングサービスで、インフラストラクチャ管理の煩わしさから解放され、ビジネスロジックの記述と実行に集中できます。このLambdaは注文作成部分のOrder ServiceとProduct Serviceを表しており、これらのサービスはAmazon API Gatewayを通じてエンドユーザーに公開されます。データベースには注文情報が保存され、注文が完了すると、注文作成イベントが発行され、フルフィルメント側の他のアーキテクチャコンポーネントに通知されます。

Thumbnail 820

Thumbnail 840

ここで、このアーキテクチャをPoolモデルで考えてみましょう。B2B eコマースプラットフォームにオンボードする異なるテナントからの呼び出しを、共通のLambda関数で処理する場合です。Lambda関数のマルチテナンシーについて、どのようなアーキテクチャ上の考慮事項が必要でしょうか?マルチテナンシーの課題を考える際、最初に挙げられるのがデータ分離の問題です。Lambda関数におけるデータ分離のアーキテクチャ上の考慮事項について見ていきましょう。

Thumbnail 860

Thumbnail 870

Lambdaには3つの可能なデプロイメントモデルがあります。先ほど見たSilo、Pool、Bridgeの全てが実現可能です。例えば、OrderサービスとProductサービスは、複数のテナントが共通のLambda関数を実行するPoolモデルでデプロイすることができます。また、この Lambda関数を各テナントに専用に割り当ててデプロイすることもでき、これがSiloモデルとなります。マルチテナントアーキテクチャには、これらのデプロイメントを実現するための様々なTier戦略があります。そして最後に、これらのLambdaベースのアーキテクチャコンポーネントをデプロイするためのCI/CDパイプラインを構築することができます。

Thumbnail 890

Thumbnail 900

次にデータ分離について説明しましょう。データ分離のためのアーキテクチャ上の考慮事項について説明する前に、従来のマルチテナンシーソリューションでデータ分離がどのように行われているかを見てみましょう。まずユーザーは認証を受けます。

データ分離とコスト算出:Amazon Verified PermissionsとLambda Layersの活用

Thumbnail 910

Thumbnail 930

Thumbnail 940

認証を行うために、Identity Providerが認証処理を担当することができます。例えば、AWSではAmazon Cognitoがこのプロセスを処理します。認証が完了すると、ユーザーにBearer Tokenが返され、ユーザーはこのBearer Tokenを使用して様々なサービスを呼び出すことができます。今回の場合、それはProductサービスとOrderサービスになります。リクエストはAmazon API Gatewayで受け取られ、そこで認可チェックが行われます。これはベストプラクティスの1つで、ProductやOrderなどのエンドレイヤーサービスで認可を処理するのではなく、認可のような横断的な関心事をゲートウェイレイヤーでオフロードするというものです。

Thumbnail 980

認可が完了すると、バックエンドロジックが実行されます。マルチテナンシーのシナリオでは、各テナントはそれぞれのビジネス要件や規制要件に基づいて独自の認可要件を持つことになります。一般的によく見られるのは、ビジネスロジックがどのリソースにアクセスできるかを理解するRole-Based Access(ロールベースアクセス)や、リソースに対してよりきめ細かいアクセス制御を必要とするテナントのためのAttribute-Based Access(属性ベースアクセス)で、この認可ロジックが設定されることです。Role-Based AccessやAttribute-Based Accessを含むこれらの運用ロジックは、全てビジネスロジック内にコーディングまたはタグ付けされます。異なるテナントの運用要件が複雑化するにつれて、これがビジネスロジックを汚染していくことになります。

Thumbnail 1070

標準的な推奨事項の1つは、これらの横断的な関心事をサービスから完全に切り離すことです。なぜなら、サービスはビジネスにとって差別化要因となる処理のみを行うべきだからです。つまり、ビジネスロジックのみを含み、認可ルールやバリデーションなどの横断的な関心事を含むべきではありません。これらの複雑な認可ロジックは、Amazon Verified Permissionsという外部ポリシーストアを通じて、コアビジネスロジックから切り離すことができます。これにより、Role-Based Access ControlとAttribute-Based Access Controlをビジネスロジックから切り離し、より宣言的な方法で設定することが可能になります。

Thumbnail 1080

Thumbnail 1100

ユーザーがアプリケーションにリクエストを送信すると、アプリケーションはAmazon Verified Permissionsにアクセスします。Amazon Verified Permissionsには、テナントのニーズに応じてロールベースのアクセス制御と属性ベースのアクセス制御を含むすべてのポリシーストアが設定されています。Verified Permissionsは異なるアクセス権限を動的に検証し、許可または拒否を判断し、そのアクセス判定をアプリケーションに返します。これにより、マルチテナントアーキテクチャにおける異なるテナント間のデータ分離ポリシーをより適切に、より統制された形で定義することができます。

Thumbnail 1120

Thumbnail 1140

これらの宣言的なポリシーを定義するために、一般的にCedar Policy言語が使用されています。ここにあるサンプルを見てみましょう。このポリシーは、エンドユーザーやロールなどのPrincipal(主体)に対して何を許可するかを定義しています。ロールには、注文の作成や閲覧といった異なる許可されたアクションが特定のリソースに対して設定されます。また、Principalが同じテナンシーのコンテキスト内にいるか、Principalのアカウントがロックされていないか、多要素認証が使用されているかなど、より細かな制御を定義する様々な属性も含まれています。

Thumbnail 1180

Thumbnail 1200

これが、宣言的なCedar Policy言語を通じて、ロールベースのアクセスと属性ベースのアクセスを組み合わせる方法です。これらのポリシーはAmazon Verified Permissionsによって保存され、マルチテナントアプリケーション向けに構築しているプール型マイクロサービスから動的にアクセスすることができます。動的に注入されるテナントIDに基づいて、これらのポリシーは独自の効果を持つことができます。実装では、異なるテナントが、私たちのユースケースにおける注文や商品といった共通のプール型マイクロサービスにアクセスします。

Thumbnail 1210

Thumbnail 1220

各ユーザーは、テナント1やテナント2のような、独自のテナンシーコンテキストを持っています。各テナントが独自の複雑な認可要件を持っている場合、これらのテナントごとに異なるポリシーストアを定義することができます。アプリケーションロジックはプール型のままで、Lambdaもプールモデルを維持しますが、Amazon Verified Permissionsによって異なるテナントごとに異なるポリシーストアが定義されます。

Thumbnail 1240

Thumbnail 1270

Thumbnail 1280

Thumbnail 1290

プール型マイクロサービスであるLambdaは、テナンシーコンテキストに基づいて、そのテナント用にAmazon Verified Permissionsで設定された特定のポリシーストアを動的に識別し、それに応じてそれらのポリシーが評価され、アクセス権限の結果がアップストリームシステムに返されます。また、マルチテナントが共通または類似の認可要件を持ち、特殊な複雑な認可要件を持たないシナリオもあり得ます。そのような場合、プール型のLambdaサービスレイヤーを持つAmazon Verified Permissionsのプール型コンセプトを採用することができます。異なるテナントからのリクエストがこのLambda関数で受信されると、1つのVerified Permissionsポリシーストアに格納された1つのポリシーが、異なるテナントに対しても動的に評価されます。このモデルは、マルチチャネルソリューションで最初に直面した課題であるテナント分離の実現に役立ちます。

Noisy Neighbor問題の解決策:SQSとLambdaの最適化

Thumbnail 1310

Thumbnail 1320

それでは、 Multi-tenancyアーキテクチャにおいて、テナントごとのコストをどのように算出するかという次の課題について見ていきましょう。 適切なコストを算出するためには、マルチテナントアプリケーション環境における各テナントのアクションと、それぞれのアクションに対するリソース消費量を特定することが重要です。テナントのアクションはアプリケーションログから収集でき、リソース消費量はクラウド上のさまざまな使用量メトリクスから収集することができます。

Thumbnail 1370

Thumbnail 1400

Order ServiceとProduct Serviceがあります。 このアプリケーションログとリソース使用量メトリクスの収集は、これらのサービスだけでなく、他のすべてのサービスにも適用されます。推奨されるアーキテクチャパターンの1つとして、コードの重複を避けるために、アクションとリソース消費メトリクスを特定するためのアプリケーションログに関する設定を外部化する必要があります。そこで、Lambda Layersというオプションを使用します。これはLambda関数内のメカニズムで、異なる共通ライブラリや設定を共有する際に、関数のサイズを増やすことなくコードを共有することができます。

Thumbnail 1420

Thumbnail 1450

テナントのアクションを特定するために、MetricsとLoggingのLambda Layersを使用してアプリケーションログとリソース消費量を収集できます。これらのLoggingとMetricsのLambda Layersは、各リクエストに含まれる特定のテナントのアイデンティティコンテキストを含むトークンをデコードできる別のLambda Layerを活用できます。処理の流れは次のとおりです:リクエストはサービスレイヤーで受け取られ、Lambda Layerにアクセスします。Lambda Layerはトークンをデコードし、適切なテナントコンテキストを特定して、アプリケーションの動作と消費メトリクスをログに記録します。

Thumbnail 1500

アーキテクチャ的には、それぞれのテナントに対してAPI Gatewayがどれだけのリクエストを処理したか、またはLambdaがどれだけの回数呼び出され、どれだけの時間実行されたかを把握するためにメトリクスを収集したいと考えています。これにより、各テナントのコスト消費について適切な理解が得られます。 テナントユーザーは、それぞれのトークンを使用してAPI Gatewayにコールを行い、認証が完了すると、リクエストはProduct ServiceやOrder Serviceなどのサービスレイヤーに渡されます。

Thumbnail 1510

Thumbnail 1520

Thumbnail 1530

その後、このサービスレイヤーはLambda Layersにアクセスし、Lambda Layerは様々なアプリケーションログと消費メトリクスをテナントIDで表されるテナントコンテキストと共にCloudWatchログにプッシュします。 ご覧のように、これが典型的なアプリケーションログの例で、テナントIDコンテキストが注入されています。このメカニズムにより、SaaSまたはマルチテナント環境内での各テナントの消費量とアクションを特定し、それに基づいてコストを算出することができます。この考え方により、テナントごとのコスト算出が可能になります。

Thumbnail 1550

Thumbnail 1560

Thumbnail 1570

Thumbnail 1580

Thumbnail 1590

次に考慮すべき点は、Noisy Neighborの問題です。一般的なSaaSモデルではTier戦略とスロットリング戦略を採用して、あるテナントが他のテナントの動作に影響を与えないようにしています。これらのTieringとスロットリングの戦略を構築するメカニズムの1つが、Amazon API Gatewayのusage planとusage keysパターンを活用することです。このパターンでは、異なるTierに属するテナントと共有される各種キーを持つusage planを構築できます。これらのusage plan keysは、各テナントがマルチテナントアプリケーションに送信できるリクエスト数に関して、それぞれ異なるスロットリング設定を評価します。

Thumbnail 1610

全体的な実装は次のようになります。API Gatewayにusage plan keyを設定し、それらのusage plan keysに異なるTier戦略を設定できます。最終的に、異なるマイクロサービス層はこれらの設定に基づいてゲートウェイレベルでスロットリングされます。Tier戦略は優れていますが、Premiumティアのユーザーが支払っているサービスを確実に受けられ、環境内の他のTierのテナントの影響を受けないように、専用のキャパシティが必要な場合はどうすればよいでしょうか?

Thumbnail 1660

従来のEC2コンピュートやコンテナコンピュートでは、Premiumティアの顧客やテナント向けに専用クラスターと自動設定を用意する方法や、専用のコンテナクラスターをプロビジョニングする方法がありました。しかし、インフラストラクチャへの直接的な制御を提供しないサーバーレスLambdaでは、Premiumティアのテナントのエンドユーザーが劣悪なサービス体験に直面しないよう、常にキャパシティを確保するにはどうすればよいのでしょうか?Lambdaには2つの概念があります:任意の時点で実行できる同時Lambda関数の数を決定するアカウント同時実行数(デフォルト制限は1,000ですが増やすことができます)と、10秒ごとに1,000の新しい同時Lambda関数というスケーリング率を決定する関数クォータ(この制限は増やすことができません)です。

Thumbnail 1750

Thumbnail 1770

Thumbnail 1800

さらなるスケールが必要な場合はどうでしょうか?例えば、Premiumティアのお客様の1人が非常に大規模なユーザーベースを持っており、Lambdaが標準で提供する1回のバーストあたり1,000のLambda関数スケールを超えるスケーリングが必要な場合です。そこで活用できるのがProvisioned Concurrencyで、Lambda関数を事前にウォームアップし、標準のLambdaスケーリング機能と組み合わせて設定することができます。これにより、マルチテナンシーのシナリオでPremiumのお客様が必要とする追加のスケーリングニーズに対応できます。しかし、テナント固有のパターンに応じてLambda関数をスケールする必要がある場合はどうでしょうか?

Thumbnail 1820

下流のシステムについてはどうでしょうか?例えば、1秒あたり2,000のLambda関数までスケールできたとしても、それらすべてのリクエストを処理するためのデータベース接続数が不足している場合はどうでしょうか?そこで活用できるのがReserved Concurrencyと呼ばれる機能で、これは各Lambda関数に対して同時実行数を制御できます。このReserved Concurrencyコントロールにより、Lambda基盤のアーキテクチャにおけるNoisy Neighbor状況を回避するためのスロットリング制御を定義することができます。

Thumbnail 1850

仕組みについて説明させていただきます。 例えば、Advancedティアのテナント向けにReserved Concurrencyを300に設定した場合、Advancedティアのテナントは300までのLambda関数の実行が可能となり、それ以上は制限されます。その結果、残りのReserved Concurrencyをプラチナのお客様に割り当てることができ、プラチナのお客様には常に一定の処理能力が確保されることになります。Reserved ConcurrencyとProvisioned Concurrencyを組み合わせることで、Usage Plan Keysと共にサーバーレスベースのマルチテナントアーキテクチャにおけるNoisy Neighbor問題を軽減することができます。

Thumbnail 1890

Thumbnail 1910

Lambda関数とアーキテクチャに関する重要なポイントをご紹介します:Amazon Verified Permissionsを使用して認可ロジックを外部化し、ビジネスロジックを汚さないようにしましょう。 Reserved ConcurrencyとAmazon API Gateway Usage Planを使用してNoisy Neighbor問題を制御し、Lambda Layersを活用して適切なロギングとメトリクスの収集を行います。これにより、ビジネスロジックから切り離して、テナントごとの適切なコスト算出が可能になります。

イベント駆動型アーキテクチャの実装:Amazon EventBridgeの活用

Thumbnail 1930

では次に、マルチテナントアーキテクチャにおける様々な統合パターンのアーキテクチャと設計上の考慮点について理解を深めていきましょう。ここで、共同発表者のNishをご紹介させていただきます。ありがとうございます、Anand。聞こえていますでしょうか?はい、完璧です。ありがとうございます。皆さんの中で、SaaSベースのアプリケーションにマイクロサービスを使用している方はどのくらいいらっしゃいますか?もし私が顧客として、今日SaaSベースのアプリケーションをホストする方法を選ぶとすれば、どれを選ぶでしょうか?私は間違いなくマイクロサービスを選びます。独立したスケーリング、より良いリソース活用、容易なデプロイメントといった利点があるからです。これは確実に運用の優位性をもたらします。そのため私たちはマイクロサービスを使用しています。しかし、私たちが直面している課題は、これらをどのように相互に通信させるかということです。

Thumbnail 2000

Thumbnail 2030

SaaSベースのアプリケーションをマイクロサービスベースのアーキテクチャでホストする際に、お客様がよく使用する一般的な統合パターンについて詳しく見ていきましょう。最初は同期型で、送信者がリクエストを受信者に送信します。 受信者がリクエストを処理している間、送信者は待機状態となります。しかし、受信者が失敗した場合はどうなるでしょうか?送信者は自身でリクエストを再試行しなければなりません。また、受信者が下流のサービスに依存している場合はどうでしょうか?その場合も待機が必要です。これらの課題を解決するために、非同期型と呼ばれる別の一般的なパターンがあります。 このパターンでは、送信者と受信者の間に中間者を導入します。送信者はバッファとして機能するキューにメッセージを配置し、メッセージが受信され転送されることの確認応答を得ます。受信者はメッセージを取得して処理を行い、処理が完了すると送信者にメッセージが届きます。

Thumbnail 2060

Thumbnail 2080

Thumbnail 2090

最後はパブリッシュ・サブスクライブパターンで、パブリッシャーがメッセージを送信し、 ルーティングロジックを使用して複数の信頼できる相手に配信します。これは2番目のアーキテクチャである非同期型のポーリングベースのメカニズムとは異なり、プッシュベースのメカニズムで動作します。それでは、アーキテクチャの前半から 後半に進めていきましょう。私たちはProduct ServiceとOrder Serviceを見てきましたが、アーキテクチャをFulfillment ServiceとShipment Serviceにまで拡張すると、これが可能な実装の一つとなります。 Fulfillment Serviceでは、ピッキングリストを作成し、注文が梱包可能であることを確認する必要があるかもしれません。そこで、注文作成イベントを受信し、EventBridgeを通じてSQSキューに渡され、AWS Lambdaがメッセージを取得します。

Thumbnail 2130

Lambdaは出荷注文イベントを生成し、それがShipmentサービスに渡されます。このサービスには、Step Functionsを通じて実装された独自のワークフローがあります。SQSに関してお客様からよくいただく質問には、キューを異なるリソース間で共有すべきかどうか、あるテナントが他のテナントより多くのメッセージを生成した場合どうなるのか、運用の複雑さやエラー処理にどう対応するか、そして重要なポイントとして、データの分離をどのように管理するか、といったものがあります。これらの課題について順を追って説明していきましょう。

Thumbnail 2150

まず、Noisy Neighborの状況について見ていきましょう。スライドを見ていただくと、テナントAが他の2つのテナントと比べてより多くのメッセージを生成していることがわかります。ここで、テナントCがプレミアムティアに属していて、そのメッセージを優先的に処理したい場合を考えてみましょう。この場合、テナントCはキューの前にある全てのメッセージが処理されるまで待たなければなりません。お客様は、コスト効率を維持しつつテナント間の完全なデータ分離を確保しながら、このような状況を避けたいと考えています。

Thumbnail 2200

一つの解決策は、テナントのティアに基づいて個別のキューを設定することです。例えば、テナントAとテナントBがスタンダードティアに属している場合、彼らのために1つの別個のキューを用意できます。テナントCがプレミアムティアに属している場合は、別のキューを用意することで、スタンダードティアのメッセージの影響を受けることなく、プレミアムティアのテナントのメッセージを優先的に処理することができます。

Thumbnail 2250

Noisy Neighborのレート制限による制御に関して、現在お客様が多く活用している人気のメカニズムの一つは、送信者にできるだけ近い位置でレート制限を実装することです。例えば、マイクロサービス内で異なるテナントから複数の注文作成メッセージを受け取る場合、特定のテナントに対して1秒あたり何件のリクエストを許可するかというスロットルをチェックするメカニズムを実装できます。Amazon API Gatewayのユーセージプランを使用している場合、ティアをユーセージプランに関連付け、ゴールドティアでは1秒あたり300リクエストといった具合に、スタンダードティアではそれより低いレートというように、具体的なリクエストレートを定義することができます。

Thumbnail 2320

Thumbnail 2350

Lambda関数とSQSを設定する際、1回の呼び出しで処理されるメッセージ数を決定するバッチサイズという属性があります。デフォルトでは、バッチサイズは10に設定されています。しかし、これにより異なるテナントのメッセージが同じバッチで処理される可能性があります。コードにバグがある場合、あるテナントが他のテナントのデータにアクセスしてしまうリスクが生じる可能性があります。デフォルトのバッチサイズが10の場合、これらのメッセージは全て1回の呼び出しで処理されることになります。

Thumbnail 2360

クロステナントのデータアクセスを防ぐために、バッチサイズを1に設定することで、1回の呼び出しで特定のテナントに属する1つのメッセージのみを受け取るようにすることができます。ただし、この方法にはデメリットもあります。複数のテナントがいて、メッセージ数が多い場合、キューにバックログが発生する可能性があります。このアプローチは、小規模なテナントや、メッセージ数が少ないシナリオに適しています。

Thumbnail 2400

データの分離とセキュリティに関して、メッセージをSQSに配置する際、 どの送信者がメッセージを書き込めるかを制御するResource-based Policyという機能が標準で提供されています。

Thumbnail 2410

SQSが提供するもう1つの重要な機能として、データ分離のロジックをビジネスロジックから切り離すのに役立つMessage Attributesがあります。Message Attributesには、パラメータとメッセージの内容を含むメッセージボディが含まれます。下流のサービスがそれらのコンテキストにアクセスできるように、テナントIDやテナントコンテキストを保持するようにAttributesを設定する必要があります。これにより、アーキテクチャの最後のコンポーネントに書き込まれるまで、メッセージの旅路全体を通してコンテキストが一貫して維持されます。

Thumbnail 2450

Thumbnail 2460

ここで、SQSのベストプラクティスについて説明しましょう。 マルチテナントアーキテクチャでは、Gold、Platinum、Standardなど、異なるテナント層があります。SQSのDelay Queue機能を活用すると便利です。SQSにメッセージを送信する際、メッセージごとにDelay in Secondsという属性を設定できます。プレミアム層のメッセージには遅延を非常に短くするか、ゼロに設定し、Standard層には意図的に遅延を追加することができます。

Thumbnail 2500

Thumbnail 2530

Thumbnail 2560

In-flightメッセージは必ず避けるべきです。これは、受信者が受け取ったものの、キューから削除していないメッセージのことです。メッセージがキューから削除されないと、バックログが発生します。そのため、受信者やコンシューマーが処理後にメッセージを確実に削除するようにしましょう。 受信者がエラーに遭遇して処理できないメッセージには、Dead-letter Queueを使用します。SQSでMessage Receive Countパラメータを設定することで、処理されずにキューに戻ってくるメッセージの回数を追跡できます。その後、そのメッセージを分析用の別のDead-letter Queueに移動することができます。 SQSキューをテナント間で共有する場合は、Message Attributes機能を活用してテナントコンテキストを渡すことを検討すべきです。

Thumbnail 2580

Thumbnail 2630

例え話をさせていただきます。飛行機に乗る際、手荷物をハンドリングチームに預けますが、その際、荷物が航空機に積み込まれるまで待つように言われることはありません。代わりに、システム内で手荷物預かりのイベントが作成され、セキュリティチェックに進むことができます。もし荷物が航空機に到着するまで待たなければならないとしたら、フライトに乗り遅れてしまうかもしれません。 同様に、アプリケーションのEvent-drivenアーキテクチャでは、注文作成イベントを受け取った際、これらのイベントは意味のある対象先に振り分けられます。どの関係者も他の関係者を待つことなく、レスポンスが処理され、完了すると通知が行われます。

Thumbnail 2660

このアーキテクチャを支えているのが、フルマネージド型AWSサービスのAmazon EventBridgeです。イベントマッチングパターン(ルールに記述されたロジック)に基づいて、イベントを異なるターゲットにルーティングする機能を提供します。マッチするものがあれば、そのイベントはターゲットにマッピングされます。お客様からよく質問されるのは、テナントごとに別々のルールを持つべきか、すべてのテナントに同じルールを使用すべきかということです。

Thumbnail 2710

Thumbnail 2720

これに対する答えは、様々な要因によって変わってきます。この例で説明しましょう。 ここにOrder Serviceからのイベント例があり、このイベントはGoldティアに属し、Tenant IDは一意の識別子となっています。 Amazon EventBridgeのルールでは、ティアに基づいてイベントをマッチングするように設定できます。Goldメッセージに属することを設定すると、イベントは処理されてFulfillment Serviceにルーティングされます。テナントが同じルーティングと処理ロジックを持っている場合は、複数のテナントを1つのルールにまとめることができます。しかし、異なるルーティングや処理ロジックを必要とするテナントがある場合は、テナントごとに別々のルールを持つことが有益です。ほとんどの場合、共有ルールで対応できます。

Thumbnail 2790

Thumbnail 2800

時間の経過とともにルール数を最適化していきましょう。SaaSアプリケーションが成長すると、テナント数が増加し、ルールやリソースの数が制限に達するまで増え続けるため、時間とともに複雑になる傾向があります。 例を見てみましょう。テナントAからのイベントでは、Shopping Cartサービスでタイムアウトエラーが発生しています。 テナントBからの別のイベントでは、別のマイクロサービスであるInventoryサービスから在庫切れを示すエラーが発生しています。これら2つのイベントの共通点は、どちらもエラーだということです。アーキテクチャとSaaSプラットフォームが成長しても、同じルールを使用できる共通パターンを認識することで、最適化の機会を見出すことができます。

Thumbnail 2830

Thumbnail 2840

Amazon EventBridgeには、ワイルドカードパターンマッチングという標準機能があります。 ワイルドカードパターンマッチングでは、異なるテナントからのイベントを定義します。この例では、すべてのエラーメッセージを1つのCloudWatch Log Groupで処理したい場合があります。ワイルドカードパターンマッチングを使用してEventBridgeルールを設定することで、異なるテナントやマイクロサービスからのエラーメッセージを1つのCloudWatch Log Groupに送ることができます。".error."がワイルドカードで、パターンマッチングの仕組みに基づいて、"error"を含むすべてのメッセージが適切にルーティングされます。SaaSベースのアプリでエラーが発生した場合にインシデントを発生させたり、何が問題なのかを分析するために単一のCloudWatch Log Groupにメッセージを送信したりすることができます。

Thumbnail 2900

Thumbnail 2910

このアプローチを実装すると、 オペレーショナルエクセレンスを促進し、ルール管理を削減することができます。 場合によっては、専用のEvent Busが必要になることもあれば、共有Event Busが適している場合もあります。コンプライアンス規制やセキュリティの要件により、個別のバスが必要なコンポーネントやテナントには、専用のEvent Busを使用できます。ただし、大多数のケースでは共有Event Busを使用することができます。両方のアプローチの利点を組み合わせるために、SaaSベースのアプリケーションではブリッジモデルを実装することができます。

Thumbnail 2950

Thumbnail 2990

マルチテナントアーキテクチャをホストする際のEventBridgeに関するベストプラクティスと重要なポイントについて説明しましょう。 まず、サブスクライバーごとに1つのルールを使用することで、ルールパスのパターンマッチングが容易になります。変更を加える場合、意図したターゲットにのみ影響を与え、他のターゲットには影響を与えません。1つのルールに5つの異なるターゲットがあり、ルーティングメカニズムやパターンマッチングを変更すると、他のターゲットやメッセージの受信方法に影響を与える可能性があります。 もう1つの重要な実践は、カスタムアプリケーションではデフォルトのEvent Busを使用しないことです。EventBridgeには、デフォルトとカスタムの2種類のEvent Busがあります。AWS Amazon EventBridgeコンソールにログインすると、デフォルトのEvent Busが表示されます。

Thumbnail 3040

Thumbnail 3050

カスタムアプリケーションの場合は、カスタムEvent Busを作成する必要があります。デフォルトのEvent Busは、インスタンスの停止、実行、削除といったAWSネイティブサービスからのイベント、つまりCloudTrailからのこのようなAPIコールを受け取るように設計されており、自動的にデフォルトEvent Busに転送されます。独自のビジネスアプリケーションを使用する場合は、Event Busやイベントが混在しないように、カスタムEvent Busを使用すべきです。 ワイルドカードパターンマッチングは可能な限り使用してください。これによりルールの数と管理が削減できます。 プールモデルを使用する場合、1つのEvent Busを複数のテナント間で共有することになります。制限に注意を払い、制限に達する可能性がある場合は、制限引き上げのリクエストを検討してください。ハード制限の場合は、別々のキューや別々のEvent Busを持つことができます。

まとめと今後の学習リソース

Thumbnail 3080

このセッションの重要なポイントは、スライドを通じて議論した内容の核心を表しています。 アーキテクチャ設計は非常に重要です。チームは、SaaSベースのアーキテクチャに対してブリッジ、プール、サイロのどのモデルを選択するかを十分な時間をかけて決定する必要があります。今日の決定は今後数年にわたってアプリケーションをホストすることになるため、すべての要因を考慮する必要があります。データの分離をビジネスロジックから外部化する必要があります。SQSについては、メッセージ属性がメッセージ本体とは異なることについて説明しました。Lambdaの場合はLambda Layersを使用し、API Gatewayの場合はLambda Authorizerを使用してテナント固有のコンテキスト関連データを生成します。

Thumbnail 3140

Thumbnail 3170

Thumbnail 3180

コスト最適化には、Lambda Layersを使用して SQSや前のサービスからコンテキストを取得し、これを処理することでテナントごとのコストを計算することができます。送信者やサービスを待たせたくない場合は、可能な限りメッセージを非同期で処理することをお勧めします。これにより時間を削減でき、最終的にコストの節約につながります。Noisy Neighborの問題を避けるために、 レート制限の実装、Lambdaの予約同時実行数の使用、そしてSQSで説明したような複数のティアに基づくティアベースの戦略を使用することができます。 スケーラビリティとアジリティに関しては、サーバーレスを使用することでインフラストラクチャの管理を心配することなくアーキテクチャをホストできます。さらなる最適化の余地があり、ローコードサービスに焦点を当てることができます。異なるサービスからのイベントストリーミングデータを処理するためにEventBridge Pipesの使用を検討し、アーキテクチャ間のフローをオーケストレーションするためのローコードまたはノーコードサービスであるStep Functionsの使用も検討できます。

Thumbnail 3220

Thumbnail 3250

こちらは、Serverlessコミュニティに所属している方や、ServerlessやAWSネイティブのServerlessサービスについての知識を深めたい方にお勧めのセッションです。後で参照できるよう、写真を撮っていただいても構いません。Serverlessの学習を続けるために、AWSのエキスパートが講座を収録しているAWS Skill Builderの活用をご検討ください。また、ServerlessやEvents and Workflowsに関するバッジの取得も可能です。私自身もこれらのバッジを取得し、Skill Builderの講座を受講しましたが、そこでの内容は非常に充実していました。

Thumbnail 3280

Thumbnail 3310

AWS Lambda Powertoolsについてですが、これは可観測性やモニタリングのベストプラクティスを実現するための独自のライブラリです。構造化ログ、分散トレーシング、またはアーキテクチャでのべき等性を、すべてのロジックを自分で書くのではなく、少ない行数のコードで実現したい場合は、Powertoolsを活用できます。以上で本日の内容は終了となります。お時間を取っていただき、ありがとうございました。素晴らしいセッションになったことを願っています。この後10-15分ほど、Anandと私はここに残っていますので、ご質問がありましたらお答えいたします。また、会場内でも見かけた際はお声がけください。皆様、ありがとうございました。


※ こちらの記事は Amazon Bedrock を利用することで全て自動で作成しています。
※ 生成AI記事によるインターネット汚染の懸念を踏まえ、本記事ではセッション動画を情報量をほぼ変化させずに文字と画像に変換することで、できるだけオリジナルコンテンツそのものの価値を維持しつつ、多言語でのAccessibilityやGooglabilityを高められればと考えています。

Discussion