CI/CDにおけるFlywayを活用したMySQLスキーママイグレーションとバージョン管理ガイド

MySQL tutorial - IT technology blog
MySQL tutorial - IT technology blog

背景と必要性

時刻は深夜2時ちょうど。バックエンドクラスタで500エラーが多発し、PagerDutyのアラートが鳴り響きます。深夜リリース後、チェックアウトAPIが完全に停止しました。サーバーログを開くと、原因は一目瞭然でした。Unknown column 'discount_rate' in 'field list'

原因は、Kubernetesが割引計算ロジックを含む新しいイメージをプルしたものの、開発者がプルリクエストのマージ前に手動でSQLスクリプトを実行し忘れたため、MySQLのordersテーブルが古いスキーマのままになっていたことでした。

深夜の場当たり的なパッチ適用は非常に危険です。数百GB規模の本番データベースに対してSSH経由でスクリプトを貼り付けると、テーブルロックやデータ損失を引き起こしかねません。環境(Dev、Staging、Prod)が増えるにつれ、コードとデータベース間のスキーマ不整合(スキーマドリフト)はダウンタイムの最大の要因となります。

Flywayはこのリスクを根本から解決します。すべてのスキーマ変更を明確なバージョン番号付きのスクリプトファイルに変換し、チェックサムハッシュを自動検証してCI/CDパイプライン内でマイグレーションを実行します。

# 深夜2時にコードとデータベースのバージョン不整合で発生する典型的なエラー
ERROR 1054 (42S22): Unknown column 'discount_rate' in 'orders'

インストール

FlywayのCommunity Editionは完全無料で利用できます。スタンドアロンCLI、Dockerコンテナ、またはMaven/Gradleへの統合に対応しています。GitHub ActionsやGitLab CIなどのモダンなCI/CDパイプラインでは、DockerイメージまたはCLIバイナリを使用するのが最も軽量な選択肢です。

LinuxサーバーへのFlyway CLIのインストール

Mavenリポジトリから公式リリースを直接ダウンロードします。

# Flyway CLIをダウンロードして展開
cd /tmp
wget -qO- https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/10.10.0/flyway-commandline-10.10.0-linux-x64.tar.gz | tar -xvz

# システムディレクトリへ移動
sudo mv flyway-10.10.0 /opt/flyway
sudo ln -s /opt/flyway/flyway /usr/local/bin/flyway

# インストールしたバージョンを確認
flyway -v

プロジェクトディレクトリ構成の作成

設定ファイルとSQLマイグレーションファイルを配置するワークスペースを整理します。

mkdir -p ~/mysql-flyway-migration/{sql,config}
cd ~/mysql-flyway-migration

詳細設定

Flywayはプレフィックスと2つのアンダースコア(__)に基づく命名規則に従ってマイグレーションファイルをスキャンします。

  • V<Version>__<description>.sql: バージョン管理されたマイグレーション(例: V1.0__create_users_table.sql, V1.1__add_discount_to_orders.sql)。各ファイルはバージョン番号の昇順で1回のみ実行されます。
  • U<Version>__<description>.sql: ロールバック用のUndoマイグレーション(Teams/Enterpriseエディションのみ対応)。
  • R__<description>.sql: リピータブルマイグレーション。ファイル内容が変更されるたびに再実行され、View、Stored Procedure、Functionの管理に最適です。

Flyway設定ファイルの作成

MySQL接続情報を定義するconfig/flyway.confを作成します。

# config/flyway.conf
flyway.url=jdbc:mysql://127.0.0.1:3306/ecommerce_db?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
flyway.user=flyway_user
flyway.password=SuperSecretPassword123!
flyway.locations=filesystem:sql
flyway.table=flyway_schema_history
flyway.baselineOnMigrate=true
flyway.baselineVersion=0.0
flyway.validateOnMigrate=true
flyway.cleanDisabled=true

セキュリティ上の注意: 本番環境では必ずflyway.cleanDisabled=trueを設定してください。このフラグは、誤ったflyway cleanコマンドによる全テーブルおよびデータの完全削除を防ぎます。

サンプルマイグレーションスクリプトの作成

まず、sql/V1.0__init_schema.sqlに初期テーブル構造を作成します。

-- sql/V1.0__init_schema.sql
CREATE TABLE IF NOT EXISTS users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    email VARCHAR(255) NOT NULL UNIQUE,
    full_name VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS orders (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT NOT NULL,
    total_amount DECIMAL(12, 2) NOT NULL DEFAULT 0.00,
    status VARCHAR(50) NOT NULL DEFAULT 'PENDING',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

次に、割引率カラムを追加するsql/V1.1__add_discount_to_orders.sqlを作成します。

-- sql/V1.1__add_discount_to_orders.sql
ALTER TABLE orders 
ADD COLUMN discount_rate DECIMAL(5, 2) NOT NULL DEFAULT 0.00 AFTER total_amount,
ADD COLUMN updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;

CI/CDパイプラインへのFlywayの統合(GitHub Actions)

手動実行の代わりに、アプリのロールアウト前にスキーマを自動マイグレーションするワークフロー.github/workflows/db-migration.ymlを設定します。

name: Database Migration Pipeline

on:
  push:
    branches:
      - main
    paths:
      - 'sql/**'

jobs:
  migrate:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Run Flyway Migration
        uses: docker://flyway/flyway:10.10.0
        with:
          args: >-
            -url=jdbc:mysql://${{ secrets.DB_HOST }}:3306/${{ secrets.DB_NAME }}?useSSL=false&allowPublicKeyRetrieval=true
            -user=${{ secrets.DB_USER }}
            -password=${{ secrets.DB_PASSWORD }}
            -locations=filesystem:sql
            -connectRetries=10
            migrate

検証と運用

実際にマイグレーションを実行する前に、CLIコマンドで保留中のスクリプト一覧を確認することをおすすめします。

CLIによるマイグレーション状態の確認

# 履歴と未適用のバージョンを詳細表示
flyway -configFiles=config/flyway.conf info

# マイグレーションを実行
flyway -configFiles=config/flyway.conf migrate

migrateコマンドが完了すると、FlywayはMySQL内にflyway_schema_historyテーブルを自動作成します。このテーブルには、スクリプトのSHA-256チェックサム、ミリ秒単位の実行時間、successステータスフラグなど、すべての履歴が記録されます。

MySQLのメタデータテーブルの直接確認

-- スキーマ履歴テーブルを確認
SELECT installed_rank, version, description, type, script, checksum, installed_on, execution_time, success 
FROM flyway_schema_history 
ORDER BY installed_rank DESC;

チェックサム不整合(Checksum Mismatch)の対処

よくあるトラブルとして、StagingやProductionに適用済みの古いV1.0__init_schema.sqlファイルを誤って変更してしまうケースがあります。再実行時にFlywayは現在のファイルハッシュがDB内のチェックサムと異なることを検知し、処理を即座に停止します。

ERROR: Validate failed: Migrations have failed validation
Migration checksum mismatch for migration version 1.0
- Applied to database : 1948294012
- Resolved locally    : -492810481

鉄則: リリース済みのマイグレーションファイルは絶対に編集しないでください。常に新しいバージョンを作成しましょう(例: V1.2__fix_orders_index.sql)。空白やコメントのみの変更で、SQLを再実行せずにハッシュのみを更新したい場合は、repairコマンドを使用します。

# DB内のチェックサムをディスク上のファイルと再同期
flyway -configFiles=config/flyway.conf repair

FlywayをCI/CDパイプラインに統合することで、チームはスキーマ履歴を100%管理できるようになります。手動でのSQL実行忘れや、カラム不足による深夜の緊急呼び出しはもう発生しません。

Share: