35
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

テナント分離を実現するデータストアの選定 - インフラ編 -【マルチテナントSaaSにおけるエンジニアリング大全 Day4】

Last updated at Posted at 2025-12-03

Day4_rev1.jpg

はじめに

本記事は any Advent Calendar #2 「マルチテナントSaaSにおけるエンジニアリング大全」 Day4 の記事です。弊社anyのアドベントカレンダーをひとつ丸ごと占有して、ひとりアドベントカレンダーとして、筆者の「マルチテナントSaaSのエンジニアリング」への経験をすべてアウトプットしていくカレンダーです。

ここまではリレーショナルデータベースを中心に説明してきましたが、実際のマルチテナントアプリケーションでは、RDB以外にも様々なデータストアを使用します。それぞれのデータストアにおいても、適切なテナント分離戦略が必要になってきます。

インフラ全体の設計において、常に「テナント分離」を検討することが重要です。各データストアの分離方法は、昨日紹介したパターンのいずれかに該当させることで大きく分類することができます。そこで本記事では、個別プロダクトの制約をもとに、主要なデータストア別に確認していきましょう💪

ファイルストレージにおけるテナント分離を実現する方法

ファイルストレージにおけるテナント分離は最も直面しやすい一例で、AWSではS3で管理されることがほとんどでしょう。S3でファイルやオブジェクトを管理する場合、以下のアプローチが考えられます。

s3.png

プレフィックスベースの分離(プールモデル):

s3-by-tenant.png
https://aws.amazon.com/jp/blogs/news/partitioning-and-isolating-multi-tenant-saas-data-with-amazon-s3/

単一のS3バケット内で、オブジェクトキーにテナントIDをプレフィックスとして含める方式です。例えば、{tenant_id}/users/{user_id}/profile.jpg のような構造を採用します。

メリットとしては、バケット数の制限を気にせず運用でき、実装の難易度も非常に低いです。一方で、IAMポリシーやバケットポリシーでテナント単位のアクセス制御を実装する必要があり、アプリケーションレイヤでのプレフィックス管理が必須となります。

弊社Qastにおいてもこの方式を採用しています。一方で実装において留意すべきセキュリティ対策も非常に多いので後述します。

バケット分離(サイロモデル / Bucket-Per-Tenant):

s3-multi.pnghttps://aws.amazon.com/jp/blogs/news/partitioning-and-isolating-multi-tenant-saas-data-with-amazon-s3/

テナントごとに専用のS3バケットを作成する方式です。tenant-{tenant_id}-assets のようなバケット名を付けます。ただしS3はグローバル命名のため、バケット名はすべてのAWSのバケット名と重複できないことに注意が必要になります。

以前はバケット数に物理的な制約があったため、S3においてサイロモデル(各社ごとにバケットを用意する)を採用することは現実的ではありませんでした。しかし、昨年末よりバケット数の大幅緩和が行われたことで採用することも可能になりました!

この方式では、IAMロールやバケットポリシーでテナント単位の厳密なアクセス制御が可能になり、監査ログやライフサイクルポリシーもテナント単位で管理できます。特に非機能面が重要になる場合は、2025年現在では十分に採用ができるようになりましたね。

実装上のポイント

いずれの場合においても、IAM Policyを明確に設定しましょう。例えば、バケット共有の場合でも、以下のようなIAM Policyでテナント分離を実現できます。

  • Resourceの指定において、そのテナントのオブジェクトにアクセスするときに一時的に認証情報を取得し、それを活用するようにする。テナント固有のIAMポリシーの定義が必要になる。
  • より強固にしたい場合は、オブジェクトタグを利用して各種リソースにタグを付与し、きめ細やかにアクセス制御も可能。ただし追加料金が発生するため注意。
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject"
      ],
      "Resource": "arn:aws:s3:::my-bucket/tenant-${tenant-id}/*", ⭐️
      "Condition": {
        "StringEquals": {
          "s3:x-amz-tagging-project-id": "${tenant-id}" ⭐️
        }
      }
    }
  ]
}

バケットポリシーは複雑になりがちなので、S3 Access Pointを利用する方法もあります。ただし作成上限があるため、注意が必要です。S3アクセスポイントについては詳細に入り込みすぎるためこの記事では割愛しますが、下記の記事が詳しいです。

また、CDN(CloudFront)との併用するパターンも考慮が必要になります。このようにS3というトピックひとつでも多様なデータ分離のテクニックが存在します。マルチテナントのデータ分離を徹底することの難しさが少し垣間見えるのではないでしょうか。

その他のデータストアにおけるテナント分離について

OpenSearch(Elasticsearch)

全文検索や分析用途で使用されるOpenSearchでも、テナント分離が必要になります。大きく分けて以下の方式が考えられます。

  • インデックス名にテナントIDを含めて分離する方法(論理分離のSiloモデルに近い)
  • 各ドキュメントに tenant_id フィールドを持たせてクエリ側でフィルタリングする方式(Poolモデルに近い)
  • 物理的にOpenSearchインスタンスを分離する方法(物理分離のSiloモデル)

これらは、データベースと同じような設計パターンを当てはめることができます。RLSと類似した概念として、OpenSearchには Document Level Security(DLS)を使用してクエリレベルでフィルタリングを強制することができます。

SQS/SNS/EventBridge

同様にキュー(トピック/イベントバス)ごとに分離する方法、共有キューに投げるメッセージにメッセージ属性としてテナントIDを付与する方法があります。

弊社Qastではテナント間で共有したキューを採用し、メッセージ属性にテナントIDを付与するスタイルをとっています。また重要な点として、メッセージ自体には識別子のみを付与し、具体的なデータを含めないことも重要です。

データ分離とは異なる視点にはなりますが、直近でAWS SQSにはフェアキューという概念が導入されました。こちらについては、データ分離の文脈とはやや異なり、ノイジーネイバー問題への対策となるため、また後日の紹介とさせてください。

DynamoDB

DynamoDBにおいてはデータベースの分離方法とほぼ同様の設計になります。テーブルごとにテナントを作成する方式、またはパーティションキーにテナントIDを含めることでテナント分離を実現できます。後者の場合、IAM ポリシーの Condition として dynamodb:LeadingKeysを設定することでより強固な分離を実現できます!

{
    "Effect": "Allow",
    "Action": [
         "dynamodb:*"
    ],
    "Resource": [
         "arn:aws:dynamodb:*:*:table/{{table}}"
    ],
    "Condition": {
        "ForAllValues:StringEquals": {
            "dynamodb:LeadingKeys": [ "{{tenant}}" ]
        }
    }
}

注意点としては、パーティションキーをテナントIDにした場合、十分にデータの分散が実現されず、DynamoDBでは性能問題が発生する可能性があります。下記に詳しく記述されているため、ここでは割愛します。

ネットワークレベルの分離

これまではデータストア層でのテナント分離戦略を議論してきましたが、マルチテナント SaaS ではインフラのネットワークレベルでも適切なテナント分離が求められる場合があります。この記事では、下記の記事に記載の内容を簡単にまとめます。

方式 説明 メリット デメリット
AWSアカウント分離 各テナントを専用の AWS アカウントにデプロイする方法 テナント間のアクセス可能性に対する懸念が強い顧客に、明確な境界を提供できる
ビジネスモデルによっては顧客のAWSアカウント環境上にデプロイする形も可能
アカウント制限がボトルネックとなる
VPC分離 単一アカウント内で、各テナントを独立した VPC でホストする方式 ネットワーク境界を使用した分離を提供
アカウント制限の課題を超えることができる
管理と中央集約化が可能
VPC 数の増加に伴い管理が複雑化する
サブネット隔離 単一の VPC 内で、テナントごとに専用のサブネットを割り当て、ネットワークルーティング構成でテナント間のアクセスを防止する方式 テナントごとに細粒度の隔離が実現できる テナント数が増えるとスケーリングと管理が非常に複雑になる
小規模なテナント集団向けに限定される

ネットワークレベルでの分離については、特に代替がききにくいので、慎重に検討することをオススメします。もっとも、これも提供するプロダクトで求められるセキュリティレベルによるところが大きいため、正解がないのが難しいところです。

まとめ

私個人の経験談としても、非常に難易度の高い設計作業かつ、可逆性が低い領域のため、慎重に検討を進めることをオススメします。ここで述べられなかったノイジーネイバー問題なども後続で取り扱う予定なので、楽しみにしていてくださいね🙆

35
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?