事故を防ぐDB変更フロー 〜opsqlが変える日々の運用スタイル〜

こんにちは。最近は夏も終わり、昼休みは大濠公園を大爆走しています、P山です。

今回は、弊社で運用しているデータベース変更管理ツール「opsql」について、実際の運用フローと、それによってどのように業務が改善されたかをお話しします。

データベース変更の課題

モビリティサービスを支えるシステムでは、データベースへの変更作業が日常的に発生します。ユーザーの状態変更、不正なデータの修正、緊急時の設定変更など、様々な場面でSQLを実行する必要があります。

従来は、社内リポジトリMarkdown形式の手順書を作成し、レビュー後に作業者が手動で実行していました。例えば、以下のような手順書です:

## 概要
決済失敗レコードを削除して、決済成功状態にする

## 対象レコード確認
select * from orders where order_id = 123456;

## 実行クエリ
BEGIN;

DELETE FROM orders
WHERE order_id = 123456 AND id = 789012;

DELETE FROM payment_transactions
WHERE id = 345678 AND order_id = 789012;

-- 問題なければ
COMMIT;
-- 問題あれば
ROLLBACK;

この従来の手順書ベースの運用では、以下のような課題がありました:

  • 手順書の品質のばらつき: 作成者によって記述の詳細度や確認手順が異なる
  • 実行前の確認不足: 手順書に書かれた確認クエリを実行し忘れるリスク
  • 実行結果の検証漏れ: 変更後の状態確認が手動のため、見落としが発生する可能性
  • 作業ログの分散: 手順書、実行ログ、Slackでの報告が別々に管理される
  • 実行ミスのリスク: 手動でのSQL実行時に、コピー&ペーストミスや値の入力間違いが発生する可能性

これらの課題を解決するために、P山が開発したopsqlを導入し、YAMLベースでのデータベース変更管理を実現しました。

opsqlによる構造化されたDB変更管理

YAMLによる変更定義

opsqlでは、データベースへの変更をYAMLファイルで構造化して定義します。以下は具体例です:

params:
  target_order_id: "123456"
  original_status: "pending"
  new_status: "completed"

operations:
  # 変更前の状態確認
  - sql: |
      SELECT COUNT(*) as count
      FROM orders
      WHERE order_id = '{{ .params.target_order_id }}'
        AND status = '{{ .params.original_status }}'
    expected:
      - count: 1

  # 実際の更新処理
  - sql: |
      UPDATE orders
      SET status = '{{ .params.new_status }}'
      WHERE order_id = '{{ .params.target_order_id }}'
        AND status = '{{ .params.original_status }}'
    expected_changes:
      update: 1

  # 変更後の確認
  - sql: |
      SELECT COUNT(*) as count
      FROM orders
      WHERE order_id = '{{ .params.target_order_id }}'
        AND status = '{{ .params.new_status }}'
    expected:
      - count: 1

この形式により、以下のメリットが得られます:

  • パラメータ化: 変更対象を変数として定義し、再利用可能
  • 事前確認: 変更前の状態を必ずチェック
  • 期待値の明示: 実行結果の期待値を事前に定義
  • 自動ロールバック: 期待値と異なる結果の場合は自動的にロールバック

GitHub PRによる変更の可視化

作成したYAMLファイルはGitHub PRとして提出します。PRを作成すると、GitHub Actionsが自動的にdry-runを実行し、結果をPRコメントとSlackに通知します。

上記のように実行予定のクエリの結果がひと目でわかるのでレビュワーが確認しやすく、またSlack通知によりチーム全体に変更内容が共有されます。

GitHub Actionsによる自動実行

ワークフロー設計

実際の実行は、GitHub Actionsの手動ワークフローで行います。opsqlコマンドの基本的な使い方は以下の通りです:

# Dry-run(テスト実行)
opsql run --config config.yaml --dry-run

# 本実行
opsql run --config config.yaml

GitHub Actionsでの実行例:

name: OPSQL Deploy
on:
  workflow_dispatch:
    inputs:
      config_path:
        description: 'Configuration file path (e.g., opsql/prod/20250101-fix-payment.yaml)'
        required: true
        type: string

jobs:
  dry-run:
    runs-on: ubuntu-latest
    steps:
      - name: Install opsql
        run: go install github.com/pyama86/opsql@latest

      - name: Dry-run OPSQL
        env:
          DATABASE_DSN: ${{ secrets.DATABASE_DSN }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
        run: |
          # dry-run実行
          opsql run --config ${{ inputs.config_path }} --dry-run \
            --github-repo ${{ github.repository }} \
            --github-pr ${{ github.event.number }}
  execute:
    runs-on: ubuntu-latest
    environment: 'production'
    steps:
      - name: Install opsql
        run: go install github.com/pyama86/opsql@latest

      - name: Execute OPSQL
        env:
          DATABASE_DSN: ${{ secrets.DATABASE_DSN }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
        run: |
          # 本実行
          opsql run --config ${{ inputs.config_path }}

本番環境の保護

本番環境(opsql/prod/配下)の変更では、GitHub ActionsのEnvironment機能を使用して手動承認を必須としています。これにより、以下のフローが実現されます:

  1. PRでのレビュー: 変更内容をチーム全体で確認
  2. Dry-runの自動実行: 安全性の事前確認
  3. 手動ワークフロー実行: 実行タイミングをコントロール
  4. 本番承認: 本番環境では追加の承認ステップ

運用による効果

人的ミスの削減

従来の手動作業では、SQLの書き間違いや実行環境の間違いなどが発生していましたが、構造化された定義とdry-runにより、これらのリスクが大幅に軽減されました。

P山自身、普段から「九州が育んだtypoの神」と自称するほどタイプミスが多く、以前は手動でのSQL実行時にうっかりミスを犯してしまうことがありました。opsqlの導入により、YAMLファイルでの事前定義とdry-runによる確認で、そうしたヒューマンエラーは大幅に減少しました。

変更履歴の一元管理

すべての変更がGitリポジトリで管理されるため、以下が実現されました:

  • 完全な履歴: いつ、誰が、何を変更したかが明確
  • 差分の確認: 変更内容の詳細な比較が可能
  • 変更パターンの蓄積: 過去の変更例を参考にした作業の標準化

チーム内の知識共有

YAMLファイルとPRを通じて、データベースの変更パターンがチーム全体で共有されるようになりました。新しいメンバーも、過去の変更例を参考にして、適切な変更定義を作成できます。

実行の自動化と安全性

GitHub Actionsによる自動実行により、以下が実現されました:

  • 一貫した実行環境: 環境差異による問題の排除
  • ログの確実な記録: Slackへの通知とアクションログでの完全な記録
  • 承認フロー: 本番環境での適切な承認プロセス

今後の展望

現在のopsql運用は順調に機能しており、データベース変更作業の安全性と効率性を大幅に向上させることができました。今後もこのツールがGO社内だけでなく、同様の課題を抱える他の会社でも活用されることを期待しています。

最後に

opsqlの導入により、データベースの変更作業が大幅に改善されました。属人化の解消、安全性の向上、作業の可視化など、多くのメリットを実感しています。

何より、データベース変更作業での緊張から解放されたのは、P山にとって最大の収穫でした。

データベースの変更管理に課題を感じている方は、ぜひopsqlの導入を検討してみてください。きっと、より安全で効率的な運用が実現できるはずです。

最後に、opsqlの開発と運用改善に協力してくださった、チームの皆さんに感謝して、締めとします。