AtlasとGitHub Actionsによるデータベース・スキーマ変更の自動化:GitOpsガイド

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

午前2時15分のデータベースの惨劇

それは開発者1年目の、ある火曜日の夜のことでした。私たちは深夜0時にv2.4リリースを控えていました。コードは完璧で、Dockerイメージも準備万端、CI/CDパイプラインも「緑」に輝いていました。私は「デプロイ」ボタンをクリックし、祝杯代わりのエスプレッソを飲みに行きました。

10分後、監視アラートが爆発しました。1万5千人のアクティブユーザーに対し、サイトは500エラーを吐き出し続けていたのです。ログは無機質にこう告げていました:column "user_tier" does not exist(列 “user_tier” が存在しません)。ローカルのDjangoモデルにはフィールドを追加していましたが、本番のPostgreSQLインスタンスへの手動SQLマイグレーションを忘れていたのです。それから45分間、CTOがダウンタイムの時計を凝視する中、私は冷や汗をかきながらターミナルでALTER TABLEコマンドを叩き続けました。わずか1時間で週間収益の4%を失いました。それは残酷で、かつ回避可能な教訓でした。

データベース変更の自動化は、譲れないスキルです。アプリケーションコードと同じように、スキーマを厳格にバージョン管理すれば、本番環境で発生する特定のカテゴリーの障害を完全に根絶できます。

なぜデータベースのデプロイは失敗するのか

通常、アプリケーションコードはステートレス(状態を持たない)です。デプロイがうまくいかなければ、以前のコンテナイメージにロールバックするだけです。しかし、データベースは違います。データベースには状態(ステート)があります。3年分の顧客履歴を消し去ってしまったDROP COLUMNコマンドを、簡単に「元に戻す」ことはできません。

なぜこれほど頻繁に起こるのでしょうか?主な原因は3つの摩擦点に集約されます:

  • 人間の記憶への依存: 疲弊したエンジニアに、午前3時に手動でスクリプトを実行することを期待するのは、惨劇を招くようなものです。手順を飛ばしたり、タイポ(打ち間違い)をしたりします。
  • スキーマドリフト: 誰かが先月、本番コンソールで直接「クイック修正」を行ったせいで、ステージング環境と本番環境が全く別物になっています。
  • バージョンの乖離: コードはv3.0なのにデータベースがv2.8のままだと、アプリはクラッシュします。チームが成長するにつれ、これらを手動で同期させるのは不可能になります。

ツールセットの比較

私はAtlasに辿り着くまでに、いくつかの手法を試しました。他の方法がどこで失敗するかを理解することで、スケールするにつれて「宣言的アプローチ」がなぜ有効なのかが明確になります。

1. 手動SQLフォルダ方式

これは古典的なジュニアレベルのアプローチです。/migrationsフォルダに001_init.sql002_add_email.sqlを詰め込みます。進捗はスプレッドシートや記憶で管理します。これは1週間は機能しますが、誰かが5番目のスクリプトを飛ばした瞬間に、スタック全体が崩壊します。

2. 言語固有のORM (Prisma, TypeORM, Django)

これらはコードからSQLを生成してくれるため便利です。しかし、複雑なマイグレーションには弱く、特定のエコシステムに縛られてしまいます。Go、Python、Node.jsがすべて一つのデータベースを共有しているマイクロサービス環境では、どのORMを「信頼できる情報源」にすべきでしょうか?

3. Javaベースの巨頭 (Flyway, Liquibase)

FlywayとLiquibaseは業界のベテランです。強力ですが、重厚に感じられます。Javaランタイムが必要で、冗長なXML設定の管理を強いられることもあります。これらは「命令的(インペラティブ)」であり、データベースをどのように変更するかをステップバイステップで細かく指示する必要があります。

4. Atlasアプローチ (宣言的GitOps)

Atlasは、Kubernetesがインフラを扱うのと同じようにデータベースを扱います。「データベースはどうあるべきか」という「理想の状態(Desired State)」を定義し、Atlasがそこに至るための最も効率的な経路を計算します。クリーンで言語に依存せず、GitOpsのために構築されています。

GitHub ActionsでAtlasを実装する

プルリクエストをマージした際に、スキーマ変更のLint(静的解析)、テスト、適用を自動的に行うパイプラインを構築しましょう。これにより、Gitリポジトリが絶対的な「信頼できる情報源」となります。

ステップ1:Atlas CLIのインストール

バイナリを取得します。LinuxまたはmacOSの場合は、インストールスクリプトを使用します:

curl -sSf https://atlasgo.sh | sh

ステップ2:スキーマの定義

AtlasはHCL (HashiCorp Configuration Language) を使用します。生のSQLよりもはるかに読みやすいです。schema.hclを作成します:

table "users" {
  schema = schema.public
  column "id" {
    null = false
    type = int
  }
  column "username" {
    null = false
    type = varchar(255)
  }
  column "email" {
    null = false
    type = varchar(255)
  }
  primary_key {
    columns = [column.id]
  }
}

ステップ3:バージョン管理されたマイグレーションの生成

変更を闇雲に適用してはいけません。「バージョン管理されたマイグレーション(Versioned Migration)」ワークフローを使用して、監査可能な履歴を作成します。一時的なDockerコンテナをサンドボックスとして使用し、以下のファイルを生成します:

atlas migrate diff add_users_table \
  --dir "file://migrations" \
  --to "file://schema.hcl" \
  --dev-url "docker://postgres/15/dev"

このコマンドを実行すると、タイムスタンプ付きのSQLを含むmigrationsフォルダが作成されます。これで、バージョン管理された監査証跡が得られました。

ステップ4:CIチェックの自動化

セーフティネットが必要です。1行のデータに触れる前に、不正なSQLを検知しましょう。.github/workflows/atlas-ci.yamlを作成します:

name: Atlas CI
on:
  pull_request:
    paths:
      - 'migrations/**'
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - uses: ariga/atlas-action/migrate/lint@v1
        with:
          dir: 'file://migrations'
          dev-url: 'docker://postgres/15/dev'

このワークフローは、主要なテーブルの削除といった破壊的な変更がメインブランチに届く前に、フラグを立てて警告してくれます。

ステップ5:自動デプロイ (CD)

プルリクエストがマージされたら、変更を自動的に反映させます。.github/workflows/atlas-deploy.yamlを作成します:

name: Atlas Deploy
on:
  push:
    branches:
      - main
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: ariga/atlas-action/migrate/apply@v1
        with:
          dir: 'file://migrations'
          url: ${{ secrets.DATABASE_URL }}

まとめ

セットアップには約45分かかりますが、これから1年間にわたる深夜のデバッグ作業を数週間分は節約できるでしょう。データベースにGitOpsを取り入れれば、「デプロイの日」への恐怖は消え去ります。データベースは恐ろしいブラックボックスではなくなり、単なるコードの一部となります。

守るべき3つのルール:

  1. 開発用データベースを信頼する: 差分を検証するために、CIでは常にdocker://を使用してください。それがSQLの妥当性を保証する唯一の方法です。
  2. シークレットを保護する: DATABASE_URLをハードコードしてはいけません。すべてにGitHub Secretsを使用してください。
  3. ドライランでテストする: マイグレーションに不安がある場合は、atlas migrate apply --dry-runを実行して、データに触れることなく何が起こるかを正確に確認してください。

今すぐこれらの習慣を身につけましょう。将来のあなた、および運用チームは、デプロイの夜に静寂が保たれていることに感謝するはずです。

Share: