🕶️

マイグレーションツールをsqldefに移行した話

2022/01/26に公開

どうも!株式会社LEAN BODYでエンジニアをやっておりますko30005です!

今回はマイグレーションツールを移行したお話です。

弊社はAPIをGo+Echoという構成で開発しており、ORMはGORMを使っています。
なのでマイグレーションもGormの機能で運用していたのですが、先日sqldefに移行しました。

sqldefとは?

Ridgepoleのような感じで一つのスキーマファイルでマイグレーションを実行することができるGo製のマイグレーションツールです。
生SQLでスキーマ管理することができるのでDSLを覚えたりする必要がないので学習コストも低いツールかと思います。

sqldefの詳しい使い方等はREADMEや他の導入記事に既に記載されているのでそちらに譲ることとします。

何故sqldefに変えたのか

Gormのマイグレーションではスキーマの変更を行うたびにマイグレーションファイルを作る必要があり面倒だと感じていたのと、別ツールを使わないとロールバックもできないためローカルの開発が結構手間だと感じていました。

私は以前別の開発現場でRidgepoleを使っていて便利だなと思っていましたが弊社はサーバーはGoがメインなのでバイナリで実行できて生SQLで管理できるsqldefが候補に上がっていました。

導入前にsqldefとGROMマイグレーションのメリットデメリットを調査したところ以下のような感じでした。

GROMマイグレーション

  • メリット
    • 追加したマイグレーションファイルに明示的に書いてあるものしか反映しないため不慮の事故が起きにくい
  • デメリット
    • スキーマの変更を行うたびにマイグレーションファイルを作る必要があり
    • ロールバックがないので修正する時に不便
      • ただし別ライブラリを入れればロールバック可能にはなる

sqldef

  • メリット
    • スキーマ変更の度にマイグレーションファイルを作る必要が無い
    • 生SQL管理なので、ライブラリの仕様の影響を受けず、外部キーの付与忘れなどが発生しにくい
    • 生SQL管理なのでメソッドやDSLを覚える必要がなく学習コストが低い
    • 1ファイルで管理出来るので現在のDB情報がわかりやすい
  • デメリット
    • スキーマファイルからテーブルやカラムの情報を消した状態で実行してしまうとテーブルやカラムが消えてしまう

Gorm専用の別ツールを導入するという手もありましたが、メリデメ考えた結果結局sqldefに移行することにしました。

また、色々調査した際に他の企業さんでも本番で使っているという記事もそこそこあった為大きな問題はないだろうと思い移行しました。

sqldefへの移行

ツールをsqldefへ移行すること自体はすぐに終わりました。

  • sqldefが動くマイグレーションコンテナ用のDokerfileを用意
    • 弊社はECS Fargateを使用しているため。ECS run taskでマイグレーションタスクを実行するようにしました。
    • マイグレーションのログはAWS CLIでCloudwatchから取得するようにしました。
  • 現状のCREATE TABLE構文を抽出し、sqldefに読ませるスキーマファイルにコピー
    • sqldefは生SQLでスキーマ管理するので移行がかなり簡単でした。。。
  • 既存のマイグレーションファイルを削除

これだけで移行できました。

フローの見直し

一つのスキーマファイルだけで管理出来るのは便利なのですが、

sqldefの場合スキーマファイルからテーブルやカラムの情報を消した状態で実行してしまうとテーブルやカラムが消えてしまうというデメリットがあります。

事故防止のためデプロイ前にdry-runの結果をチェックできる仕組みが必要だと考え開発フローの見直しをしました。

※ 2022/01/27追記

sqldef作者の方からコメントいただきました。
--skip-dropオプションを使うとより安全にマイグレーションできそうです。
https://twitter.com/k0kubun/status/1486588393026822145?s=20
追記ここまで

既存の開発フロー

既存の開発フローは以下のような感じでした。

  1. Pull Request作成
  2. レビューOKならマージ
  3. マージをトリガーにCircleCIのデプロイワークフロー開始
  4. 自動テスト
  5. マイグレーション
  6. APIデプロイ処理

最初は既存のデプロイフローにdry-runのタスクを入れるだけにしていたんですがCircleCIのログを見に行かないとdry-runの結果が見えないのが不便だったのと、Pull Requestのコメントに送信することでレビュワーの確認し忘れを無くせると考えたので、スキーマファイルを変更したPull Requestのコメントにログを自動的に送信する機能も実装することになりました。

Pull Requestへのコメント自動送信機能もCircleCIからやろうとしたのですがPull Requestの情報取得したりコメント送信したりするのはGithub Actionsの方が相性良さそうでした

弊社ではリリース用Pull Request自動作成ツールもGithub Actionsで作っており

  • 自動テストやAPIのデプロイ等のCIはCircleCI
  • Github周りの自動化はGithub Actions

みたいな感じで適材適所な運用でやっていますので今回もPull Requestコメント送信機能はGithub Actionsで実装しました。

スキーマファイルを変更しない場合の処理

弊社の場合スキーマファイルはAPIのリポジトリの中に入っているため、そのままだとDB変更しないのにマイグレーションタスクが毎回発生するみたいなことが起きてしまうので、スキーマファイルに差分がある場合のみCircleCIとGithub Actionのマイグレーションタスクを動くようにしてCIの稼働時間を節約するようにしました。(上記Pull Requestコメント送信機能もファイルの差分を見るようにしています)

ファイルに差分があると以下のような感じでbotがコメント書いてくれるようになりました!

修正後のフロー

最終的にPull Requestオープンしてからのリリースフローは以下のようにしました。

  1. Pull Request作成
  2. マイグレーションファイルに差分があればGithub Actionsでdry-runの結果をPull Requestのコメントに送信
  3. dry-runの結果も確認してレビューOKならマージ
  4. マージをトリガーにCircleCIのデプロイワークフロー開始
  5. 自動テスト
  6. マイグレーションファイルに差分があればsqldefでdry-run
  7. dry-runのログ確認して問題なければApproveボタン押してCIのワークフロー続行
  8. マイグレーションファイルに差分があればsqldefでマイグレーション
  9. APIデプロイ

これでデメリットのリスクを減らしつつ快適にマイグレーション周りを修正できるようになりました。

まとめ

今回はGORMのマイグレーションからsqldefに移行した話をしました。

会社のメンバーからもsqldefかなり便利で移行してから開発体験向上したとの声も上がったので今回移行してよかったです!

sqldef使いたいけど迷ってる。。。。みたいな方々の参考になればと思います。

Discussion