コンテンツにスキップ

Multi-tenant アーキテクチャー 実現方法 (1)

マルチテナント方式で「文書管理+検索サービス」を構築する際に、Aurora(RDB)とOpenSearchを用いてメタデータおよび検索インデックスを管理するための実現方法をまとめたものです。この内容を参考に、要件やシステム特性に応じて、必要に応じた方法を選択・組み合わせる必要があります。

前提

  • サービス概要
    • ユーザ企業(エンドユーザ)がアップロードした文書のメタデータをAuroraで管理し、検索に必要なインデックス情報をOpenSearchで管理する。
    • 企業(テナント)単位で文書へのアクセスや検索が行われる。
  • マルチテナント要件
    • テナント(企業)ごとにデータを安全に隔離する必要がある。
    • テナント数増加に対してスケーラブルに対応できるアーキテクチャが求められる。
    • コスト管理や運用負荷にも配慮が必要。

1. Aurora(RDB)でのメタデータ管理

メタデータとは、文書の所有テナント、文書ID、作成者、作成日時、タグなどの情報を指します。 マルチテナントRDB設計時の主な方式として、下記3つを考慮します。

  1. 単一データベース・単一スキーマ方式 (テナントIDをキーに共用)
  2. 単一データベース・複数スキーマ方式 (テナントごとにスキーマ分割)
  3. 複数データベース方式 (テナントごとにDBインスタンス/DBを分割)

それぞれの特徴・メリット・デメリットを以下に整理します。

1.1 単一データベース・単一スキーマ方式 (テナントIDをキーに共用)

特徴

  • すべてのテナントのデータを1つのデータベース・スキーマで管理し、各テーブルにtenant_idを持つ。
  • コードベースがシンプルでスキーマが1つのため、運用が比較的簡単。
  • テナントごとのデータ量に応じてテーブルが巨大化する。

メリット

  • 開発のしやすさ テーブルを共通化することで、アプリケーションコード・マイグレーションなどのメンテナンスが容易。
  • スケールアウトの柔軟性 Auroraのスケール機能を活用しやすい。
  • コスト効率 データベースリソースを共用するため、総コストを抑えやすい。

デメリット

  • テナント間のデータ隔離 アプリケーションレイヤーでのtenant_idフィルタリングが必須。誤って他のテナントのデータを参照するリスクがあり、厳格なアクセス制御が必要。
  • データ量増加時のパフォーマンス テナント数やデータが急激に増加すると、単一テーブルが巨大になりパフォーマンスチューニングが難しくなる可能性がある。

1.2 単一データベース・複数スキーマ方式

特徴

  • 一つのDBインスタンスの中で、テナントごとにスキーマを分割する。
  • スキーマの単位で権限を設定でき、テナント間のデータを切り分けやすい。

メリット

  • データ隔離の強化 テナントごとにスキーマを分けることで、他テナントのテーブルにアクセスしにくい。
  • 運用一元化 単一DBインスタンス配下のため、接続先や基本的なDB設定は一つにまとまる。

デメリット

  • スキーマ管理のオーバーヘッド テナントが増えるほどスキーマ数が増え、マイグレーションやメンテナンスに手間がかかる。
  • スキーマ数上限 AuroraやDBエンジンの種類(例: Aurora MySQLかPostgreSQLか)によって、実質的に管理可能なスキーマ数に上限やパフォーマンス的制約が生じることがある。

1.3 複数データベース方式

特徴

  • テナントごとにデータベース(あるいはDBクラスター)を分割する。
  • テナントが極端に増えたり減ったりする場合のコストや管理の柔軟性を検討する必要あり。

メリット

  • データ隔離が最も強固 テナントごとに物理的(or 論理的)にDBを分離するため、アクセス制御が明確。
  • パフォーマンスチューニングがテナントごとに独立 テナント規模が大きい場合、そのテナント専用に性能チューニングができる。

デメリット

  • 運用コスト増 テナントが増えるたびにDBを作成・管理する必要があり、コストや管理が煩雑化する。
  • 総コスト上昇 アイドル状態のテナントDBにも最低リソースコストがかかる。

Auroraでの推奨パターン

  • テナント数が少ないうちは「単一データベース・単一スキーマ方式」から始め、将来的にテナント数が増えるようであれば「複数スキーマ」や「複数DB方式」に移行するパターンを採用するのが一般的。
  • ただし金融や医療など特にデータ隔離要件が強い場合は最初から複数DB方式や複数スキーマ方式を選択する。
  • Auroraが提供するパーティショニングやシャーディングの仕組み(Aurora Serverless v2の活用など)も検討し、大量データや高負荷に備える。

2. OpenSearchでの検索インデックス管理

文書本文やタグ、メタデータの一部などをOpenSearchにインデックスし、全文検索や複雑なクエリを実現します。 マルチテナントでOpenSearchを運用する場合にも大きく3つの方式が考えられます。

  1. 単一クラスター・単一インデックス方式
  2. 単一クラスター・テナント別インデックス方式
  3. テナントごとにクラスターを分離する方式

2.1 単一クラスター・単一インデックス方式

特徴

  • すべてのテナントデータを同一インデックスに集約し、tenant_idを持たせてフィルタリングする。
  • インデックス数が1つなので、クラスタ管理は簡単。

メリット

  • 管理のシンプルさ インデックス設定やマッピングの変更が1つに統一されるため、メンテナンスが少なくて済む。
  • リソース効率 シャードやレプリカの割り当てもまとめて実施できる。

デメリット

  • フィルタリングミスによるセキュリティリスク アプリケーションでのクエリによりテナント隔離を担保するため、バグがあると他のテナントのデータが見えてしまう可能性がある。
  • 大規模化時のパフォーマンス インデックスが巨大になりすぎると、クエリ性能に影響が出る場合がある。

2.2 単一クラスター・テナント別インデックス方式

特徴

  • テナントごとにインデックスを作成し、同一OpenSearchクラスター内で運用する。
  • クラスター自体は1つだが、インデックス単位で隔離される。

メリット

  • インデックス単位での隔離 テナントごとにインデックスを分けることで、アクセス権限やパフォーマンス管理がしやすい。
  • クエリのシンプル化 テナントに紐づくインデックスのみを検索するため、アプリケーション側のフィルタリングミスを避けやすい。

デメリット

  • インデックス数増加によるオーバーヘッド 多数テナントが存在する場合、インデックス数が膨大になり、シャード管理の複雑さやメモリ使用量が増える。
  • Mapping 変更の手間 テナント別にスキーマ(マッピング)が同じならまだしも、カスタマイズが必要な場合はインデックスごとに管理が必要。

2.3 テナントごとにクラスターを分離する方式

特徴

  • テナントごとにOpenSearchクラスターを分割する。
  • 大規模かつ高いセキュリティ/パフォーマンス要件がある場合に適する。

メリット

  • データ隔離が最も強固 テナント間で物理的に別クラスターになるため、アクセス制御が明確。
  • クラスターごとにスケール テナント固有の負荷に合わせて専用にスケーリングできる。

デメリット

  • 管理の煩雑さ テナントごとにクラスターを運用するため、クラスターの数だけ監視・保守が必要。
  • コスト テナントごとにクラスターを立ち上げるため、利用しないテナントにとっても稼働コストがかかる可能性がある。

OpenSearchでの推奨パターン

  • テナント数が少なく、シンプルな構造で十分なうちは「単一インデックス方式」から開始し、 テナント数が増加したり、インデックスサイズが大きくなるにつれ、「テナント別インデックス方式」に移行するのが一般的。
  • セキュリティ要件が厳しい、大規模テナントが多いなどの要因がある場合は、最初から「テナント別インデックス方式」を採用するケースも多い。
  • テナントごとにクラスターを分離する方式は、マネージドサービスであっても運用コストが高いため、よほどの要件(機能要件・コンプライアンス要件など)でない限り最終手段として検討。

3. セキュリティとアクセス制御

マルチテナント環境では誤った設定により他のテナントのデータを参照できるリスクがあるため、厳重なセキュリティとアクセス制御が求められます。

  1. アプリケーションレイヤーでの強固なフィルタリング
    • すべてのクエリでtenant_idをWHERE句やフィルタに適用し、他テナントのデータが返らないよう徹底する。
    • バッチ処理や管理画面でも同様のチェックを入れる。
  2. Auroraの権限設定
    • テナント別にDB接続ユーザを用意し、アクセス可能なスキーマ/テーブルを制限する(複数スキーマ方式の場合など)。
    • データベースレベルで常にtenant_idカラムの行レベルセキュリティ(RLS)を導入できる場合は検討する(Aurora PostgreSQLなど)。
  3. OpenSearchのアクセスコントロール
    • AWS Identity and Access Management(IAM)のリソースベースポリシーやOpenSearch内の細かいアクセスコントロール(Access Policy, Fine-grained access control)を組み合わせて、テナント別のインデックス/ドキュメントに絞ったアクセス許可を設定する。
    • Kibana/OpenSearch Dashboards等のGUIを提供する場合も、テナント別にアクセス範囲を制限。
  4. 暗号化と通信
    • Aurora, OpenSearchともに転送中暗号化(TLS)保存時暗号化(Encryption at rest)を有効にし、データ漏えいリスクを下げる。
    • KMSなどを活用した鍵管理でコンプライアンス要件を満たす。
  5. 監査ログ・監視
    • Auroraのクエリログ、OpenSearchの監査ログ等を活用し、不要なアクセスや不正クエリを検出する仕組みを構築。
    • CloudWatch LogsやElasticsearch/OpenSearchの自動監視機能を活用してアラートを設定。

4. スケーラビリティとコスト管理

マルチテナントサービスでは、テナント数やデータ量の増加に伴いスケール戦略とコスト管理が重要になります。

  1. Auroraのスケーリング
    • Aurora Serverless v2やオートスケール機能を活用し、負荷変動に応じて自動的にリソースを調整する。
    • ストレージは自動で拡張されるが、オブジェクト数や大きさに関しても定期的に監査し、クリーンアップやアーカイブを実施。
  2. OpenSearchのスケーリング
    • インデックスサイズやシャード数を定期的に見直し、不要データの削除やローテーション(過去データをアーカイブし、検索対象から除外)を行う。
    • フィールド数が多くならないよう制限(動的マッピングを避け、必要なフィールドだけを明示的に定義)。
    • 必要に応じてドキュメントレベルのTTL(Time to Live)や過去インデックスのスナップショットを検討。
  3. コスト最適化
    • Aurora, OpenSearchともにリージョンやインスタンスタイプの選択、Reserved InstanceSavings Planの活用によりコストを削減。
    • テナントごとに利用状況を可視化し、コストチャージバック(従量課金など)を検討することで、テナントに応じたコスト配分が可能。
  4. マルチテナントにおけるシャーディング戦略
    • Aurora: テナントIDごとにシャードを分割したり、特定のテナントを大きさに応じてシャードを切り出す。
    • OpenSearch: インデックスを適切にシャード分割し、ホット・ウォーム・コールドといったストレージ階層を活用する。

5. 運用・モニタリング

  1. モニタリング項目
    • Aurora: CPU使用率、接続数、クエリレイテンシ、スロークエリログなど。
    • OpenSearch: クラスター状態、ノード状態、CPU/メモリ使用率、キュー状態、GC回数、インデックスサイズなど。
    • テナント別のモニタリング: 特定テナントが異常に大量のクエリやデータを送信していないか監視し、早期アラートを設計。
  2. アラート設計
    • リソース使用率(CPU, メモリ, ストレージ)がしきい値を超えた場合に通知。
    • 一定期間内に特定テナントのクエリが急増したら通知(DoS攻撃やバグの兆候)。
  3. バックアップ/リカバリ
    • Auroraの自動バックアップウィンドウを設定し、定期的にスナップショットを取得。
    • OpenSearchのスナップショットをS3に保存し、障害時に迅速にリストアできるようにする。
    • 災害対策(Disaster Recovery)も考慮し、複数AZや複数リージョンへのレプリケーションを検討。
  4. 段階的リリース/テスト
    • スキーマ変更やインデックスのマッピング変更の際は、ステージング環境で検証してから本番に段階的に適用。
    • ロールバック戦略も明確にしておく。

6. 実装時のポイントまとめ

  1. テナントIDの一貫性
    • メタデータ(Aurora)と検索インデックス(OpenSearch)で共通のtenant_idを用いて整合性を保つ。
    • PK、FKの一部としてtenant_idを組み込むことで誤操作を防ぐ。
  2. API層での認可設計
    • APIリクエスト時にJWT等で認証・認可を行い、tenant_idをコンテキストに含める。
    • AuroraやOpenSearchへのクエリ生成時に必ずtenant_id条件をセットする。
  3. データライフサイクル管理
    • 文書の更新頻度や閲覧頻度を考慮し、一定期間が経過したデータはアーカイブや削除を検討。
    • OpenSearchの過去インデックスを別ストレージに移行し、検索対象を最新データに絞ることで性能を維持。
  4. 同時実装・データ整合性
    • メタデータ登録(Aurora)後にOpenSearchにインデックスを作成する際、障害やネットワーク切断が発生すると整合性が崩れやすい。
    • 冪等性(idempotency)を担保し、再試行時に重複エントリが生じない仕組みや、定期整合チェックを設計。
  5. サイジングと将来計画
    • テナント数やデータ増加量を見込み、シャード数やインスタンスタイプを定期的に見直す。
    • 大規模化したらテナントを再シャーディング・リバランスできるような仕組みを準備。

7. まとめ

  • Auroraのメタデータ管理
    • テナント数や要件の厳しさに応じて、「単一DB/スキーマ共有」「スキーマ分割」「DB分割」を選択。
    • データ隔離の強度と運用コストのバランスをとり、将来的スケールを見据える。
  • OpenSearchのインデックス管理
    • まずは「単一インデックス方式」でシンプルに始め、テナント数やデータ量の増加に伴って「テナント別インデックス方式」を検討。
    • 物理的にクラスターを分けるのは最終手段とする。
  • セキュリティとアクセス制御
    • tenant_idによるデータ隔離はアプリケーションとDBの両面から強化。
    • Aurora, OpenSearchそれぞれの機能(IAM, fine-grained access control, 暗号化など)をフル活用する。
  • スケーラビリティとコスト最適化
    • Aurora Serverless v2やOpenSearchのシャーディング/スナップショット機能を駆使し、必要に応じてリソースをスケール。
    • 不要データの削除やライフサイクルポリシーによりリソース浪費を防ぐ。
  • 運用・モニタリング
    • リソース監視・アラートを設計し、障害や不正アクセスを早期発見。
    • 定期的なスナップショットとバックアップ、テスト環境での検証プロセスにより安定稼働を実現。