↓ メインコンテンツへスキップ

SaaS Architecture Single-Tenant

シングルテナント vs マルチテナント SaaS:どちらをいつ選ぶか

シングルテナント(マルチインスタンス)とマルチテナントのSaaSは何が違うのか。分離、コスト、運用の比較、その中間の選択肢、どちらをいつ選ぶか、共有データベースでテナントを分ける方法、そして顧客ごとのインスタンス運用を現実的にするツールについてまとめました。

シングルテナント vs マルチテナント SaaS:どちらをいつ選ぶか

シングルテナントのSaaSは、顧客ごとにアプリケーションとデータベースを1セットずつ用意します。マルチテナントのSaaSは、全顧客を1つの共有アプリケーションとデータベースに載せ、テナントIDで区別します。そのチェックはすべてのクエリで必要です。シングルテナントは、分離、顧客ごとの制御、コンプライアンス対応のしやすさと引き換えに、大量のコピーを運用するコストを払います。マルチテナントは安くて運用もずっとシンプルですが、顧客同士を分けておくのはコードの仕事になります。多くのプロダクトはマルチテナントから始め、本当に必要な一部の顧客にだけ専用インスタンスを提供しています。

B2BのSaaSをやっていると、いつか必ずテナンシーの問題にぶつかります。だいたいは、大口の見込み客から「うちのデータは他社と分離できますか?」と聞かれた日です。私もこの議論には何度か立ち会ってきました。答えは基本的に2つ。顧客ごとに専用インスタンスを用意する(シングルテナント・マルチインスタンス)か、全員を1つの共有インスタンスに乗せる(マルチテナント・シングルインスタンス)か。どちらが正解ということはなく、壊れ方が違うだけです。

インフラストラクチャ設計
#

シングルテナント・マルチインスタンス
#

シングルテナント、マルチインスタンス: この構成では、インフラのインスタンスを複数用意して、複数のテナントにサービスを提供します。

こちらの魅力は「分離」に尽きます。メリットはほぼすべてそこから生まれます。顧客のデータがそれぞれ独立した環境にあるので、セキュリティレビューやコンプライアンスの話がぐっと楽になります。デプロイの失敗や重いワークロードの影響も、全顧客ではなく1顧客で済みます。インスタンスごとに独立してスケール、チューニング、カスタマイズができて、リソースが専用なので、隣のテナントの使い方次第でパフォーマンスが揺れることもありません。メンテナンスも顧客単位で計画できるのでダウンタイムを小さく抑えられますし、料金設計をきちんとすれば、インフラコストをテナントごとの実際の使用量に合わせることもできます。

その代償は、すべてをN個ずつ運用することになる点です。リソースのオーバーヘッドは積み上がり、インスタンス間で設定はドリフトし、フリート全体でバージョンとデータを揃えること自体がひとつのプロジェクトになります。スケーリング、分離、コスト管理の問題は消えるのではなく、増殖するのです。

マルチテナント・シングルインスタンス
#

マルチテナント、シングルインスタンス: 1つのアプリケーションで複数のテナントにサービスを提供します。

こちらがデフォルトになっているのには理由があります。デプロイはひとつ、バックアップするデータベースもひとつ、本番のバージョンもひとつ。リソース効率が良く、運用はシンプルで、アップデートもセキュリティパッチも全顧客に同時に届きます。

難しさは「共有」の裏返しです。あるテナントの重い使い方が全員のパフォーマンスに響きますし、顧客ごとのカスタマイズには限界があります。そしてデータの分離はインフラではなくアプリケーションコードの責務になるため、バグひとつがテナント間のデータ漏えいになりえます。スケーラビリティのボトルネックは全顧客に同時に効きますし、「この顧客だけ」という影響範囲が存在しないぶん、メンテナンスにも余計に気を使います。

SaaS ソリューションの詳細 - マルチインスタンス vs. マルチテナントアーキテクチャ

マルチテナント vs マルチインスタンス
#

同じ選択が「マルチテナント vs マルチインスタンス」と呼ばれることもよくありますが、同じものを反対側から見ているだけです。「テナント」は顧客の話で、1つのソフトウェアを何社で共有するか。「インスタンス」はデプロイの話で、ソフトウェアをいくつ動かすか。シングルテナントのプロダクトは顧客ごとに1つずつ動かすマルチインスタンス構成で、マルチテナントのプロダクトはたいてい全員で共有するシングルインスタンスです。つまり「マルチインスタンスにしてほしい」という要望は、シングルテナントにしてほしいという意味です。

実際には2択というよりグラデーションで、本当の判断はほとんどデータベースで決まります。いちばん共有寄りなのは、全顧客の行を同じテーブルに入れて tenant_id 列を持たせ、すべてのクエリでそれを絞り込む形です。運用コストは最も安く、間違えるのも最も簡単です。一歩進むと、共有データベースの中に顧客ごとのスキーマを切る形になります。分離がきれいになり顧客単位のバックアップも取れますが、マイグレーションはスキーマの数だけ実行することになります。さらに進むと、アプリケーションは共有のまま、データベースを顧客ごとに分ける形です。スタック一式を顧客ごとに動かさなくてもデータをしっかり分離できるので、よく選ばれる中間地点です。反対の端が完全なシングルテナントで、アプリケーションもデータベースも専用です。AWSのSaaSガイダンスでは両端をサイロ(silo)とプール(pool)、その組み合わせをブリッジ(bridge)と呼んでいて、顧客のセキュリティチームに構成を説明するときに便利な言葉です。

シングルテナントとマルチテナントの比較
#

シングルテナント(マルチインスタンス)マルチテナント(シングルインスタンス)
データの分離顧客ごとに別のインフラコード、またはデータベースのポリシーで担保
顧客あたりのコスト高い:顧客ごとにスタック一式低い:全員で1つを共有
運用デプロイ、アップグレード、ダッシュボードが顧客の数だけどれも1つずつ
ノイジーネイバーなし重いテナント1社で全員が遅くなりうる
カスタマイズ顧客単位で可能、バージョンを分けることもほぼ設定とフィーチャーフラグまで
リリース顧客ごとに順番に展開全員に同時
デプロイ失敗の影響1顧客全顧客
セキュリティレビューとデータの所在答えやすい説明に手間がかかる

どちらをいつ選ぶか
#

特別な理由がない限り、マルチテナントを選びましょう。少額を払う顧客がたくさんいるとき、ユーザーが自分でサインアップするとき、そして30個の環境を抱えたら回らなくなるくらいチームが小さいときは、マルチテナントが正しいデフォルトです。

シングルテナントが手間に見合うのは、いくつかの場面に限られます。医療、金融、行政の顧客は、データを物理的に分けることや特定の国に置くことを求めることが多く、セキュリティレビューでそれに正直に答えるいちばん簡単な方法が専用インスタンスです。エンタープライズ契約の大口顧客が数社なら、専用スタックのコストを負担してもらえますし、そもそもそれを期待されていることも多いです。そして、顧客ごとにリリースのタイミングを変えたい、バージョンを固定したい、深くカスタマイズしたいという要望があるなら、インスタンスを分けることはオーバーヘッドではなく、売っているものの一部になります。

結局、多くのチームは両方に落ち着きます。ほぼ全員向けにはマルチテナントのプロダクトを、必要としていてお金も払ってくれる一部の顧客には、エンタープライズプランとして専用インスタンスを。マルチテナント版で最初からすべてにテナントIDを持たせておけば、あとで1社だけ専用データベースに移すのは書き直しではなく、マイグレーションで済みます。

マルチテナントの分離を壊れにくくする
#

マルチテナントでいちばん怖いバグは、テナントの絞り込みを忘れたクエリです。エラーにはなりません。ほかの顧客のデータを黙って返すだけで、コードレビューですべてを拾えるわけでもありません。Postgresを使っているなら、行レベルセキュリティ(RLS)でこのチェックをデータベース側に移せます。絞り込みを忘れたクエリでも、見えるのは今の顧客の行だけになります。

ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON invoices
    USING (tenant_id = current_setting('app.tenant_id')::uuid);

あとは各リクエストが、トランザクションの中で SET LOCAL app.tenant_id = '…' を実行してテナントを設定します。こうすればコネクションプールの接続が前の顧客のIDを次のリクエストに持ち越すことはなく、設定し忘れたリクエストは行の代わりにエラーを受け取ります。注意点は2つあります。アプリはテーブルの所有者ではないロールで接続すること。所有者は FORCE ROW LEVEL SECURITY を付けない限り、このポリシーを素通りします。そしてスーパーユーザーでは絶対に接続しないこと。こちらは何をしても素通りです。

シングルテナントはどこから始めるか
#

シングルテナントで行くなら、それが「管理可能」になるか「スローモーションの災害」になるかを分けるのはツールです。30個の環境を手作業で面倒見ることはできません。

核になるのはコンテナとInfrastructure as Codeです。Dockerでアプリを全テナント同一の形にパッケージし、Kubernetesで大量のインスタンスのデプロイとスケーリングを捌きます。TerraformやPulumi、AWS CloudFormationでテナント一式のスタックをコードとして定義しておけば、31社目の顧客の立ち上げは週末の徹夜作業ではなくプルリクエストになります。VM寄りの世界にいるなら、Ansible、Chef、Puppetといった構成管理ツールが同じ役割を果たしてくれます。CI/CD(Jenkins、GitLab CI/CD)はいつも以上に重要です。すべてのリリースを、付きっきりにならずに全インスタンスへ展開しないといけないからです。

その先はプロダクトの形次第です。ワークロードによってはサーバーレス(AWS Lambda、Azure Functions)でサーバー層ごと消せます。Amazon RDSやAzure SQL Databaseのようなマネージドデータベースは、テナントごとのDBを手で管理する苦行から救ってくれます。モニタリング(Prometheus、Grafana、New Relic)はフリートを持った時点で必須になります。どのインスタンスが不調かは、顧客に言われる前に知りたいですよね。認証まわりはAuth0やOktaのようなIAMサービスに任せれば、テナントごとに作り直さずに済みます。使っている言語にも、この手の配管作業を省いてくれるSaaS向けのフレームワークやライブラリがきっとあります。ホスティングはAWS、Azure、Google Cloud Platformといった大手クラウドならどこでも問題ありません。

初日から全部を揃える必要はありません。コンテナとIaCから始めて、テナントが増えるにつれてモニタリングとIAMを足し、あとは実際に痛みを感じた順に選んでいけば十分です。

ざっくり言うと
#

シングルテナント、つまりマルチインスタンスは、顧客ごとにアプリケーションとデータベースを別々に持つ構成です。分離と顧客単位の制御が手に入る代わりに、運用するフリートを抱えます。マルチテナントは1つを全員で共有する構成で、安くてシンプルですが、分離はコードで担保します。多くのプロダクトはマルチテナントで始め、すべてにテナントIDを持たせてデータベースにそれを強制させたうえで、必要としていてお金も払う一部の顧客にだけ専用インスタンスを提供するのがよいでしょう。シングルテナントで運用するなら、管理できる状態を保つ鍵はコンテナとInfrastructure as Codeです。