メインコンテンツまでスキップ

データベースマイグレーションガイド

このドキュメントでは、MySQL データベースのスキーマ変更(マイグレーション)を実行する方法を説明します。

目次


概要

データベースマイグレーションは、データベーススキーマの変更(テーブルの作成、カラムの追加・削除など)を管理するプロセスです。

マイグレーションが必要な場面

  • 新しいテーブルを追加する
  • 既存のテーブルにカラムを追加・削除する
  • インデックスを追加・削除する
  • データ型を変更する
  • テーブル間の関係を変更する

マイグレーション方法の選択

このインフラでは、以下の2つの方法でマイグレーションを実行できます:

方法メリットデメリット推奨度
1. アプリ起動時に自動実行・自動化されている
・人的ミスが少ない
・CI/CD と統合しやすい
・起動時間が少し長くなる
・ロールバックが難しい場合がある
⭐⭐⭐
2. Web Shell から手動実行・実行タイミングを制御できる
・問題発生時に即座に対応可能
・手動作業が必要
・人的ミスのリスク
⭐⭐

推奨: 基本的には**方法1(アプリ起動時に自動実行)**を使用してください。開発段階や特殊なケースでは、方法2を使用することもできます。


方法1: アプリ起動時に自動実行(推奨)

概要

アプリケーションの起動時に、マイグレーションを自動的に実行する方法です。

メリット:

  • デプロイ時に自動的にマイグレーションが実行される
  • 人的ミスが少ない
  • CI/CD パイプラインと統合しやすい

実装方法:

  1. アプリケーションのエントリーポイント(起動スクリプト)でマイグレーションコマンドを実行
  2. マイグレーション完了後、アプリケーションを起動

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 を開く

  1. Azure Portal を開く
  2. リソースグループを選択(例: rg-myapp-dev
  3. Container App を選択(例: ca-myapp-backend-dev

2. コンソールを開く

  1. 左メニューから Console を選択
  2. Choosebash または sh を選択
  3. 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. 本番環境では慎重に実行する

本番環境でマイグレーションを実行する前に:

  1. 開発環境でテスト: 必ず開発環境で動作確認
  2. バックアップを取得: データベースのバックアップを取得
  3. ロールバック計画を準備: 問題が発生した場合の対応を準備
  4. メンテナンスウィンドウを設定: 可能であれば、メンテナンス時間を設定

3. データ変更を伴うマイグレーションは分割する

大規模なデータ変更を伴うマイグレーションは、複数のステップに分割します。

: カラム名の変更

# ステップ1: 新しいカラムを追加
alembic revision --autogenerate -m "Add new_column"

# ステップ2: データを移行(アプリケーションコードで実施)

# ステップ3: 古いカラムを削除
alembic revision --autogenerate -m "Remove old_column"

4. マイグレーションは前方のみを推奨

本番環境では、マイグレーションのロールバック(downgrade)は避け、新しいマイグレーションで修正します。

理由:

  • データ損失のリスクが高い
  • ロールバックスクリプトが正しく動作しない場合がある

5. マイグレーション実行中のダウンタイムを最小化

戦略:

  1. Blue-Green デプロイメント: 新旧両方のスキーマに対応するアプリケーションをデプロイ
  2. 後方互換性を保つ: マイグレーション後も古いアプリケーションが動作するようにする
  3. オンラインスキーマ変更ツールを使用: 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;
}

トラブルシューティング

マイグレーションが失敗する

症状: マイグレーションコマンドがエラーで終了する

原因と対処:

  1. データベース接続エラー
# 環境変数を確認
echo $DATABASE_URL
  1. マイグレーションファイルの構文エラー

マイグレーションファイルを確認し、構文エラーを修正します。

  1. 既存データとの競合

既存データが新しいスキーマと互換性がない場合、データを修正するマイグレーションを作成します。

マイグレーション状態が不整合になった

症状: alembic currentprisma 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>

アプリケーションが起動しない(マイグレーション失敗)

症状: アプリケーションが起動時にマイグレーションでエラーになる

対処:

  1. Container Apps のログを確認
az containerapp logs show \
--name ca-myapp-backend-dev \
--resource-group rg-myapp-dev \
--follow
  1. Web Shell から手動でマイグレーションを実行

方法2(Web Shell から手動実行)を参照して、手動でマイグレーションを実行します。

  1. 一時的にマイグレーション自動実行を無効化

起動スクリプトを変更して、マイグレーションをスキップします。

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 downgradeknex migrate:rollback が失敗する

原因:

  • down / downgrade メソッドが実装されていない
  • データが既に変更されている

対処:

  1. 新しいマイグレーションで修正

ロールバックせず、新しいマイグレーションで修正します(推奨)。

  1. 手動でスキーマを修正

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 で管理
  • ロールバックは避ける: 新しいマイグレーションで修正

詳細は以下のドキュメントも参照してください: