Nest.js + Prisma の CI / CD 構築
Nest.js + Prisma 構成の CI / CD を構築するときのメモ
ポイント
- テストをどう実行するか?
- migration をどう実行するか?
- Prisma クライアントを正しく生成するにはどうするか?
前提
- CI / CD には AWS CodePipeline を利用する
- デプロイ先は AWS Elastic Beanstalk の Docker ランタイム
テストをどう実行するか?
シンプルに、データベースコンテナとビルドしたイメージを起動する docker-compose を構成して、CodeBuild 上で実行することにした。
テストでは実際のデータベースアクセスを伴うテストがある前提。
参照: https://zenn.dev/5t111111/scraps/f9002ee51a588a
migration をどう実行するか?
課題
prisma migrate
でマイグレーションを実行するには、ワークスペースが prisma init
されている、言い換えると ./prisma
ディレクトリが存在している必要がある。
しかし、一般的な Nest.js の本番構成では、ビルド後のディレクトリ (デフォルトは ./dist
) と dependencies
の詰まった node_modules
を設置すればアプリケーションとしては動作するため、その構成にすることが普通だと思う。
これは言い換えると、本番環境にデプロイした状態で prisma migrate
を実行することはできないということである。Prisma のドキュメントを見ても、マイグレーションは CI/CD で実行することが意図されている様子。
マイグレーションを CI / CD に組み込むこと自体は難しくないのだが、以下などの点で考慮すべき点は増える:
- CI / CD プロセスからデータベースへアクセスさせる必要がある
- CodeBuild だったので RDS アクセスは VPC 内で動かすことが可能だったが、GitHub Actions とかだと追加で ECS Runtask のような仕組みを利用しないといけない (そしてこのときまた
./prisma
ディレクトリ問題がある)
- CodeBuild だったので RDS アクセスは VPC 内で動かすことが可能だったが、GitHub Actions とかだと追加で ECS Runtask のような仕組みを利用しないといけない (そしてこのときまた
- アプリケーションのデプロイとマイグレーションの順序とタイムラグの管理
- アプリケーションのデプロイ失敗、マイグレーション失敗時の適切なロールバック
やったこと
CodePipeline のデプロイアクションの 後 に、マイグレーション用のアクションの CodeBuild を設置しマイグレーションを実行するようにした。
Docker マルチステージビルドで、コードベースを展開し npm install
を行うことでパッケージの準備をするステージを用意しておき、マイグレーション用のアクションではそのステージをターゲットとして npx prisma migrate deploy
を実行することにした。
先述の通り RDS に CodeBuild からアクセスさせる必要はあるが、それは VPC と subnet をそれ用に構成すれば OK。
残る課題としては、デプロイ成功 & マイグレーション失敗時にどうロールバックするかだが、これは Elastic Beanstalk ならバージョン戻しが楽なので、CI / CD での考慮は一旦置いといて、マイグレーション失敗を検知したら手動で戻すでカバーする。CodePipeline のステージの失敗をフックとして Lambda を実行とかはよくやるパターンなので、やろうと思えば自動化もできるとは思う。
この対応はアプリケーションの性質などに強く依存するので、構築対象のモノによって最適なやり方は変わると思う。ベストプラクティスと言えるものが何になるのかはわからない…
Prisma クライアントを正しく生成するにはどうするか?
これがどういうことかというと、Prisma のクライアントは、npm install
や明示的に prisma generate
を実行したときに node_modules
内に作られる。
したがって、Prisma クライアントが正しく生成されているかはかなり意識する必要がある。正しく生成できていない場合、アプリケーションの起動 (Nest.js ならば node dist/main
でエントリポイントを叩いたとき) で以下のようなランタイムエラーが発生する。
@prisma/client did not initialize yet. Please run "prisma generate" and try to import it again.
これが「普通の」Node アプリケーションの構成と異なるところなので、ぶっちゃけ一番きつくて、さらによくわかっていない点もある。
例えば、prisma
パッケージは通常 devDependencies
としてインストールすることが期待されているが、 npm install --only=production
としたときに、上記のエラーが 出ることもあったし出ないこともあった 。
ここは現時点で原因が特定できておらず問題として残っている。一旦は prisma
パッケージは devDependencies
としたまま、--only=production
をつけずに npm install
することで回避している。
上記のように prisma
ディレクトリがある状態で npm install
などで Prisma クライアントを生成しておけば、実際のアプリケーション実行時には prisma
ディレクトリは不要。
その他のハマりどころ
dist/main
がない!
これは、デフォルト構成の Nest.js で Prisma や Nest.js のドキュメントにしたがって進めたときの罠。
prisma
ディレクトリには seed ファイルなど TS ファイルが混ざってきてビルド対象になるので、デフォルト構成のままビルドすると、dist
内のディレクトリレイアウトが変わってしまう。要は、dist/main.js
ではなく dist/src/main.js
となる。
これを回避するには、tsconfig.build.json
をいじって、prisma
ディレクトリを除外対象として追加しておく必要がある。
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "dist", "test", "**/*spec.ts", "prisma"]
}
参考ページなど
メモ
-
prisma migrate deploy
では seed は実行されない