マルチテナントの可観測性を向上させる「SaaS survivor: Building a rich multi-tenant operations experience」に参加しました #SAS403 #AWSreInvent

2023.12.03

いわさです。

AWS re:Invent 2023 でワークショップ「SaaS survivor: Building a rich multi-tenant operations experience」に参加しました。

ワークロードで問題が生じていないかを確認するため、通常は運用ダッシュボードで可視化を行い、アラームなどを設定します。
しかし、マルチテナント SaaS アプリケーション(特にプール型)では複数テナントの情報が混在するため、通常は SaaS アプリケーション全体で問題が生じているのかを確認することは出来ても、具体的にどのテナントで問題が生じているのかを把握することが困難です。
そこで、SaaS on AWS ではテナントコンテキストを用いてこの問題を解決することが推奨されています。

このワークショップではテナントコンテキストの実装例や、それによって可観測性が向上する様子を体験することが出来ます。
さらに、そこから得た洞察をもとにテナント分離を行い、クロステナントアクセスが生じているかどうかを運用チームが把握するための方法も学ぶことが出来ました。

会場の雰囲気

ワークショップの時間帯が 11:30 ~ 13:30 ということもあり、会場へランチボックスを持ち込み昼食を取りながら参加している方が多かったです。そ、その手があったか...!

ワークショップの概要

re:Invent 2023 公式サイトのワークショップ概要を Amazon Translate で翻訳した内容が以下となります。

  • マルチテナント型SaaSサービスの構築は仕事の一部に過ぎません。環境がテナントでいっぱいになっても、システムの状態を分析、トラブルシューティング、評価するための豊富で先を見越したツールとメカニズムが必要です。
  • このワークショップでは、一連の運用イベント (ノイズの多い隣人、隔離違反など) をシミュレートして説明し、個々のテナントや階層のアクティビティを追跡および分析するために必要なツールを作成する手順を説明します。
  • マルチテナント環境で表面化する可能性のある運用上の課題について学び、この経験をサポートできるテナント対応の運用メカニズムを構築する方法を探ります。

スピーカー

  • Dave Roberts
  • Zp Bappi

LEVEL

  • 400

ワークショップの内容

ワークショップ開始時点で、次のようなマルチテナント SaaS アプリケーションが構築済みです。
Basic プランテナントがプール型で、上位プランテナントがテナントごとのサイロ型が混在している、Tier 分離型ですね。

そして運用チームは以下の実装済みの CloudWatch ダッシュボードを使ってワークロードに問題が生じていないかを観測することが出来ます。

上記の赤枠部分を見てみると、ワークロードでエラーとスロットリングが少し発生していることを把握することが出来ます。

しかし、このダッシュボードからはテナントごとの情報が把握出来ないので、どのテナントが正常で、どのテナントに問題が生じているのかを把握することが難しいです。

テナントコンテキストの実装

まず最初のステップとして、ワークロードにテナントコンテキストを挿入します。
具体的にはログやメトリクスに、どのテナントによって発生したログ・メトリクスなのかを設定し、その情報を使ってテナントごとの情報が把握できるようなダッシュボードを構築します。

ログへの挿入

ワークショップのアーキテクチャーの場合はカスタムオーソライザーを使った API Gateway にリクエストが送信されます。

このワークショップではカスタムオーソライザー上で JWT トークンクレームからトークン情報を抽出し、カスタムコンテキストとして設定しています。

こうすることで、API Gateway のカスタムアクセスログのフォーマットをカスタマイズする際に、テナントコンテキストをログ出力することが出来ます。

また、API Gateway に統合された Lambda 関数のログ出力部分にもテナントコンテキストの出力を追加しています。

最後にメトリクスフィルターを使って、テナントごとの情報をメトリクスとして出力します。

メトリクスへの挿入

Lambda 関数でカスタムメトリクスを出力している部分があります。
ここはメトリクスのディメンションとしてテナントコンテキストを設定します。

インサイトを得る

このワークショップでは変更後のダッシュボードをスクリプトで自動生成することが出来ます。
コードを確認することで CloudWatch ダッシュボードの作成方法は参考に出来ると思いますが、ここでのポイントはテナントコンテキストを挿入して可視化出来てるかという点なので割愛します。

新しいダッシュボードからテナントごとにエラーカウントやスロットリングカウントを把握出来るようになりました。
これによってプール型、つまりベーシックプランのテナント 3 でエラーが多く発生しており、ノイジーネイバー化しつつあるというインサイトを得ることが出来ます。

先程まではどのテナントが問題を引き起こしているのか確認出来なかったと思いますが、テナントコンテキストを挿入することによってプール型でもテナントごとの分析が出来るようになりました。

ノイジーネイバー対策

テナントごとのインサイトが得られるようになりましたが、特定のテナントがリソースを使用しすぎて他のテナントのユーザーエクスペリエンスに影響が生じる恐れがあります。ノイジーネイバーという問題です。

ここではノイジーネイバー問題と、ベーシックプランのパフォーマンスが高すぎる問題に対処するために、ベーシックプランのテナントに使用制限を設定します。

具体的には今回のアーキテクチャーだと API Gateway の使用量プランと、Lambda の予約済み同時実行数を使用します。

実装済みの使用量プランと Lambda の予約済み同時実行数を調整するだけではありませんが、ポイントとしては、ベーシックプランのスロットル設定を微調整することで次のようにスロットリングを発生しやすくさせることが出来ました。

これによってプレミアムプランへのアップグレードのインセンティブと高めるという目的もあります。

テナント分離テスト

テナント分離の実装はこのアーキテクチャーでは実装面には触れていませんが、運用性を高めるためにはテナント分離性について評価することが推奨されています。

ワークショップ内では具体的に、Jest に基づいたテナント分離性のテストの確認を行いました。

以下では Cloud9 上で実行されていますが、CI/CD パイプラインへの組み込みも推奨されていました。

ここでは、テナント分離モデルが期待どおりに機能することをテストできました。

テナント分離の監査

テナント分離を使用してテナントが他のテナントのデータにアクセスできないようにすることは、SaaS の基本的な概念です。したがって、期待どおりに動作するかどうかをテストすることが重要です。

ここでは実際に運用を行っている際にテナントが別のテナントのデータにアクセスしようとする違反が発生した時に、運用チームへ警告通知を行う機能を実装しました。

テナント分離の完全性を証明できることは非常に価値があります。
たとえば、コンプライアンス上の理由から監査が必要な場合があります。さらに、高度に規制された環境で顧客に販売する際に、セキュリティ体制を実証するのにも役立ちます。

このワークショップ内ではアプリケーションが Lambda で、データストアが DynamoDB です。
DynamoDB へアクセスする際に、Lambda 関数は対象テナント情報で Assume Role を行います。

これによって、STS のログと DynamoDB のデータイベントログに対して、同一アクセスキーでテナント ID が異なる情報を Athena で抽出することが出来ます。

with sts as (
    select
        eventid as sts_eventid,
        json_extract_scalar(requestparameters, '$.principalTags.tenantId') as sts_tenantid,
        json_extract_scalar(responseelements, '$.credentials.accessKeyId') as accesskeyid,
        eventtime as sts_eventtime
    from "management_event_logs"
    where
        eventsource = 'sts.amazonaws.com'
),
ddb as (
    select
        eventid as ddb_eventid,
        useridentity.accesskeyid as accesskeyid,
        json_extract_scalar(requestparameters, '$.key.shardId') as ddb_shardid,
        substring(json_extract_scalar(requestparameters, '$.key.shardId'), 1, 32) as ddb_tenantid,
        json_extract_scalar(requestparameters, '$.tableName') as ddb_tablename,
        errorcode as ddb_errorcode,
        eventtime as ddb_eventtime,
        eventname as ddb_eventname
    from
        data_event_logs
)
select * from ddb
left outer join sts on ddb.accesskeyid = sts.accesskeyid
where ddb_tenantid != sts_tenantid

このワークショップで実際に確認してみると、次のようにクロステナントアクセスが数件発生していることが出来ました。

まずは Athena で確認しましたが、SaaS 運用で活用するには、このプロセスを自動化する必要があります。

ワークショップ内ではこのクエリを自動実行して検出時に通知する仕組みを作ります。
StepFunctions を使って、定期的な Athena でのクエリ実行と、取得した違反件数をカスタムメトリクスとして CloudWatch へ送信します。

あとは運用チームはダッシュボーへ可視化したり、あるいは CloduWatch アラームを設定して違反の検出が可能になります。

さいごに

本日は「SaaS survivor: Building a rich multi-tenant operations experience」のワークショップ内容を紹介させて頂きました。

SaaS on AWS でもマルチテナントの運用性や、クロステナントアクセス監査については概念としてはよく出てきます。
しかし、具体的にどのようにテナントコンテキストを挿入するのか、監査をどう行うことが出来るのかについて触れているドキュメントはかなり少ないと思います。

このワークショップでは、ふわっとした SaaS on AWS の概念から一歩踏み込んだ具体的な実装アプローチを学ぶことが出来ました。
かなりお勧め出来るコンテンツですので、ワークショップスタジオなどに追加された際は是非試してみてください。