Django でマルチテナント

6 min read読了の目安(約5800字 2

はじめに

この記事は Django Advent Calendar 2020 13日目の記事です。

django-tenants を使ったマルチテナントの実装を紹介します.

マルチテナントとは

マルチテナント方式*1とは、1つのシステムの中に複数の企業(ユーザー)のサービスを同居させ、リソースや運用コストを大幅に低減する方式のことである。

出典: マルチテナント・アーキテクチャ - @IT

本記事の紹介内容

企業や店舗ごとにデータを分離して管理したい場合があります.
1つの方法として, それぞれのテーブルに店舗IDのようなフィールドを設けることで実現できます. しかし, クエリごとに毎度店舗IDを指定する必要があるなど, 煩雑になるデメリットがあります.

本記事では django-tenants というパッケージを使ったマルチテナントシステムの実装方法を紹介します.
django-tenants を用いると, 店舗ごとにDBのスキーマを分け, ドメインごとにアクセスするスキーマを切り替えることができます. これにより店舗IDのようなフィールドが不要になり, ほとんど意識することなく, 通常の Django App の実装でマルチテナントを構築することができるようになります.

環境

  • Python 3.8
  • Django 3.1
  • DB: PostgreSQL 11

PostgreSQLのスキーマを使用してマルチテナントを実現するので PostgreSQL は必須です.

以下にこの環境の Dockerfile とサンプルコードがあります. 本記事ではこのサンプルをもとに説明します.
fumuumuf/multi-tenant-example

実装

django-tenants 概要

django-tenants

概要図

django-tenants はドメインごとにテナントを切り替える機能を提供してくれます.

図のように, テナントごとに個別のスキーマを対応させます.
public スキーマは特別なスキーマで, 全テナント間の共通のデータを管理します. また, public スキーマに対応するテナントがあり, 本記事ではこれを「public テナント」とします.

django-tenants install

https://django-tenants.readthedocs.io/en/latest/install.html#installation

ドキュメントに従いインストールと設定を進めます.
まず, pip で django-tenants インストールしてください.

pip install django-tenants

テナントモデル, ドメインモデルの追加

テナントの情報とそのテナントのドメインを管理するモデルを追加します.
ここでは tenants app を作成し, Tenant モデルと Domain モデルを作成しています.

# teants/models.py

from django.db import models
from django_tenants.models import TenantMixin, DomainMixin


class Tenant(TenantMixin):
    name = models.CharField(max_length=100)
    def __str__(self):
        return self.name


class Domain(DomainMixin):
    pass

settings.py の設定

DB 設定など

最初に DATABASES, DATABASE_ROUTERS, MIDDLEWARE を django-tenants のものに変更, 追加します.

# settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django_tenants.postgresql_backend',
        # ..
    }
}

DATABASE_ROUTERS = (
    'django_tenants.routers.TenantSyncRouter',
)

MIDDLEWARE = (
    'django_tenants.middleware.main.TenantMainMiddleware',
    #...
)

apps 設定

次に apps の設定を行います.
通常の Django の設定で INSTALLED_APPS に指定する app のリストを SHARED_APPS, TENANT_APPS に分けて指定します. 全テナントで共有するデータもつ app は SHARED_APPS に, テナントごとにデータを分ける app は TENANT_APPS に指定します. テナント・ドメインモデルを含む app (サンプルでは tenants app) は 必ず SHARED_APPS に指定します.
このように app 単位での指定となるため, 1つの app 内のモデルを public とテナント用に分けることはできません.

また, 少しわかりにくいのですが, SHARED_APPSTENANT_APPS 両方に追加した app は, public テナント含め全てのテナントでそれぞれ独立に管理されます.

# settings.py

SHARED_APPS = (
    'django_tenants',  # mandatory
    'tenants',  # you must list the app where your tenant model resides in

    'django.contrib.contenttypes',

    # everything below here is optional
    'django.contrib.auth',  # 簡単のためユーザー情報は SHARED におきます.
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.admin',
    'django.contrib.staticfiles',
)

TENANT_APPS = (
    # The following Django contrib apps must be in TENANT_APPS
    'django.contrib.contenttypes',

    # your tenant-specific apps
    'polls',
)

# 最後に INSTALLED_APPS にすべての app を指定します.
INSTALLED_APPS = list(SHARED_APPS) + [app for app in TENANT_APPS if app not in SHARED_APPS]

ここでは TENANT_APPS に 事前に作成した polls app を 追加しました.

テナント・ドメインモデルの指定

テナントとドメインのモデルを指定します.

TENANT_MODEL = "tenants.Tenant"
TENANT_DOMAIN_MODEL = "tenants.Domain"

基本的な設定は以上です.
次のコマンドを実行し, public スキーマにマイグレーションを実行できることを確認します.

  • ./manage.py makemigrations
  • ./manage.py migrate_schemas --shared

migrate_schemamigrate コマンドの拡張したものだと思います.(ちゃんと理解できてない)

(https://django-tenants.readthedocs.io/en/latest/use.html#migrate-schemas)

テナントの追加

public テナントの登録

まず public スキーマに対応する public テナントを作成します. 以下リンクのコードを shell などで実行して作成してください.
サンプルには同様のものを apps/create_public_tenant.py に作成しています.

https://django-tenants.readthedocs.io/en/latest/use.html#creating-a-tenant

このとき, public テナントに紐づくドメインを登録します. ここでは public.localhost を設定したものとします.

(注: ローカル環境で動かす場合は hosts ファイルを書き換えて, ドメインとローカルホストを紐付けてあげる必要があります. [1])

public テナントへアクセス

Django サーバーを立ち上げ, http:public.localhost:8000/admin/ へアクセスして管理サイトが表示されることを確認してください.
public テナントでは図のように TENANT_APPS に指定した app は使用できなくってないます.

not available poll

テナントの追加

public テナントの管理サイトから, 新しいテナントとそのドメインを順に登録します. テナント登録のとき, Schema name に入力した名前で DB に新たなスキーマが作成され, このスキーマに TENANT_APPS に指定した app のモデルのテーブルが作成されることになります.
ここでは「spamテナント」を作成し, ドメイン spam.localhost を登録しました.

テナント追加
ドメイン追加
(ドメインの Is Primary は基本的に True で良さそうです. [2])

テナントの管理サイトへアクセス

http://spam.localhost:8000/admin/ へアクセスして管理サイトへログインしてください.
TENANT_APPS に指定した app は緑色のヘッダーで表示され, テナント固有の app が見分けやすくなっています.

テナント管理画面

おわりに

django-tenants を使うと簡単にマルチテナントが導入でき, 通常の Django アプリケーションと同様に開発することができます. マルチテナントの開発機会があれば候補の1つとして検討してみてはいかがでしょうか.

今回は基本的な導入のみの紹介となりましたが, いくつか django-tenants を使う上での tips があるので, また機会があれば紹介したいと思います.

脚注
  1. mac だと Macでhostsファイルを変更・反映 - Qiita が参考になります. ↩︎

  2. テナントに複数のドメインを設定するときに使用するようです. Issue #5 ↩︎