はじめに
本記事は any Advent Calendar #2 「マルチテナントSaaSにおけるエンジニアリング大全」 Day3 の記事です。弊社anyのアドベントカレンダーをひとつ丸ごと占有して、ひとりアドベントカレンダーとして、筆者の「マルチテナントSaaSのエンジニアリング」への経験をすべてアウトプットしていくカレンダーです。
さて、本日からはより具体的な設計についてみていきます。マルチテナントアーキテクチャにおいて、データの格納場所(データストア)の選定は、非常に重要なテーマのひとつです。「テナントを分離する思考」があらゆる設計に幅を利かすため、まずはここから始めていきましょう。
特にテナント分離をデータベースレイヤで実現することは、テナント間のデータ取得・更新を防ぐ重要な設計方針になります。テナントをまたぐデータアクセスは、非常にクリティカルなセキュリティインシデントになりかねません。
そのような背景もあり、データベースのテナント分離における設計には、一定のプラクティスがすでに存在します。これらを紹介していきましょう。
データストア分割の分類
AWS公式のマルチテナントSaaSにおける解説記事より、データストアの分割に関する3つの主要なモデルが定義されています。非常に明確な整理のため、この分類を軸にこのあとの話を進めていきます。
出典: AWS | マルチテナント SaaS システムを構築する
これらの3つのモデルは、テナント分離の強度、コスト効率性、運用複雑性において異なるトレードオフを持ちます。以下、それぞれのモデルについて詳しく解説していきます。
プール(Pool) モデル
プールモデルは、図の左側のモデルで、すべてのテナントのデータを単一のデータベース内で共有する方式です。最もプリミティブな実装としては、各テーブルに tenant_id のようなカラムを持たせ、クエリ実行時に必ずテナントIDでフィルタリングすることでデータを分離します。
メリット:
- リソース効率が高く、データベースインスタンスが1つで済むため、インフラコストを最小限に抑えられる。運用管理が比較的シンプルで、バックアップやメンテナンス、スキーマ変更などの作業を一箇所で実行可能になる
- テナント数が増えても線形にコストが増えないため、スケーラビリティに優れている。新規テナントの追加が容易で、プロビジョニング時間がほとんどかからない
デメリット:
- アプリケーションレイヤでのテナント分離ロジックが必須。すべてのクエリに
WHERE tenant_id = ?を漏れなく付ける必要がある - ノイジーネイバー問題が発生しやすく、特定のテナントの負荷が他のテナントに影響を与える可能性がある
実運用においての考慮点
さてこれだけ見るとプールモデルはテナント分離という観点からは、採用しにくいアーキテクチャのように思えますが、マイグレーションの複雑性を緩和できるなど運用上のメリットも非常に大きいです。
このバランスを取った戦略として、例えばPostgreSQLにおいては、Row Level Security(RLS)という仕組みを活用して、物理的にはプールモデルを採用しつつも、データベースレイヤでの分離が可能になっています。
参考として、Bill OneやNStockさんの記事も紹介しておきます。個人的観測では、B2B SaaSにおいてPoolモデルをRLSと併せて採用するプロダクトが非常に増えてきたように感じます。
その他にも Viewを利用した方法や、ORM(Object-Relational Mapping)レベルでテナントIDを自動付与する仕組みを導入するなど、ミドルウェアやライブラリレベルで解決する方法もあります。
また、データ分離以外での課題として、同一インスタンスでの運用に伴うノイジーネイバー問題のリスクがありますが、パーティションやシャーディングを活用することで対策できます。これらの詳細はDay10で取り扱う予定です。
それでは、次にSiloモデルについて紹介しましょう。
サイロ(Silo) モデル
サイロモデルは、図の右側のモデル、テナントごとに専用のデータベースまたはスキーマを割り当てる方式です。物理的または論理的にデータを完全に分離するため、最も強固なテナント分離を実現できます。
メリット:
- テナント間のデータ分離が物理的に保証されるため、セキュリティリスクが大幅に低減。アプリケーションのバグによるテナントまたぎのデータアクセスが構造的に不可能
- テナントごとに独立したリソースを持つため、ノイジーネイバー問題が発生しない
- テナント単位でのバックアップ、リストア、メンテナンス、規制要件(データレジデンシー、業界標準など)が可能になる
デメリット:
- テナント数に比例してデータベースインスタンスまたはスキーマが増えるため、インフラコストが増大する
- 運用管理の複雑性が増します。多数のデータベースに対してマイグレーション、バックアップ、アップデート、モニタリングを個別に行う必要が生じる
実運用においての考慮点
サイロモデルは特にRLSが主流になる前の分離方法としては、マルチテナントSaaSの潮流を占めていたように感じます。実際の運用において問題になりがちな点は、なんといっても「マイグレーション」にかかわる諸問題でしょう。
最も有名な参考事例のひとつがSmartHRさんの記事ではないでしょうか。規模の拡大に応じてマイグレーションの時間が爆発的に伸びてしまった事例の一つです。
またHRBrainさんの導入事例も非常に参考になります。RLSへの移行までの話なので、ここまで読み進めた方であれば納得感も高いのではと思います。
Qastでは、論理的に分離されたスキーマによるデータ分離、つまりSiloモデルを採用しています。Amazon Aurora MySQLを採用し、各テナントの情報は各スキーマごとに管理されています。現時点では直接的な問題は生じていませんが、中長期的には対策を検討したいと考えています。
※ データ分離とは異なりますが、最近RDSからAuroraへ移行した話は以下の記事をご覧ください。
https://zenn.dev/any_dev/articles/1836fbd7a47339
実際に、MNTSQさんのように同様の課題を抱え、移行の検討事例を公開されている企業も存在するため、多くのプロダクトが直面する一般的な課題だと言えます。
ブリッジ(ハイブリッド)モデル
プールモデルとサイロモデルの中間的なアプローチとして、テナントの特性に応じて使い分ける方法もあります。例えば、エンタープライズ向けの大規模テナントにはサイロモデルを適用し、小規模テナントにはプールモデルを適用するといった柔軟な設計が可能です。
実際このモデルを採用する必要があるプロダクトは、法律による規制など強いセキュリティ制約を遵守するために分離せざるをえないパターンも多いのではと想像しています。
またオンプレ版やセルフホスト版などを提供する企業も間接的にはこのモデルを採用することになります。例えばnulabさんの事例のように、顧客にDBインスタンスを用意してもらうパターンなどが該当するのではないでしょうか。
まとめ
データベースの選定を通してデータ分離の基本を紹介してきました。
ひとつ注意していただきたい点としては、「RLSが正義」といった論調になりがちですが、すべてのプロダクトに当てはまるわけではありません。筆者のHorizontal B2B SaaSという領域においては、契約企業数の規模が重要となるため、バランスの取れた選択肢のひとつがRLSであるということです。
そもそも契約企業が少ない場合や、セキュリティ要件が厳しい開発においては、1社あたり物理的に独立したデータベースを採用することも一般的でしょう。つまり、モデルの選定においてはビジネスモデルとのバランスを考慮した選択が肝要であることを強調しておきます。
さて次回は、これらのデータストアモデルをAWS上で実装する際の具体的なアーキテクチャパターンについて掘り下げていきます。
