Atlasによる宣言型データベーススキーマ管理:PostgreSQLとMySQLのマイグレーション自動化

Database tutorial - IT technology blog
Database tutorial - IT technology blog

課題:なぜ命令型マイグレーションはスケールすると破綻するのか

多くのエンジニアリングチームは、自動化されたCI/CDパイプラインを活用して1日に何十回もアプリケーションコードをデプロイしています。しかし、データベースの変更は依然として脆弱な手動ワークフローに取り残されがちです。一般的に、開発チームはFlywayやLiquibaseといった命令型のマイグレーションツールに依存しています。スキーマを変更するたびに、エンジニアは明示的なALTER TABLE文を含むV14__add_status_to_users.sqlのようなインクリメンタルなスクリプトを手作業で作成しなければなりません。

このパターンは、チームの規模が拡大するにつれて破綻しやすくなります。同じ日の午後に3人のエンジニアがmainブランチから分岐して作業を開始したと想像してみてください。そのうちの2人がマイグレーションファイルのプレフィックスに偶然V15を割り当ててしまいました。Git上のマージは競合なく完了しますが、シーケンス番号の重複による衝突が発生し、本番ロールアウト時にデプロイパイプラインがクラッシュしてしまいます。

さらに深刻なのがスキーマドリフト(schema drift)です。深夜3時のインシデント対応中、エンジニアがCPU使用率のスパイクを抑えるために本番環境のレプリカに直接インデックスを追加したとします。この緊急対応によるインデックスはGitリポジトリには反映されません。数か月後、本番環境とローカル開発環境の乖離が原因で、そのテーブルに対する新たなマイグレーションが失敗したり、予期せぬ挙動を引き起こしたりします。

命令型マイグレーションでは、中間ステップを「どのように(how)」実行するかを指定せざるを得ず、各環境において最終状態が「どうあるべきか(what)」を検証することが困難です。

解決策:Atlasによる宣言型スキーマ管理

クラウドエンジニアリングの世界では、この課題は10年前に解決されています。TerraformやKubernetesといったツールが、脆弱なシェルスクリプトを宣言型の状態定義へと置き換えました。コード上で「あるべき状態(desired state)」を宣言すれば、調整エンジン(reconciliation engine)がそこに到達するために必要なステップを自動計算してくれます。

Atlasはこのワークフローをリレーショナルデータベースにもたらします。手続き型のDDLスクリプトを手作業で作成する代わりに、プレーンなSQL、Atlas HCL、またはORMモデル(Prisma、GORM、SQLAlchemyなど)であるべき状態を定義します。Atlasが稼働中のデータベースとあるべき状態との差分(diff)を厳密に計算し、適切なマイグレーションステートメントを自動生成します。

このアプローチの真価は、リレーショナルデータベース製品ごとの差異を扱う際に現れます。PostgreSQLはトランザクショナルDDLをサポートしているため、失敗時にもスキーマ更新を安全にロールバックできます。対照的に、MySQL 8.0はDDLステートメントをアトミックなトランザクションでラップしません。1500万行のMySQLテーブルで誤ってテーブルロックが発生すると、何分間もトラフィックが停止する恐れがあります。Atlasはこうした製品ごとの方言や挙動の違いを標準で把握しています。Atlasには2つの運用モードが用意されています:

  • Schema Apply(直接宣言型):Atlasが対象データベースを検証し、差分を即座に適用します。このワークフローは、ローカル開発、レビューアプリ、一時的なテスト環境向けに設計されています。
  • Versioned Migrations(宣言型主導のバージョン管理マイグレーション):Atlasがあるべき状態とGit管理されたSQLファイルを比較し、プルリクエストのレビューや本番CI/CDパイプライン向けに、タイムスタンプ付きの決定論的なマイグレーションスクリプトを生成します。

実践ガイド:PostgreSQLとMySQLでのハンズオン

1. インストール

ワークステーションまたはCIエージェントにスタンドアロンバイナリをインストールします:

# macOS / Linux
curl -sSf https://atlasgo.sh | sh

# バイナリの準備が完了したか確認
atlas version

2. SQLであるべき状態を定義する

schemaというディレクトリを作成します。AtlasはHCLをサポートしていますが、プレーンなSQL DDLファイルも特別な設定構文なしでそのまま使用できます。以下の定義をschema/schema.sql内に保存します:

-- schema/schema.sql
CREATE TABLE users (
    id BIGSERIAL PRIMARY KEY,
    email VARCHAR(255) NOT NULL UNIQUE,
    full_name VARCHAR(120) NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE orders (
    id BIGSERIAL PRIMARY KEY,
    user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    amount NUMERIC(10, 2) NOT NULL DEFAULT 0.00,
    status VARCHAR(50) NOT NULL DEFAULT 'pending',
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_orders_user_status ON orders(user_id, status);

3. 既存データベースのインスペクション

新規プロジェクトを完全にゼロから立ち上げるケースは稀です。既存のデータベースを宣言型管理に移行する場合は、schema inspectを使用して現在の状態をダンプします:

# PostgreSQLの場合
atlas schema inspect \
  -u "postgres://postgres:secret@localhost:5432/store_prod?sslmode=disable" \
  > current_schema.sql

# MySQLの場合
atlas schema inspect \
  -u "mysql://root:secret@localhost:3306/store_prod" \
  > current_schema.sql

4. ローカル開発環境で変更を直接適用する

ローカルのDockerコンテナを最新のschema.sqlの変更と同期させたい場合、マイグレーションスクリプトを手動で作成することなくschema applyを実行できます:

atlas schema apply \
  -u "postgres://postgres:secret@localhost:5432/store_dev?sslmode=disable" \
  --to "file://schema/schema.sql" \
  --dev-url "docker://postgres/16/dev"

--dev-urlフラグは、Atlasに使い捨てのコンテナ環境を提供します。Atlasはまずこの一時インスタンスにスキーマをロードし、対象データベースを変更する前に構文の検証、テーブル間の依存関係の解決、安全な差分シミュレーションを行います。

5. バージョン管理されたマイグレーションファイルの生成

スキーマの直接変更は開発環境にとどめるべきであり、本番環境で行うべきではありません。厳格に管理されるステージングや本番環境では、監査やピアレビューのためにGitにコミットされたイミュータブル(不変)なSQLファイルが求められます。Atlasは必要な差分を計算し、マイグレーションスクリプトを自動生成することでこの要求を満たします:

# マイグレーションディレクトリの初期化
mkdir -p migrations

# schema.sqlとmigrations/を比較してバージョン管理マイグレーションを生成
atlas migrate diff add_orders_and_users \
  --dir "file://migrations" \
  --to "file://schema/schema.sql" \
  --dev-url "docker://postgres/16/dev"

Atlasはmigrations/内に2つのファイルを生成します:

  • migrations/20261102110130_add_orders_and_users.sql:スキーマ更新に必要な正確なDDLステートメント。
  • migrations/atlas.sum:暗号化された整合性チェックサムファイル。他のエンジニアがマージ済みのマイグレーションスクリプトを誤って変更した場合、チェックサムの不一致が検出され、パイプラインでの意図しないドリフトを防止します。

6. デプロイ前のリンティングとCI/CDでの実行

破壊的な変更がステージング環境に届く前に検知します。GitHub Actions内でatlas migrate lintを実行すると、データが存在するカラムの削除や、高負荷なテーブルに対する非コンカレントなインデックス作成など、安全でないパターンをチェックできます:

# CI環境でmainブランチのベースラインに対してスキーマのリンティングを実行
atlas migrate lint \
  --dir "file://migrations" \
  --dev-url "docker://postgres/16/dev" \
  --latest 1

承認されてマージされたら、コンテナのリリースジョブ中に生成されたマイグレーションをデプロイします:

atlas migrate apply \
  --dir "file://migrations" \
  --url "postgres://db_admin:${DB_PASSWORD}@prod-primary.internal:5432/store_prod"

まとめ

命令型のマイグレーションスクリプトは、スキーマの保守をエラーの起きやすい煩雑な作業にしてしまいます。Atlasによる宣言型スキーマ管理を採用することで、データベースのライフサイクルを最新のCI/CD標準と統合できます。あるべき状態を一度定義すれば、エンジンが検証済みのDDLを生成し、整合性ハッシュによってPostgreSQLおよびMySQLデータベースをあらゆる環境間で完全に同期させることができます。

Share: