データベースマイグレーションガイド
このドキュメントでは、MySQL データベースのスキーマ変更(マイグレーション)を実行する方法を説明します。
目次
- 概要
- マイグレーション方法の選択
- 方法1: アプリ起動時に自動実行(推奨)
- 方法2: Container Apps の Web Shell から手動実行
- マイグレーションツール別ガイド
- ベストプラクティス
- トラブルシューティング
概要
データベースマイグレーションは、データベーススキーマの変更(テーブルの作成、カラムの追加・削除など)を管理するプロセスです。
マイグレーションが必要な場面
- 新しいテーブルを追加する
- 既存のテーブルにカラムを追加・削除する
- インデックスを追加・削除する
- データ型を変更する
- テーブル間の関係を変更する
マイグレーション方法の選択
このインフラでは、以下の2つの方法でマイグレーションを実行できます:
| 方法 | メリット | デメリット | 推奨度 |
|---|---|---|---|
| 1. アプリ起動時に自動実行 | ・自動化されている ・人的ミスが少ない ・CI/CD と統合しやすい | ・起動時間が少し長くなる ・ロールバックが難しい場合がある | ⭐⭐⭐ |
| 2. Web Shell から手動実行 | ・実行タイミングを制御できる ・問題発生時に即座に対応可能 | ・手動作業が必要 ・人的ミスのリスク | ⭐⭐ |
推奨: 基本的には**方法1(アプリ起動時に自動実行)**を使用してください。開発段階や特殊なケースでは、方法2を使用することもできます。
方法1: アプリ起動時に自動実行(推奨)
概要
アプリケーションの起動時に、マイグレーションを自動的に実行する方法です。
メリット:
- デプロイ時に自動的にマイグレーションが実行される
- 人的ミスが少ない
- CI/CD パイプラインと統合しやすい
実装方法:
- アプリケーションのエントリーポイント(起動スクリプト)でマイグレーションコマンドを実行
- マイグレーション完了後、アプリケーションを起動
Python / FastAPI での実装
1. Alembic を使用
Alembic のインストール:
pip install alembic
Alembic の初期化:
alembic init alembic
alembic/env.py の設定:
import os
from sqlalchemy import engine_from_config, pool
from alembic import context
# 環境変数からDATABASE_URLを取得
config = context.config
config.set_main_option("sqlalchemy.url", os.getenv("DATABASE_URL"))
# メタデータのインポート
from app.models import Base
target_metadata = Base.metadata
def run_migrations_online():
connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata
)
with context.begin_transaction():
context.run_migrations()
run_migrations_online()
マイグレーションの作成:
# モデルの変更を検出してマイグレーションを作成
alembic revision --autogenerate -m "Add users table"
起動スクリプト(start.sh):
#!/bin/bash
set -e
echo "Running database migrations..."
alembic upgrade head
echo "Starting application..."
uvicorn app.main:app --host 0.0.0.0 --port 8000
Dockerfile での設定:
FROM python:3.11-slim
WORKDIR /app
# 依存関係のインストール
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# アプリケーションのコピー
COPY . .
# 起動スクリプトに実行権限を付与
RUN chmod +x start.sh
# 起動スクリプトを実行
CMD ["./start.sh"]
2. SQLAlchemy で直接実行
Alembic を使用しない簡易的な方法:
models.py:
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, DateTime
from datetime import datetime
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String(255), unique=True, index=True)
name = Column(String(100))
created_at = Column(DateTime, default=datetime.utcnow)
main.py:
import os
from sqlalchemy import create_engine
from models import Base
def run_migrations():
"""データベースマイグレーションを実行"""
database_url = os.getenv("DATABASE_URL")
engine = create_engine(database_url)
print("Running database migrations...")
Base.metadata.create_all(bind=engine)
print("Migrations completed")
if __name__ == "__main__":
# マイグレーションを実行
run_migrations()
# アプリケーションを起動
import uvicorn
uvicorn.run("app.main:app", host="0.0.0.0", port=8000)
Node.js / Next.js での実装
1. Prisma を使用
Prisma のインストール:
npm install @prisma/client
npm install -D prisma
Prisma の初期化:
npx prisma init
prisma/schema.prisma の設定:
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
createdAt DateTime @default(now()) @map("created_at")
@@map("users")
}
マイグレーションの作成:
npx prisma migrate dev --name add_users_table
起動スクリプト(start.sh):
#!/bin/bash
set -e
echo "Running database migrations..."
npx prisma migrate deploy
echo "Starting application..."
npm start
package.json での設定:
{
"scripts": {
"start": "node server.js",
"start:migrate": "prisma migrate deploy && npm start"
}
}
Dockerfile での設定:
FROM node:20-slim
WORKDIR /app
# Prismaの依存関係
RUN apt-get update -y && apt-get install -y openssl
# 依存関係のインストール
COPY package*.json ./
RUN npm ci
# アプリケーションのコピー
COPY . .
# Prisma Clientの生成
RUN npx prisma generate
# 起動スクリプトに実行権限を付与
RUN chmod +x start.sh
# 起動スクリプトを実行
CMD ["./start.sh"]
2. Knex.js を使用
Knex のインストール:
npm install knex mysql2
knexfile.js の設定:
module.exports = {
client: 'mysql2',
connection: process.env.DATABASE_URL,
migrations: {
directory: './migrations'
}
};
マイグレーションの作成:
npx knex migrate:make add_users_table
migrations/xxx_add_users_table.js:
exports.up = function(knex) {
return knex.schema.createTable('users', function(table) {
table.increments('id').primary();
table.string('email', 255).unique().notNullable();
table.string('name', 100);
table.timestamp('created_at').defaultTo(knex.fn.now());
});
};
exports.down = function(knex) {
return knex.schema.dropTable('users');
};
起動スクリプト(start.sh):
#!/bin/bash
set -e
echo "Running database migrations..."
npx knex migrate:latest
echo "Starting application..."
npm start
Docker Compose での実装
開発環境で Docker Compose を使用する場合:
docker-compose.yml:
version: '3.8'
services:
backend:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=mysql://user:password@db:3306/myapp
depends_on:
db:
condition: service_healthy
command: >
sh -c "
echo 'Running migrations...' &&
alembic upgrade head &&
echo 'Starting application...' &&
uvicorn app.main:app --host 0.0.0.0 --port 8000
"
db:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=rootpassword
- MYSQL_DATABASE=myapp
- MYSQL_USER=user
- MYSQL_PASSWORD=password
ports:
- "3306:3306"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
方法2: Container Apps の Web Shell から手動実行
概要
Azure Portal の Container Apps で Web Shell(コンソール)を開き、マイグレーションコマンドを手動で実行する方法です。
使用する場面:
- 開発段階でマイグレーションをテストする
- 本番環境で慎重にマイグレーションを実行する
- 特殊なマイグレーション(データ変換など)を実行する
手順
1. Azure Portal で Container Apps を開く
- Azure Portal を開く
- リソースグループを選択(例:
rg-myapp-dev) - Container App を選択(例:
ca-myapp-backend-dev)
2. コンソールを開く
- 左メニューから Console を選択
- Choose で
bashまたはshを選択 - Connect をクリック
3. マイグレーションコマンドを実行
Python / Alembic の場合:
# 現在のマイグレーション状態を確認
alembic current
# マイグレーションを実行
alembic upgrade head
# マイグレーション履歴を確認
alembic history
Node.js / Prisma の場合:
# 現在のマイグレーション状態を確認
npx prisma migrate status
# マイグレーションを実行
npx prisma migrate deploy
# マイグレーション履歴を確認
npx prisma migrate status
Node.js / Knex の場合:
# 現在のマイグレーション状態を確認
npx knex migrate:currentVersion
# マイグレーションを実行
npx knex migrate:latest
# マイグレーション履歴を確認
npx knex migrate:list
4. 実行結果を確認
マイグレーションが成功したことを確認します。
# テーブルが作成されたか確認(mysqlクライアントがある場合)
mysql -h $MYSQL_HOST -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE -e "SHOW TABLES;"
マイグレーションツール別ガイド
Alembic(Python)
公式ドキュメント: https://alembic.sqlalchemy.org/
よく使うコマンド:
# 新しいマイグレーションを作成(自動生成)
alembic revision --autogenerate -m "Add column to users"
# 新しいマイグレーションを作成(手動)
alembic revision -m "Custom migration"
# マイグレーションを実行
alembic upgrade head
# マイグレーションをロールバック(1つ前)
alembic downgrade -1
# 特定のバージョンにダウングレード
alembic downgrade <revision_id>
# 現在のバージョンを確認
alembic current
# マイグレーション履歴を表示
alembic history
Prisma(Node.js)
公式ドキュメント: https://www.prisma.io/docs/
よく使うコマンド:
# スキーマを変更してマイグレーションを作成(開発環境)
npx prisma migrate dev --name add_column_to_users
# マイグレーションを実行(本番環境)
npx prisma migrate deploy
# マイグレーション状態を確認
npx prisma migrate status
# スキーマをリセット(開発環境のみ)
npx prisma migrate reset
# Prisma Clientを再生成
npx prisma generate
Knex.js(Node.js)
公式ドキュメント: https://knexjs.org/
よく使うコマンド:
# 新しいマイグレーションを作成
npx knex migrate:make migration_name
# マイグレーションを実行
npx knex migrate:latest
# マイグレーションをロールバック(最後の1つ)
npx knex migrate:rollback
# マイグレーションをロールバック(すべて)
npx knex migrate:rollback --all
# 現在のバージョンを確認
npx knex migrate:currentVersion
# マイグレーション一覧を表示
npx knex migrate:list
ベストプラクティス
1. マイグレーションファイルはバージョン管理する
マイグレーションファイルは Git で管理し、チーム全体で共有します。
git add alembic/versions/*.py
git commit -m "Add migration for users table"
git push
2. 本番環境では慎重に実行する
本番環境でマイグレーションを実行する前に:
- 開発環境でテスト: 必ず開発環境で動作確認
- バックアップを取得: データベースのバックアップを取得
- ロールバック計画を準備: 問題が発生した場合の対応を準備
- メンテナンスウィンドウを設定: 可能であれば、メンテナンス時間を設定
3. データ変更を伴うマイグレーションは分割する
大規模なデータ変更を伴うマイグレーションは、複数のステップに分割します。
例: カラム名の変更
# ステップ1: 新しいカラムを追加
alembic revision --autogenerate -m "Add new_column"
# ステップ2: データを移行(アプリケーションコードで実施)
# ステップ3: 古いカラムを削除
alembic revision --autogenerate -m "Remove old_column"
4. マイグレーションは前方のみを推奨
本番環境では、マイグレーションのロールバック(downgrade)は避け、新しいマイグレーションで修正します。
理由:
- データ損失のリスクが高い
- ロールバックスクリプトが正しく動作しない場合がある
5. マイグレーション実行中のダウンタイムを最小化
戦略:
- Blue-Green デプロイメント: 新旧両方のスキーマに対応するアプリケーションをデプロイ
- 後方互換性を保つ: マイグレーション後も古いアプリケーションが動作するようにする
- オンラインスキーマ変更ツールを使用:
pt-online-schema-changeなど
6. マイグレーション実行をログに記録
マイグレーションの実行状況をログに記録し、監視します。
Python:
import logging
logger = logging.getLogger(__name__)
def run_migrations():
try:
logger.info("Starting database migrations...")
# マイグレーション実行
logger.info("Database migrations completed successfully")
except Exception as e:
logger.error(f"Database migration failed: {e}")
raise
Node.js:
console.log('Starting database migrations...');
try {
// マイグレーション実行
console.log('Database migrations completed successfully');
} catch (error) {
console.error('Database migration failed:', error);
throw error;
}
トラブルシューティング
マイグレーションが失敗する
症状: マイグレーションコマンドがエラーで終了する
原因と対処:
- データベース接続エラー
# 環境変数を確認
echo $DATABASE_URL
- マイグレーションファイルの構文エラー
マイグレーションファイルを確認し、構文エラーを修正します。
- 既存データとの競合
既存データが新しいスキーマと互換性がない場合、データを修正するマイグレーションを作成します。
マイグレーション状態が不整合になった
症状: alembic current や prisma migrate status が期待と異なる結果を返す
対処:
Alembic の場合:
# マイグレーション履歴テーブルを確認
mysql -h $MYSQL_HOST -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE -e "SELECT * FROM alembic_version;"
# 手動で修正(注意: データベースの状態を理解してから実行)
alembic stamp head
Prisma の場合:
# マイグレーション履歴を確認
npx prisma migrate status
# マイグレーション履歴をリセット(開発環境のみ)
npx prisma migrate reset
# 本番環境では、マイグレーション履歴を手動で修正
npx prisma migrate resolve --applied <migration_name>
アプリケーションが起動しない(マイグレーション失敗)
症状: アプリケーションが起動時にマイグレーションでエラーになる
対処:
- Container Apps のログを確認
az containerapp logs show \
--name ca-myapp-backend-dev \
--resource-group rg-myapp-dev \
--follow
- Web Shell から手動でマイグレーションを実行
方法2(Web Shell から手動実行)を参照して、手動でマイグレーションを実行します。
- 一時的にマイグレーション自動実行を無効化
起動スクリプトを変更して、マイグレーションをスキップします。
start.sh:
#!/bin/bash
set -e
# マイグレーションをコメントアウト
# echo "Running database migrations..."
# alembic upgrade head
echo "Starting application..."
uvicorn app.main:app --host 0.0.0.0 --port 8000
ロールバックできない
症状: alembic downgrade や knex migrate:rollback が失敗する
原因:
down/downgradeメソッドが実装されていない- データが既に変更されている
対処:
- 新しいマイグレーションで修正
ロールバックせず、新しいマイグレーションで修正します(推奨)。
- 手動でスキーマを修正
Web Shell から手動で SQL を実行してスキーマを修正します(注意して実行)。
mysql -h $MYSQL_HOST -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE
# テーブルを削除
DROP TABLE IF EXISTS users;
# カラムを削除
ALTER TABLE users DROP COLUMN email;
まとめ
- 推奨方法: アプリ起動時に自動実行(方法1)
- 手動実行: Web Shell から実行も可能(方法2)
- 本番環境では慎重に: バックアップを取得し、テスト環境で確認
- マイグレーションファイルはバージョン管理: Git で管理
- ロールバックは避ける: 新しいマイグレーションで修正
詳細は以下のドキュメントも参照してください:
- MySQL 接続ガイド - MySQL 接続方法
- 環境変数ガイド - 環境変数の取得方法
- CI/CDの使い方 - CI/CD でのデプロイ