🗄️

Go + Atlas で始めるスキーママイグレーション

に公開

はじめに

データベースのスキーマ管理は、アプリケーション開発において重要な課題です。テーブルの追加やカラムの変更を安全に行い、チーム全員が同じスキーマで開発できる環境を整える必要があります。

本記事では、Atlasを使ったデータベースマイグレーションの方法を紹介します。

Atlasとは

Atlasは、Arigaが開発しているオープンソースのデータベーススキーマ管理ツールです。

対応データベース:

  • MySQL / MariaDB
  • PostgreSQL
  • SQLite
  • SQL Server
  • ClickHouse

従来のマイグレーションツールとの違い

特徴 従来のツール(goose, migrate等) Atlas
マイグレーション方式 バージョン管理型(Up/Down) 宣言的 + バージョン管理型
スキーマ定義 SQLファイル HCL / SQL / ORM連携
差分検出 手動で記述 自動で差分を検出
ドライラン ツールによる 標準サポート

なぜAtlasを選ぶのか

  1. 宣言的アプローチ: 理想のスキーマを定義すれば、現在の状態との差分を自動計算
  2. 安全性: 破壊的変更の検出、ドライラン機能
  3. 柔軟性: HCL、SQL、ORMスキーマからの生成に対応
  4. 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を作成します。

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ファイルでスキーマを定義することです。

schema/schema.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形式では、より構造化された記述が可能です。

schema/schema.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

生成されたマイグレーションファイル:

migrations/20240101120000_create_users.sql
-- 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) チーム開発、本番環境、履歴管理が必要な場合

本番環境ではバージョン管理型を推奨します。

実践的な使い方

開発環境でのワークフロー

  1. スキーマファイルを編集
  2. マイグレーションファイルを生成
  3. レビュー後、コミット
  4. ローカル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にまとめておくと便利です。

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でマイグレーションファイルを検証する例です。

.github/workflows/atlas.yml
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