株式会社アンチパターンの穴田です!こんにちは!
本記事は、
2025 Japan AWS Jr. Champion Qiitaリレー夏
2本目の投稿になります↓
弊社ではマルチテナントSaaSのインフラについて考える機会がなかなか多いのですが、今回はその中で考えたことをアウトプットしていきたいと思います。
本来はたくさんのサービスを組み合わせてSaaSは作られますが、今回は簡単のためEC2で組み立てていきます。
(説明がやや長いので、「ふーん」という気持ちで読んでくれると嬉しいです!LLMといっしょに読むのもオススメです。)
①プール型
マルチテナントSaaS(以下、単にSaaSと呼びます)には複数のデプロイモデルがあります。
その中でも、複数のテナント(SaaSの契約単位のこと)が共通のリソースを使うデプロイモデルをプール型と呼びます。

この場合は非常にシンプルで、アプリケーションサーバー(= EC2)でテナントを識別し、データアクセスを制御すればいいわけですね。
シンプルな代わりにもちろん課題もあります。あるテナントの利用比率が大きいとき、他のテナントが利用する際のパフォーマンス低下につながるというノイジーネイバー問題がその1つです。
②サイロ型 - ALBで振り分け
プール型に対して、テナントごとにリソースが分離されているモデルをサイロ型といいます。
サイロ型になると論理的なデータアクセス制御だけではダメで、実際にネットワーク的なルーティングが必要になってきます。
その例として、まずは想像しやすいALBでの振り分けを見ていきましょう。

上の図で起こっていることをまとめると、
- EC2は各テナントごとに用意されている
- ユーザーはテナントの識別子(テナントID)を含めてリクエストする
- ALBは送られたテナントIDをもって適切なEC2へルーティングする
- 送られてきたテナントIDが正しい保証はないので、EC2では必ず認証を行う
になります。(※ALBをテナントで共有しているので、厳密にはサイロ型ではないという見方もできます。)
ただし、
- 新しくテナントが増えたとき、ALBのルールやターゲットグループを都度書き足す必要がある
- → テナントオンボーディングの冪等性がなくなる
という問題があります。テナントオンボーディングの冪等性がなくなるというのはイメージが難しいですが、
- テナント追加時にALBのルール追加やターゲットグループ追加が必要である
- → しかしCloudFormationやCDKなどで追加処理は表現しづらい
- → APIなどを使って直接ALBを変更してしまう
- → テナント削除時やデプロイ失敗時にロールバックが難しくなる
というようなイメージです。
冪等性が欲しいな、ということで生まれたのが次の構成です。
③サイロ型 - ALBもサイロ化
ALBを変更するのが冪等性を失う原因だったので、テナントが増えたらALBごと増やしてしまおうというのが以下の構成です。

この構成の使い方としては、
- テナントごとにALB・EC2のペアをデプロイ
- テナントごとにドメインを作成し、ALBに紐づけ・Route 53へ登録
- 各テナントは該当するドメインにアクセスし、EC2では本当にテナントが正しいかどうかを認証する
となります。
この構成では
- 当初の狙い通り、CloudFormationやCDKなどを使って(冪等性を保ったまま)テナントオンボーディングを実行することができる
- 同じIaCをテナントが増えるたびに実行するイメージ
- IaC実行時にテナントIDを渡すような(ステートレスな)実装をすると実現できる
- Auto Scaling group の設定によっては、各テナントごと別々にスケーリングできる
というメリットがあります。
ただし、お気づきかと思いますが、各テナントごとにALBを用意するのではコストがかさんでしまうという課題も新たに出てきます。
④自前でルーティング
- テナントごとにALBを置きたくない
- だけどもデプロイの冪等性を犠牲にしたくない
ということで編み出されたのが以下の構成です。

要点だけまとめるとLambdaがテナントを識別し自前でルーティングする構成となるのですが、詳細化すると
- 事前にCloudFront、Lambda、Route 53 Private Hosted Zoneをデプロイ
- テナントが増えたタイミングで
- テナント用のEC2を作成
- EC2のプライベートIPとテナントIDのマッピングをRoute 53 Private Hosted Zoneに保存
- ユーザーのリクエストがあるたびに
- Lambdaが認証を行いテナントIDを取得
- テナントIDをもとにRoute 53 Private Hosted ZoneからEC2のプライベートIPを参照
- LambdaがEC2へリクエストし、その結果を返す (リバースプロキシのような動き)
というような構成です。
Route 53 Private Hosted Zoneにマッピングを登録することで、IaCでのデプロイの冪等性が保たれるという工夫になっています。
要望が多い分デメリットも多く、
- IaCが2つになってしまう (元から存在するリソース用と、テナントごとのリソース用)
- LambdaがEC2のレスポンスを待つ時間もコストになる
- EC2のスケーリングや可用性が課題になる
- そもそもルーティングの処理を自前で書く必要がある
といったものがあります。
まとめ
ここまでEC2を主軸にしてSaaSの構成を見てきましたが、どうでしたか?
どの構成も一長一短で、結局はトレードオフだなということが感じられたのではないでしょうか。
実際のSaaS開発では、
- より多くの要望
- より多くのサービス
- より多くのトレードオフ
と向き合っていく必要があります。
考えることは多いですが、その分楽しみながら、よりよいアーキテクティングができるよう頑張っていきましょう…!