RailsアプリをCloud Runで動かすときにいくつか悩むことがありますが、そのひとつにDBマイグレーションをどうやって実行するかがあると思います。
この問題の解決策としてCloud Runジョブを使ったらうまくはまったのでご紹介します。
おことわり
Cloud Run自体の説明やCloud Runへのデプロイ方法については省略しています。別途公式ドキュメントや解説記事を参照してください。
また、Cloud Runジョブは現在プレビューとして提供されているので、事前に利用規約などをご確認ください。
Cloud RunのDBマイグレーションどうする問題とは
RailsアプリのDBマイグレーションはrails db:migrate
コマンドの実行によって行います。
Cloud RunにデプロイしたRailsアプリでrails db:migrate
を実行したいと思っても、Cloud Runサービスはリクエストを受けて処理を実行するものなので、リクエストを伴わずに特定の処理を実行させることはできません。
コンテナ起動処理に混ぜ込んだりCloud Runの外で実行したりいくつか方法はありますが、今回はCloud Runジョブを使う方法を順を追って設定していきます。
事前準備: Cloud RunにRailsアプリをデプロイ
サンプルとして単純なRailsアプリを用意しました。
Cloud SQLのMySQLインスタンスに接続するので、Cloud SQLのインスタンスとデータベースを作成しておきます。
まずは普通にRailsアプリをデプロイしてブラウザでアクセスできることを確認します。
Cloud Runジョブでrailsコマンドを実行してみる
Cloud Runジョブの動作確認として、rails about
コマンドをCloud Runジョブで実行してみます。
Cloud Runジョブは現在プレビューでbetaコンポーネントのインストールが必要なので、まだインストールしていなければインストールしておきます。
gcloud components install beta
ジョブを作成します。
gcloud beta run jobs create rails-about \
--image [イメージURL] \
--command bin/rails \
--args about \
--parallelism 1 \
--max-retries 0 \
--execute-now \
--wait
オプションの意味は次のとおりです。
-
--command
,--args
- 実行するコマンドとコマンドの引数
-
--parallelism 1
- 並列実行してほしくないので1を指定
-
--max-retries 0
- コマンドがエラーになったらリトライせずにそのまま終了
-
--execute-now
- ジョブ作成後すぐ実行
-
--wait
- ジョブの完了を待つ
ジョブが完了したら、GCPコンソールでジョブの実行結果を確認します。
rails about
のコマンド結果が確認できました。
Cloud Runジョブでrails db:migrate
を実行する
Cloud Runジョブでrails
コマンドを実行できることが確認できたので、次はrails db:migrate
を実行してみます。
ジョブを実行する前に、DBマイグレーションされていないのを確認してみます。
/posts
にアクセスするとエラーになります。ログを見るとposts
テーブルが存在していないことがわかります。
ではジョブを作成します。
gcloud beta run jobs create rails-db-migrate \
--image [イメージURL] \
--command bin/rails \
--args db:migrate \
--set-cloudsql-instances [インスタンス接続名] \
--set-env-vars DATABASE_USERNAME [DBユーザー名] \
--set-env-vars DATABASE_PASSWORD [DBパスワード] \
--set-env-vars DATABASE_SOCKET /cloudsql/[インスタンス接続名] \
--parallelism 1 \
--max-retries 0 \
--execute-now \
--wait
--args
にdb:migrate
を指定します。
--set-cloudsql-instances
や--set-env-vars
はCloud Runサービスと同じ情報を指定すればOKです。
先ほどと同じようにGCPコンソールでジョブの実行結果を確認します。
再度/posts
にアクセスすると今度は正常にページが表示されました。
デプロイフローに組み込む
最後に、CI/CDのデプロイフローに組み込むことを考えます。
既存のデプロイフローにコンテナイメージのビルドとCloud Runサービスのデプロイがあると思うので、その間にジョブの実行を挟みます。
gcloud beta run jobs update rails-db-migrate \
--service-account [サービスアカウントID] \
--image [イメージURL] \
--command bin/rails \
--args db:migrate \
--set-cloudsql-instances [インスタンス接続名] \
--set-env-vars DATABASE_USERNAME=[DBユーザー名] \
--set-env-vars DATABASE_PASSWORD=[DBパスワード] \
--set-env-vars DATABASE_SOCKET=/cloudsql/[インスタンス接続名] \
--parallelism 1 \
--max-retries 0 \
--execute-now \
--wait
ビルドした最新のコンテナイメージを使うようにgcloud beta run jobs update
でジョブ定義を更新します。
--service-account
でデプロイフローで使用してるサービスアカウントの権限でジョブ実行するように指定します。
まとめ
Cloud Runに載せたRailsアプリのDBマイグレーションをCloud Runジョブで実行してみました。
業務で運用しているRailsアプリでこの方法を導入していまのところ安定してデプロイできています。
いままでCloud Runではrails
コマンドやRakeタスクを実行するのが面倒でしたが、Cloud Runジョブの登場で弱点がひとつ消えてまた便利になりました。