データベース
ToukaCloud のマネージドデータベース (MariaDB Galera クラスタ) の作成と利用
1. 概要
ToukaCloud のマネージドデータベースは、MariaDB Galera クラスタ上で動作する本格的なデータベースサービスです。 SaaS アプリからの直接接続、自動バックアップなど、開発に必要な機能を提供します。
- MariaDB 10.6 以上: MySQL 互換の高速 RDBMS
- Galera クラスタ: マルチマスター同期レプリケーション
- テナント分離: ユーザーごとに独立したデータベース / ユーザーを自動作成
- 自動バックアップ: 定期バックアップ機能(ライセンス依存)
- 暗号化: パスワードは AES-256-GCM で暗号化保存
- 環境変数注入: SaaS アプリへの自動環境変数割り当て対応
2. 前提条件
- 有効なライセンスが必要です(S 以上のいずれか)
- ライセンスプランによって作成可能なデータベース数が異なります
- SaaS アプリから接続する場合は、ToukaCloud のコンテナを利用していることが前提です
ライセンス別の最大 DB 数と容量は 「容量上限とライセンス」セクション を参照してください。
3. データベースを作成する
ToukaCloud の Web UI からのみデータベース作成が可能です。 API からの直接作成には認証が必要で、通常はユーザーが Web UI を利用します。
手順
- サイドバーの データベース を開く
- 「データベース作成」 ボタンをクリック
- データベース名 を入力
- 英字で始まり、英数字とアンダースコア (
_) のみ使用可 - 最大 32 文字
- 例:
myapp_db,users_db
- 英字で始まり、英数字とアンダースコア (
- ユーザー名(任意) を入力
- 空欄にするとランダムに自動生成されます
- 指定する場合も英数字とアンダースコアのみ
- パスワード(任意) を入力
- 空欄にするとランダムに生成されます(推奨)
- 指定する場合は 8 文字以上
- 「作成」 をクリック
作成完了
作成後、接続情報ダイアログが自動表示されます。 ここで host, port, username, password を確認・コピーしてください。
4. 接続情報の取得
作成済みのデータベースの接続情報は、いつでも Web UI から確認できます。
UI からの確認方法
- データベース一覧から対象のデータベースを探す
- 「接続情報」 ボタンをクリック
- ホスト、ポート、ユーザー名、パスワード、接続文字列が表示されます
接続情報の内容
mariadb-galera.database.svc.k8s.home
Kubernetes クラスタ内の DNS 名。 SaaS アプリなら本名で接続可能です。
3306
MariaDB のデフォルトポート
tenant_<tenantId>_<dbName>
内部的に自動生成された名前です。 接続時はこの名前を使用します。
t_<tenantId>_<dbName>
このユーザーは指定されたデータベースのみアクセス可能です。
AES-256-GCM で暗号化保存されています。 Web UI でのみ復号化・表示されます。
5. Web コンソール (Adminer) と認証フロー
各データベースには Adminer(PHP ベースのWeb DB管理ツール)の Web コンソールが用意されています。 テーブルの作成・編集・SQL 実行・データのインポート/エクスポートをブラウザから直接行えます。
コンソールを開く
- データベース管理ページを開く
- 対象のデータベースカードの 「コンソール」 ボタン (ターミナルアイコン) をクリック
- 新規タブで Adminer が ログイン済みの状態 で開く
パスワード入力は不要です。ボタンを押すと安全な短期トークンを使って自動ログインされます。
認証情報の取り扱い(4階層)
コンソールを開く際、内部では 4 つの認証レイヤーが連携しています。 パスワードはユーザーに直接見せず、サーバー側で復号化して Adminer に渡します。
| 区間 | 使う認証情報 | 出どころ |
|---|---|---|
| ① ユーザー → ToukaCloud | Keycloak セッション (NextAuth Cookie) | 通常のログインと同じ |
| ② ToukaCloud → Adminer 入場ゲート | 短期 JWT (5分) + adminer_gate Cookie (4時間) | サーバー側のシークレットで HMAC 署名 |
| ③ ブラウザ → Adminer ログイン | MariaDB ユーザー名 + パスワード | DB 作成時に発行された資格情報を復号 |
| ④ Adminer → MariaDB | ③ と同じ | Adminer の PHP セッションに保持 |
セキュリティ特性
- URL 直接アクセスは 401: Adminer の URL を知っていても、有効な短期トークンか入場 Cookie が無いとログイン画面すら表示されません
- 短期トークンは 5 分で失効: URL を共有されてもすぐに無効化されます
- パスワードは通信を流れない: ブラウザの hidden form で 1 度だけ Adminer に POST され、以降は PHP セッションで管理
- 所有権チェック: 自分が作成した DB のコンソールしか開けません
- 暗号化保存: パスワードは AES-256-GCM で暗号化されてサーバー側に保管
6. ロール (owner / app / readonly)
1 つのデータベースに対して 3 種類のユーザー が自動で発行されます。 用途別に使い分けることで、アプリのバグや SQL インジェクションがあっても被害を限定できます。
| ロール | 権限 | 推奨用途 | ユーザー名プレフィックス |
|---|---|---|---|
| owner | ALL PRIVILEGES (DDL 含む) | Adminer コンソール、マイグレーション | t_ |
| app | SELECT / INSERT / UPDATE / DELETE / EXECUTE | アプリケーションの実行接続 | ta_ |
| readonly | SELECT のみ | 分析・モニタリング・閲覧専用 | tr_ |
なぜ分けるのか
- アプリには app を使う → 万一 SQL インジェクションがあっても
DROP TABLEされません - 分析ツール / BI には readonly を使う → 誤操作で書込みが発生しません
- スキーマ変更・コンソール操作だけ owner を使う → 全権ユーザーの利用面を最小化
使い方
- データベース管理ページで 「接続情報」 ボタンをクリック
- ダイアログ上部のタブで owner / app / readonly を切り替え
- まだ発行されていないロール (app/readonly) は 「(ロール名) を発行」 ボタンで作成
- 各ロールごとに以下の操作が可能:
- このロールでコンソールを開く: そのロールで Adminer に自動ログイン (権限テストに便利)
- パスワード再生成: 該当ロールのパスワードを新しい値に差し替え
- このロールを削除: app/readonly のみ削除可能 (owner は削除不可)
app ロールで継続接続している場合、ローテーション後にアプリ側の環境変数も更新が必要です。7. SaaS アプリから接続する
ToukaCloud でホストされている SaaS アプリから、 マネージドデータベースに直接接続することができます。
方法 1: 接続文字列を環境変数に設定
アプリ管理画面の「環境変数」セクションで、以下のような文字列を設定します。
# MySQL 接続文字列(Prisma / Node.js / 等) DATABASE_URL=mysql://t_abc123_myapp:password123@mariadb-galera.database.svc.k8s.home:3306/tenant_abc123_myapp # または個別に設定 DATABASE_HOST=mariadb-galera.database.svc.k8s.home DATABASE_PORT=3306 DATABASE_USER=t_abc123_myapp DATABASE_PASSWORD=password123 DATABASE_NAME=tenant_abc123_myapp
方法 2: 各言語での接続例
Node.js / Prisma
.env
DATABASE_URL="mysql://t_abc123_myapp:password123@mariadb-galera.database.svc.k8s.home:3306/tenant_abc123_myapp"
// prisma/schema.prisma
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}Python / SQLAlchemy
import os
from sqlalchemy import create_engine
engine = create_engine(
f"mysql+pymysql://{os.getenv('DATABASE_USER')}:"
f"{os.getenv('DATABASE_PASSWORD')}@"
f"{os.getenv('DATABASE_HOST')}:"
f"{os.getenv('DATABASE_PORT')}/"
f"{os.getenv('DATABASE_NAME')}"
)PHP / PDO
$db = new PDO(
'mysql:host=' . getenv('DATABASE_HOST') .
';dbname=' . getenv('DATABASE_NAME'),
getenv('DATABASE_USER'),
getenv('DATABASE_PASSWORD')
);Go
dsn := fmt.Sprintf(
"%s:%s@tcp(%s:%s)/%s",
os.Getenv("DATABASE_USER"),
os.Getenv("DATABASE_PASSWORD"),
os.Getenv("DATABASE_HOST"),
os.Getenv("DATABASE_PORT"),
os.Getenv("DATABASE_NAME"),
)
db, err := sql.Open("mysql", dsn)mariadb-galera.database.svc.k8s.homeは Kubernetes クラスタ内の内部 DNS 名です。 SaaS アプリは同じクラスタ内で実行されるため、このホスト名で直接接続できます。8. インポート / エクスポート
SQL ファイルをインポートする
既存の SQL ダンプをこのデータベースに復元できます。 ファイルはブラウザ側で AES-GCM で暗号化されて送信されるため安全です。
- データベース一覧から対象のデータベースを選択
- 「インポート」 ボタンをクリック
- .sql または .sql.gz ファイルを選択(最大 50MB)
- 「インポート」 をクリック
- 公開鍵の取得 → AES 鍵生成 → 暗号化 → 送信 の流れが自動実行
- サーバー側で RSA-OAEP で AES 鍵を復号化してからデータを復号化
エクスポート(バックアップ)
Web UI からのワンクリックバックアップはまだ実装されていません。 以下の方法でコマンドラインからエクスポートしてください。
# ローカルマシンからリモート接続してダンプ mysqldump -h <HOST> -u <USER> -p -C --single-transaction \ <DATABASE_NAME> > backup_$(date +%Y%m%d).sql.gz # 例 mysqldump -h mariadb-galera.database.svc.k8s.home \ -u t_abc123_myapp -p tenant_abc123_myapp > backup.sql
-C: gzip 圧縮,--single-transaction: トランザクション分離で一貫性確保
9. 容量上限とライセンス
ライセンスプランごとに 作成可能な DB 数 と 全 DB 合計の容量上限 が決まります。 個別テーブルにサイズ上限はなく、合計容量だけが管理対象です。
ライセンス別の制限
(ライセンスガイド と共有の単一テーブルから抜粋しています)
| プラン | 最大データベース数 | DB 合計容量 |
|---|---|---|
| SAAS_FREE | 1 | 2 GB |
| SAAS_BASIC | 1 | 2 GB |
| SAAS_PRO | 1 | 2 GB |
| SAAS_ENTERPRISE | 1 | 2 GB |
| S | 1 | 2 GB |
| M | 2 | 5 GB |
| L | 3 | 10 GB |
| XL | 5 | 20 GB |
| XXL | 10 | 50 GB |
| MAX | 20 | 100 GB |
DB 数 / 容量どちらも上限に達すると「データベース作成」ボタンが無効、または書き込みが停止されます。 より広い枠が必要な場合は上位プランへアップグレードしてください。
容量超過時の挙動 (書込停止と復帰)
全 DB の合計サイズが上限を超えると、10 分間隔の sweep ジョブがINSERT / UPDATE / CREATEを REVOKE します。SELECT /DELETE は引き続き使えるので、 自分で不要データを削除して復帰できます。
復帰手順
- 不要データを
DELETEで削除する OPTIMIZE TABLE <table>;を実行する(重要)- DatabaseManager 画面の「容量を再チェック」ボタンを押す (or 次の自動 sweep を待つ)
- 合計容量が上限を下回っていれば書込権限が GRANT で復帰
DELETEだけだと削除済みページが内部に残り、ディスク使用量 (data_length + index_length) が即座には縮まりません。OPTIMIZE TABLEで実体容量が再計算されます。クイック解放例
-- mysql client で DB に接続後 DELETE FROM big_table WHERE created_at < '2025-01-01'; OPTIMIZE TABLE big_table; -- → DatabaseManager で「容量を再チェック」 → 書込再開
DROP TABLE でも OK。10. トラブルシューティング
現在のライセンスプランの最大データベース数に達しています。 新しいデータベースを作成するにはライセンスをアップグレードするか、 不要なデータベースを削除してください。
同名のデータベースが既に存在します。 別の名前を指定するか、既存のデータベースを削除してからもう一度作成してください。
以下を確認してください。
- ホスト名:
mariadb-galera.database.svc.k8s.homeを使用していますか? - 接続情報ダイアログで表示されたユーザー名、パスワードを使っていますか?
- データベースのステータスが
稼働中ですか? 「作成中」の場合は完成を待ってください。 - アプリが ToukaCloud 上で実行されていますか?(Kubernetes クラスタ内)
ユーザー名またはパスワードが間違っています。 データベース一覧から「接続情報」ボタンで再度確認してください。
以下が考えられます。
- SQL ファイルが破損している(別のツールで一度検証してください)
- ファイルサイズが 50MB を超えている
- インポート中に SQL エラーが発生した(ブラウザコンソールで詳細確認)
- 既存テーブルとの競合が発生した(テーブル名を確認してください)
データベース一覧から対象のデータベースを選択し、 「接続情報」ボタンをクリックすればパスワードを確認できます。 パスワードを変更したい場合は、データベースを削除して新規作成してください。
データベース一覧から対象のデータベースを選択し、 ゴミ箱アイコンをクリックします。 確認ダイアログで「削除」を選択すると、全てのデータが完全に削除されます(取り消し不可)。必ず事前にバックアップを取ってください。