Amazon Web Services ブログ

AWS Lambda の予約済同時実行数の設定によるテナント多層化戦略の実装

AWS Lambda の機能である予約済同時実行数の設定は、Lambda 関数の同時に実行されるインスタンスの最大数を保証します。

この記事では、この機能を活用してマルチテナントの SaaS (Software-as-a-Service) アプリケーションの多層化戦略を定義する方法を探り、実装例について説明します。AWS Lambda 関数を使用するメリットはたくさんあります。SaaS (および一般的なサービス) をサーバーレス構成でデプロイすることで、アーキテクチャと運用業務が簡素化され、付加価値を生まない重労働を軽減できます。

アマゾンウェブサービス (AWS) 上 のサーバーレス SaaS についてさらに知りたい場合は、AWS サーバーレスサービスによるマルチテナント SaaS ソリューションの構築 のブログ記事をチェックしてください。

それでは、予約済同時実行数の設定が SaaS アプリケーションにどう役立つのか考えていきましょう。

AWS 上の SaaS における多層化戦略

例えば、ある SaaS プロバイダー A 社では e コマースプラットフォームをホスティングしています。テナントの 1 つである M 社は、このプラットフォームを通じて携帯電話を販売しています。一部のモデルはすぐに人気を博し、トラフィック量が増え、M 社では注文処理が失敗するようになりました

M 社は問題を解決するために A 社にエスカレーションしました。原因を調査したところ、複数のテナントでコンピューティングリソースが共有されており、現状のキャパシティでは、すべてのリクエストを処理するには不十分であることがわかりました。

A 社は、M 社が他のテナントに比べてはるかに多くのキャパシティを必要とし、他のテナントのパフォーマンスにも影響を与えている状況も確認しました。この状態は、 SaaS アプリケーションによく見られる問題で、「ノイジーネイバー」とも呼ばれます。

この問題を解決するには、テナントの多層化戦略がポイントになります。例えば、A 社は、M 社のような要求の厳しいテナント向けにプラチナ層 (特別層) を定義します。プラチナ層に定義されたテナントには、より多くのコンピューティングリソース、より大きなストレージ容量、または、その他のリソースを割り当てることと引き換えに、他の層のテナントよりも高い金額を請求します (図 1 はこのパターンを示しています)。

図1 – AWS の SaaS における多層化戦略

では、テナントの多層化戦略を機能させるにはどうすればよいのでしょうか?コンピューティングリソースにフォーカスして考えていきます。SaaS アプリケーションが Amazon Elastic Compute Cloud (Amazon EC2) をベースにしている場合、(プラチナ層の) テナントごとに専用の EC2 インスタンスを作成するか、テナント専用の自動スケーリンググループを作成することで簡単に実現できます。

Amazon Elastic Container Service (Amazon ECS) または Amazon Elastic Kubernetes Service (Amazon EKS) を使用している場合は、テナントごとに専用クラスターを作成できます。

Lambda の場合は、 ソリューションが異なります。Lambda はサーバーレスサービスであり、基盤となるインフラストラクチャをお客様 (例では SaaS プロバイダー A 社) が制御することはできません。

この場合、 予約済同時実行数の設定が多層化戦略を実装する手段となります。この機能を活用することで、基盤となるリソースを管理しなくても、プラチナ層のテナントに専用のコンピューティングキャパシティを割り当てることができます。

予約済同時実行数の設定によるテナントの多層化戦略

Lambda 関数は呼び出されると同時に、コンピュートインスタンスが割り当てられます。このインスタンス上で関数が実行され、イベントが処理されます。最初の Lambda 関数が実行されている間に再度関数が呼び出されると、別のインスタンスが割り当てられ、同時実行数が増加します。

Lambda 関数のスケーリングの詳細については、ドキュメントをご確認ください。e コマースを例に挙げると、1,000 件の注文が同時に処理されると予想される場合、1,000 個の注文処理関数がインスタンス化されます。

アカウントやリージョン毎に同時実行できる Lambda 関数の数は決められています。そのため、予約済同時実行数が設定されていない場合、他の関数が利用可能な同時実行数をすべて使い果たしてしまう可能性があります。関数に予約済同時実行数を適用することで、常に設定した最大数の同時実行関数インスタンスを保証することができます。

したがって、「注文処理」用の Lambda 関数があり、その関数の「予約済同時実行数」を 100 に設定すると、Lambda は他の関数の実行数に関係なく、100 件の同時リクエストを常に処理できるようになります。同時実行数の制限を定義するだけでは料金は発生しません。実際に処理されたリクエストに対してのみ料金が発生します。

この機能を活用すると、プラチナ層のテナント専用の関数定義を作成し、その関数に予約済同時実行数を設定することでテナントの多層化を実現できます。たとえば、「注文処理」用の Lambda 関数の場合、M 社用に「Tenant-M-order-processing-function」として個別に関数を定義し、同時実行数を予約できます。

予約済同時実行数は、テナントごとの需要と価格に基づいて設定でき、他の関数にも同様に実装できます。プラチナ層のテナントごとに、このアプローチを利用して専用のコンピューティングリソースを作成できます。

図 2 は、テナント PT1 とテナント PT2 のすべての機能に予約同時実行が適用される実装を示しています。

図2 – 予約済同時実行数によるテナントの多層化戦略

テナントの多層化戦略を定義する際は、より適切に計画を立てるためにいくつかの知っておくべき情報があります。1 つ目は、デフォルトで 1000 に設定されている Lambda 関数の「同時実行数の制限」です。この制限は、アカウントごとおよびリージョンごとに設定されています。また、必要に応じて、数万に増やすことができるソフトリミットでもあります。すべてのテナントに対して、常にすべての機能を実行するのに十分な同時実行クォータを確保するためには、クォータの引き上げリクエストが必要になる場合もあります。

もう1つ考慮すべきことは、「同時実行数の上限」です。いずれかのテナントのリクエスト負荷が上限を超えた場合、リクエストは自動的にスロットリングされます。これにより、テナントのリクエストが制限される可能性があります。その場合は、テナントをより上限を広げた層に変更するか、割り当てられた層自体の制限を増やすといった対処が必要になります。

一方で、マルチテナントシステムの場合、リクエストの自動スロットリングにより、ノイジーネイバーを制御することが可能になります。

予約済同時実行数の設定の詳細については、AWS Lambda 開発者ガイドの一部として提供されているウェブリファレンス「予約済同時実行数の設定」をご覧ください。予約済同時実行数の設定の基本的な考え方について説明したところで、ソリューション全体がどのように機能するかを見ていきましょう。

AWS Lambda の予約済同時実行数の設定による多層化の実装

この実装例では、プラチナ層のテナント専用のコンピューティングスペースが作成されています。このソリューションは、AWS SaaS Factory チームによって構築されたサーバーレス SaaS リファレンスソリューションの一部として実装されています。

まずリファレンスソリューションの概要について説明します。このソリューションは、SaaS 開発者やアーキテクトに実用的なコードを提供するために構築されたもので、AWS Lambda、Amazon API GatewayAmazon CognitoAmazon DynamoDBAWS CodePipeline などのサーバーレステクノロジーを使用して、AWS 上でマルチテナント SaaS ソリューションを設計して提供する方法を示しています。

このソリューションには、オンボーディングとアイデンティティ、テナントとユーザーの管理、認証と認可、データパーティショニング、テナント分離、自動デプロイメント、マルチテナントのオブザーバビリティなど、マルチテナントに関するさまざまな考慮事項が含まれています。

図 3 –サーバーレス SaaS リファレンスアーキテクチャ

予約済同時実行数の設定を利用したテナントの多層化戦略は次の 3 つのステップで実装できます。

  1. Lambda 関数と API の作成:プラチナ層のテナント用に Lambda 関数と API 定義 (API Gateway) を作成します。
  2. テナント固有のルーティングの実装:プラチナ層のテナントのリクエストをテナント固有のリソースにルーティングします。
  3. テナント固有のポリシーの実装:テナント固有の機能を不正アクセスから保護します。

Lambda 関数と API の作成

このリファレンスソリューションは、e コマースの SaaS ソリューションの一部として、注文サービスと製品サービスによって実現されています。これらのサービスを Lambda 関数で処理します。例えば、注文サービスは OrderCreateFunction、OrderGetFunction、OrderDeleteFunction などの Lambda 関数で構成されています。

AWS CodePipeline を使用して、AWS CloudFormation テンプレートからこれらの Lambda 関数をプロビジョニングすることができます。新しくプラチナ層のテナントを登録すると、このパイプラインを使用してテナント専用のコンピューティングリソースが作成されます。

リファレンスソリューションでは、テナントのプロビジョニングは CloudFormation テンプレート (具体的には AWS Serverless Application Model)「tenant-template.yaml」に基づいています。このテンプレートによって、Lambda 関数、DynamoDB テーブル、AWS IAM ロールを含む複数の AWS リソースを作成、または更新できます。

以下は、GetOrdersFunction の関数定義を示すテンプレートの抜粋です。

GetOrdersFunction:
Type: AWS::Serverless::Function
DependsOn: OrderFunctionExecutionRole
Properties:
CodeUri: s3://serverless-saas-pipeline-artifactsbucket2aac5544-149q22ii0usir/07de032a239beb005fad025861857b0b
Handler: order_service.get_orders
Runtime: python3.8
Tracing: Active
Role:
Fn::GetAtt:
- OrderFunctionExecutionRole
- Arn
ReservedConcurrentExecutions:
Fn::If:
- IsPooledDeploy
- Ref: AWS::NoValue
- Ref: LambdaReserveConcurrency
Layers:
- Ref: ServerlessSaaSLayers
Environment:
Variables:
POWERTOOLS_SERVICE_NAME: OrderService
IS_POOLED_DEPLOY:
Fn::If:
- IsPooledDeploy
- true
- false
ORDER_TABLE_NAME:
Ref: OrderTable
AutoPublishAlias: live
DeploymentPreference:
Enabled:
Ref: LambdaCanaryDeploymentPreference
Type: Canary10Percent5Minutes
Alarms:
- Ref: GetOrdersFunctionCanaryErrorsAlarm
Tags:
TenantId:
Ref: TenantIdParameter
Metadata:
SamResourceId: GetOrdersFunction

上記のテンプレートで強調しているように、関数の ReservedConcurrentExecutions 属性によって関数に予約済同時実行数が設定できます。同時実行数の最大値は、 LambdaReserveConcurrency 属性にて設定します。

同様の方法で、getOrderFunction、CreateOrderFunction、UpdateOrderFunction などの Lambda 関数にも予約済同時実行数を定義できます。その他の関数の定義については、Github 上で確認できます。

予約済同時実行数の設定は CloudFormation テンプレートを使用して更新できますが、AWS マネジメントコンソール、 AWS Command Line Interface (CLI)、または AWS Software Developer Kits (SDKs) などの他の方法を使用しても更新できることに注意してください。

テナント固有のルーティングの実装

続いて、プラチナ層のテナントユーザーがそれぞれの関数に正しくルーティングされていることを確認する必要があります。

ここでは、Amazon API Gateway を使用して API 定義を作成します。Amazon API Gateway は、あらゆる規模の API の公開、保守、モニタリング、保護を簡単に行えるフルマネージド型サービスです。

リファレンスソリューションでは、CloudFormationテンプレート「tenant-template.yaml」に API 定義が示されています。プラチナ層のテナント用の API 定義が作成されると、CodePipeline が起動し、DynamoDB テーブル上のテナントのメタデータと API Gateway URL を更新します。

この URL をクライアント側で参照すると、テナント固有のルーティングが容易になります。ソリューションでは、ユーザーインターフェイス (UI) は Angular JS をベースにしたウェブインターフェイスです。認証設定サービス (auth-configuration-service.ts) と呼ばれるコンポーネントがテナント固有の設定を行います。

ユーザーがログインすると、DynamoDB テーブルからテナントのメタデータが取得され、API Gateway URL が「localstorage」属性の1つとして設定されます。これにより、プラチナユーザーのリクエストは適切な API と Lambda 関数にルーティングされます。このコンポーネントの実装の詳細は GitHub で確認できます。

テナント固有のポリシーの実装

テナント固有のルーティングの実装だけでは、テナントの完全分離は不十分です。例えば、テナント A のユーザーがテナント B の Lambda 関数にアクセスできないようにセキュリティポリシーを設定する必要があります。

図 4 – IAMポリシーべースのユーザー認証

このリファレンスソリューションでは、このセキュリティポリシーを Amazon Cognito、IAM、API Gateway を利用して実装しており、すべての API リクエストの検証を行うことが可能です。ここでは、カスタムオーソライザーを活用しています。カスタムオーソライザーは、テナント固有の API リファレンスを含むポリシーを返します。実装の詳細については、カスタムオーソライザーのコードを参照してください。

まとめ

サーバーレス SaaS アプリケーションにおいて、AWS Lambda は基盤となるインフラストラクチャの運用やスケーリングを気にすることなく、伸縮自在なコンピューティングリソースを提供します。また、専用のコンピューティングリソースやテナントを定義する必要もありません。

そして予約済同時実行数の設定は、コンピューティングリソースを分離し、テナントの多層化戦略を定義するアプローチを提供します。

予約済同時実行数の設定を有効にする際は、アプリケーションの需要を満たすのに十分な同時実行クォータが割り当てられていることを確認する必要があります。また、予約済同時実行数の設定には上限があるため、テナントの需要と価格戦略に基づいてその値を決定する必要があります。

この記事では、ソリューションの実装について簡単に説明をしました。ソリューションのより詳細な内容については、GitHub を参照してください。
このソリューションは AWS SaaS Factory チームによってビルドされています。

AWS SaaS Factory について

AWS SaaS Factory は、SaaS 導入のあらゆる段階にある組織を支援しています。AWS を活用した新製品の開発、既存のアプリケーションの移行、SaaS ソリューションの最適化について私たちがお手伝いします。ご興味がある方は、AWS SaaS Factory Insights Hub にアクセスして、技術やビジネスに関するその他のコンテンツやベストプラクティスをご覧ください。

SaaS アプリケーションの開発者は、アカウント担当者に連絡してエンゲージメントモデルについて問い合わせたり、AWS SaaS Factory チームと協力することができます。

サインアップして、SaaS on AWS のニュース、リソース、イベントに関する最新情報を入手してください。

この記事は、シニアパートナーアーキテクトのラル・ヴェルマが執筆しています。日本語訳はソリューションアーキテクトの三宅が翻訳しました。原文はこちらです。