Rails エンジニアが Prisma に入門する:本番デプロイ(Vercel+Supabase)編
前回
の続きです。
はじめに
Rails アプリを本番環境にデプロイする際、DB のマイグレーションを同時に行いますよね。例えば、Heroku であれば Procfile にリリースフェーズで実行して欲しいコマンドを設定することができますのでこんな感じで rails db:migrate
を定義します。
release: bin/rails db:migrate
こうすることでデプロイ時にまだ実行されていないマイグレーションがあれば実行され、DB に変更が適用されます。ちなみに、このリリースフェーズのコマンドが失敗した場合(0以外のステータスで終了した場合)は、デプロイがキャンセルされます。実サービスを本番運用する際は地味にこの挙動が重要でして、仮にこの制御が上手くいかずに
- アプリケーションは最新のものがデプロイされて、DB のスキーマは古いまま
- DB のスキーマだけが変更され、アプリケーションが古いまま
なんて状態になってしまうと本番環境でエラーが発生して、最悪の場合サービスを一時停止することになるかもしれません。
ということで、このアプリケーションのデプロイと DB のマイグレーションを Prisma でも上手く制御できるか調べました。
Prisma の話というよりは環境の話になりそう
結局この話は本番環境は何か?(AWS? GCP? 何かの PaaS? BaaS?)や、CI/CD をどう行うか? DB の変更を伴うアプリケーションの変更をどういうポリシーで開発するか?という話なので、Prisma の話というよりは主に環境や DevOps の話になりそうです。
ちなみに、Prisma の公式ドキュメントにちょうどよい解説があるので、以下はほぼこちらの補足になります。
How to deploy a Prisma app to Vercel | Prisma Docs
検証環境
公式ドキュメントでは Heroku Postgres の無料プランが紹介されていますが、今回は話題の Supabase の PostgreSQL を使ってみたいと思います。Supabase にも無料プランがありますよー!
Supabase
サクッとアカウント登録して、プロジェクトを作ります。東京リージョン!
ダッシュボード、かっこいいじゃん...
DBの接続URLを探します。Settings > Database > Connection string にありました。URI に切り替えると接続URLの形式でコピーできます。パスワードはプロジェクト作成時のパスワードのようです。
これをのちほど、環境変数 DATABASE_URL
に設定します。
Prisma 公式サンプルを Vercel にデプロイ
では公式ドキュメントの手順のとおりに https://github.com/prisma/deployment-example-vercel をデプロイします。
リポジトリを作ります。後ほど参考にしていただけるよう Public にしました。
DATABASE_URL
を入力して Deploy します。
Deploy できました。
https://PROJECT_NAME.VERCEL_USERNAME.vercel.app にアクセスして、Seed data
してから Load posts
してみます。動いてますね。
Supabase の PostgreSQL にもテーブルが作成されています。
ちなみに、肝心の DB マイグレーションは package.json
の scripts
> vercel-build
に定義されています。
"scripts": {
"vercel-build": "prisma generate && prisma migrate deploy && next build"
}
-
prisma generate
でnode_modules/@prisma/client
を生成して -
prisma migrate deploy
で DB のマイグレーションを実行して -
next build
でビルドする
ことが定義されており、 &&
でコマンドの実行が連結されているので、前のコマンドが失敗したら以降のコマンドが実行されずエラーコードが返ります。その結果、Vercel のビルドが失敗 = デプロイがキャンセルされます。
つまり、例えば DB のマイグレーションに失敗したらデプロイがキャンセルされるので、アプリケーションだけが先にデプロイされて DB だけ旧スキーマの状態になることはなさそうです。
一方で、next build
が後にあるので、next build
に失敗した場合、DB だけ新スキーマになって、アプリケーションは旧コードのままという状態になってしまう可能性がありそうです🙅♂️
これは next build
を prisma migrate deploy
より先にして
"scripts": {
"vercel-build": "prisma generate && next build && prisma migrate deploy"
}
の順番の方が良いのではないでしょうか。next build
に失敗することってたまにありますよね??
この順番の入れ替えと追加の DB マイグレーションが上手く動作するか検証してみたいと思います。
公式サンプルをちょっと変更
自分のリポジトリにコピーされた公式サンプルを clone して以下の変更をしました。
-
next build
をprisma migrate deploy
より先に実行するよう変更 - Post モデルに published カラムを追加
変更内容はこちら。
で、この PR を作る前(厳密にはブランチをプッシュする前)に Vercel 側で Preview 環境用の DATABASE_URL
を本番 DB とは別の DB に変更しておかないと、PR をマージする前に本番 DB が先にマイグレーションされてしまいます。危険ですねぇ。
なぜかというと、プロジェクトを作成する際に設定した DATABASE_URL
が Production, Preview, Development
に適用されているからです。
実際にこの記事を書きながらそのミスをしてしまい、手動でカラムを削除して _prisma_migrations のレコードを削除して、マイグレーションを1つ戻しました😅
ということで、別途 Preview 環境用の DB を用意したいのですが、ちゃんとブランチ(PR)ごと = Preview 環境ごとに DB を用意(しかも自動で!)するのはなかなか大変で、「それもうほぼ仕事じゃん!」みたいな内容になってくるので、今回は1つだけ固定で Preview 環境用の DB を用意して検証だけ済ませることをゴールにします。
では、Supabase でもう1つ Preview 環境用のプロジェクト prisma-example-preview
を作ります。
接続URLをコピーして、Vercel で Preview 環境用の DATABASE_URL
を追加します。
先にあった方は Production 環境のみに変更します。
これで準備ができたのでブランチをプッシュして PR を作ります。
その PR がこちら。
問題なく Preview 環境がデプロイできたので Visit Preview
からアクセスしてみます。
Seed data
して Load posts
してみます。追加した published
フィールドが増えています!
Preview 環境用の DB にも published
カラムが増えていました。ちなみに、まだ PR をマージしていないので本番環境用の DB には published
カラムが増えていません。
長くなりましたがやっと検証したかった最後の手順です。PR をマージして本番環境にアプリケーションがデプロイされるのと同時に DB がマイグレーションされることを確認します。これだけ丁寧に確認してきたので流石に問題ないとは思いますが😅いってみましょう!
PR をマージします。
Vercel で本番環境(main ブランチ)のビルドが始まります。
せっかくなのでログを見てみます。
Applying migration `20220817021818_add_published`
The following migration have been applied:
migrations/
└─ 20220817021818_add_published/
└─ migration.sql
All migrations have been successfully applied.
追加したマイグレーションが適用(実行)されてますね!
本番DBに published
カラムが追加されました!
そして本番環境でも published
フィールドが追加されたことを確認できました!
おわりに
今回はもうバリバリ運用している人にとっては「何をいまさら〜」という初心者レベルの内容になってしまったかもしれませんが、これからはじめて Prisma Migrate を使う自分にとっては一番の心配事がこの本番環境への問題のないデプロイだったのでそれが確認できて一安心しました。
検証が終わってしまえば、まぁ普通にいけますね!という感想です😄
あと、地味に順番を入れ替えた
"scripts": {
"vercel-build": "prisma generate && next build && prisma migrate deploy"
}
でも全く問題がないことも確認できました。個人的には DB マイグレーションが適用された後に next build
に失敗して「DBは新スキーマ」「アプリは旧コード」な状態になるのは避けたいので、絶対にこの順番の方が良いと思います。ということで、自分が Prisma を本番運用するならこれでいこうと思います!
それでは!
Discussion