はじめに
この問題について考えたきっかけは、Golang を用いたマイクロサービスの開発中に、コンテナが再起動するたびにマイグレーションが実行され、意図しないデータの変更やアプリケーションの起動遅延が発生したことでした。
特に、破壊的変更を含むマイグレーションが誤って適用されるリスクがあることが問題となりました。
現在の Dockerfile の問題点
以下の Dockerfile では、コンテナ起動時に必ず migrate_app
が実行される設定になっています。
CMD ["/bin/sh", "-c", "/app/migrate_app && /app/server"]
この設定では、アプリケーションが起動するたびにマイグレーションが実行されるため、以下の問題が発生する可能性があります。
問題点
-
すでに適用済みのマイグレーションが再適用される
migrate_app
が idempotent(冪等性を持つ、つまり何度実行しても影響がない)でない場合、データベースの整合性が崩れる可能性があります。- 例えば、
CREATE TABLE
を含むマイグレーションを毎回実行すると、テーブルの重複エラーが発生します。
- 例えば、
-
アプリケーションの起動が遅くなる
- 毎回マイグレーションを適用することで、不要な処理が発生し、アプリケーションの起動時間が長くなります。
-
マイグレーション中にエラーが発生し、アプリケーションが起動しない
-
migrate_app
が失敗するとserver
が起動しないため、コンテナが正しく動作しなくなるリスクがあります。
-
-
破壊的変更のリスク
- スキーマ変更がある場合、既存データが失われる可能性があります。
-
DROP COLUMN
やALTER TABLE
でカラムの変更があると、古いバージョンのアプリが動作しなくなることがあります。 - データ移行が適切に行われないと、アプリケーションの互換性が損なわれる恐れがあります。
方法: マイグレーションを手動で適用する
アプリケーションのデプロイ前に手動でマイグレーションを適用し、コンテナ起動時には server
だけを実行する方法です。
変更後の Dockerfile
CMD ["/app/server"]
この場合、デプロイのフローにマイグレーション処理を組み込み、データベースの更新が必要な場合のみ実行します。
まとめ
-
migrate_app
を idempotent にするのが最も簡単な解決策。 - マイグレーションを手動適用する場合、コンテナ起動時には
server
だけを実行する。 - エントリポイントスクリプトを使うことで、マイグレーションの実行可否を制御できる。
- 破壊的変更がある場合、適用前にバックアップを取り、段階的なリリースを検討する。
アプリケーションの要件に応じて、適切な方法を選択することが大事だと思いました!