Go + Atlas で始めるスキーママイグレーション
はじめに
データベースのスキーマ管理は、アプリケーション開発において重要な課題です。テーブルの追加やカラムの変更を安全に行い、チーム全員が同じスキーマで開発できる環境を整える必要があります。
本記事では、Atlasを使ったデータベースマイグレーションの方法を紹介します。
Atlasとは
Atlasは、Arigaが開発しているオープンソースのデータベーススキーマ管理ツールです。
対応データベース:
- MySQL / MariaDB
- PostgreSQL
- SQLite
- SQL Server
- ClickHouse
従来のマイグレーションツールとの違い
| 特徴 | 従来のツール(goose, migrate等) | Atlas |
|---|---|---|
| マイグレーション方式 | バージョン管理型(Up/Down) | 宣言的 + バージョン管理型 |
| スキーマ定義 | SQLファイル | HCL / SQL / ORM連携 |
| 差分検出 | 手動で記述 | 自動で差分を検出 |
| ドライラン | ツールによる | 標準サポート |
なぜAtlasを選ぶのか
- 宣言的アプローチ: 理想のスキーマを定義すれば、現在の状態との差分を自動計算
- 安全性: 破壊的変更の検出、ドライラン機能
- 柔軟性: HCL、SQL、ORMスキーマからの生成に対応
- CI/CD連携: GitHub Actions等との統合が容易
Atlasのインストール
macOS
Homebrewを使ってインストールします。
brew install ariga/tap/atlas
Linux
curlでインストールスクリプトを実行します。
curl -sSf https://atlasgo.sh | sh
Windows
Scoopを使ってインストールします。
scoop install atlas
Dockerを使う場合
docker pull arigaio/atlas
バージョン確認
インストールが完了したら、バージョンを確認します。
atlas version
# atlas version v0.21.0
プロジェクトのセットアップ
ディレクトリ構成
Goプロジェクトでの推奨ディレクトリ構成です。
myapp/
├── atlas.hcl # Atlas設定ファイル
├── schema/
│ └── schema.sql # スキーマ定義(SQL形式の場合)
├── migrations/ # マイグレーションファイル
│ ├── 20240101000000_create_users.sql
│ └── atlas.sum
├── main.go
└── go.mod
atlas.hcl の設定
プロジェクトルートにatlas.hclを作成します。
# 環境変数の定義
variable "db_url" {
type = string
default = getenv("DATABASE_URL")
}
# 開発環境の設定
env "local" {
# マイグレーションファイルの場所
migration {
dir = "file://migrations"
}
# 接続先データベース
url = var.db_url
# 開発用データベース(スキーマ検証用)
dev = "docker://mysql/8/dev"
}
# 本番環境の設定
env "production" {
migration {
dir = "file://migrations"
}
url = var.db_url
}
環境変数の設定
.envファイルまたは環境変数でデータベースURLを設定します。
# MySQL
export DATABASE_URL="mysql://user:password@localhost:3306/myapp"
# PostgreSQL
export DATABASE_URL="postgres://user:password@localhost:5432/myapp?sslmode=disable"
# SQLite
export DATABASE_URL="sqlite://./myapp.db"
スキーマの定義
Atlasでは、HCL形式またはSQL形式でスキーマを定義できます。
SQL形式でのスキーマ定義
最もシンプルな方法は、SQLファイルでスキーマを定義することです。
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) NOT NULL UNIQUE,
name VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE posts (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
title VARCHAR(255) NOT NULL,
content TEXT,
published_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_posts_published_at ON posts(published_at);
HCL形式でのスキーマ定義
HCL形式では、より構造化された記述が可能です。
table "users" {
schema = schema.myapp
column "id" {
type = bigint
auto_increment = true
}
column "email" {
type = varchar(255)
null = false
}
column "name" {
type = varchar(100)
null = false
}
column "created_at" {
type = timestamp
default = sql("CURRENT_TIMESTAMP")
}
primary_key {
columns = [column.id]
}
index "idx_email" {
columns = [column.email]
unique = true
}
}
table "posts" {
schema = schema.myapp
column "id" {
type = bigint
auto_increment = true
}
column "user_id" {
type = bigint
null = false
}
column "title" {
type = varchar(255)
null = false
}
column "content" {
type = text
null = true
}
primary_key {
columns = [column.id]
}
foreign_key "fk_user" {
columns = [column.user_id]
ref_columns = [table.users.column.id]
on_delete = CASCADE
}
index "idx_user_id" {
columns = [column.user_id]
}
}
どちらを選ぶべきか
| 形式 | メリット | デメリット |
|---|---|---|
| SQL | 馴染みがある、既存SQLを流用可能 | 複雑なロジックが書きにくい |
| HCL | 構造化、再利用しやすい | 学習コストがある |
既存プロジェクトではSQL形式、新規プロジェクトではHCL形式がおすすめです。
マイグレーションの実行
Atlasには2つのマイグレーション方式があります。
1. 宣言的マイグレーション(schema apply)
理想のスキーマを定義し、現在のDBとの差分を自動で適用する方式です。
# ドライラン(実際には適用しない)
atlas schema apply \
--url "mysql://user:pass@localhost:3306/myapp" \
--to "file://schema/schema.sql" \
--dry-run
# 適用
atlas schema apply \
--url "mysql://user:pass@localhost:3306/myapp" \
--to "file://schema/schema.sql"
出力例:
-- Planned Changes:
-- Create "users" table
CREATE TABLE `users` (
`id` bigint NOT NULL AUTO_INCREMENT,
`email` varchar(255) NOT NULL,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `email` (`email`)
);
-- Create "posts" table
...
? Are you sure you want to apply these changes? [y/N]
2. バージョン管理型マイグレーション(migrate)
従来のUp/Down形式のマイグレーションです。チームでの開発や本番環境での利用に適しています。
マイグレーションファイルの生成
# スキーマからマイグレーションファイルを生成
atlas migrate diff create_users \
--env local \
--to "file://schema/schema.sql"
これにより、migrations/ディレクトリにファイルが生成されます。
migrations/
├── 20240101120000_create_users.sql
└── atlas.sum
生成されたマイグレーションファイル:
-- Create "users" table
CREATE TABLE `users` (
`id` bigint NOT NULL AUTO_INCREMENT,
`email` varchar(255) NOT NULL,
`name` varchar(100) NOT NULL,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE INDEX `email` (`email`)
);
マイグレーションの適用
# ステータス確認
atlas migrate status --env local
# マイグレーション適用
atlas migrate apply --env local
マイグレーションのロールバック
# 1つ前の状態に戻す
atlas migrate down --env local
# 特定のバージョンまで戻す
atlas migrate down --env local --to-version 20240101000000
どちらの方式を選ぶべきか
| 方式 | ユースケース |
|---|---|
| 宣言的(schema apply) | 開発初期、個人開発、プロトタイプ |
| バージョン管理型(migrate) | チーム開発、本番環境、履歴管理が必要な場合 |
本番環境ではバージョン管理型を推奨します。
実践的な使い方
開発環境でのワークフロー
- スキーマファイルを編集
- マイグレーションファイルを生成
- レビュー後、コミット
- ローカルDBに適用
# 1. スキーマを編集後、差分を生成
atlas migrate diff add_user_status \
--env local \
--to "file://schema/schema.sql"
# 2. 生成されたファイルを確認
cat migrations/20240102120000_add_user_status.sql
# 3. ローカルDBに適用
atlas migrate apply --env local
# 4. コミット
git add migrations/
git commit -m "Add user status column"
Makefileでの管理
よく使うコマンドをMakefileにまとめておくと便利です。
.PHONY: migrate-diff migrate-apply migrate-status migrate-down
# マイグレーションファイル生成
migrate-diff:
@read -p "Migration name: " name; \
atlas migrate diff $$name --env local --to "file://schema/schema.sql"
# マイグレーション適用
migrate-apply:
atlas migrate apply --env local
# ステータス確認
migrate-status:
atlas migrate status --env local
# ロールバック
migrate-down:
atlas migrate down --env local
使用例:
make migrate-diff # プロンプトで名前を入力
make migrate-apply
make migrate-status
CI/CDでの活用(GitHub Actions)
PRでマイグレーションファイルを検証する例です。
name: Atlas CI
on:
pull_request:
paths:
- 'migrations/**'
- 'schema/**'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Atlas
uses: ariga/setup-atlas@v0
- name: Lint migrations
run: |
atlas migrate lint \
--env local \
--dev-url "docker://mysql/8/dev" \
--latest 1
verify:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8
env:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: test
ports:
- 3306:3306
steps:
- uses: actions/checkout@v4
- name: Setup Atlas
uses: ariga/setup-atlas@v0
- name: Apply migrations
run: |
atlas migrate apply \
--url "mysql://root:password@localhost:3306/test"
既存のDBからスキーマを取得
既存のデータベースからスキーマを取得することもできます。
# SQLファイルとして出力
atlas schema inspect \
--url "mysql://user:pass@localhost:3306/myapp" \
--format '{{ sql . }}' > schema/schema.sql
# HCLファイルとして出力
atlas schema inspect \
--url "mysql://user:pass@localhost:3306/myapp" \
> schema/schema.hcl
おわりに
本記事では、Atlasを使ったデータベースマイグレーションの方法を紹介しました。
Atlasの主な特徴:
- 宣言的アプローチと従来のバージョン管理型の両方をサポート
- HCL / SQL形式でスキーマを定義可能
- 自動差分検出とドライラン機能
- CI/CDとの連携が容易
よく使うコマンド:
| コマンド | 説明 |
|---|---|
atlas schema apply |
宣言的マイグレーション |
atlas migrate diff |
マイグレーションファイル生成 |
atlas migrate apply |
マイグレーション適用 |
atlas migrate status |
適用状況確認 |
atlas migrate down |
ロールバック |
atlas schema inspect |
既存DBからスキーマ取得 |
Atlasを導入することで、スキーマ管理がシンプルになり、チーム全体で安全にデータベースの変更を行えるようになります。
参考リンク:
Discussion