👨🏼‍💻

NestJSとPrismaのイメージをCloudRunにデプロイしたらクラッシュしてコンテナが起動しなくなったので調べたこと

島袋恵2022/11/27に公開

はじめに

NestJSとPrismaのイメージをCloudRunにデプロイしたら、クラッシュして起動しなくなったので、原因について調べたことまとめです。

事象

Segmentation fault になって、NestJSが起動してくれない。(Cloud Runのログ)

環境

Dockerfile
#==================================================
# Build Layer
FROM --platform=linux/amd64 node:18 as build

WORKDIR /app

COPY package.json yarn.lock ./

COPY prisma ./prisma

RUN yarn install --non-interactive --frozen-lockfile

RUN yarn prisma generate

COPY . .

RUN yarn build

#==================================================
# Package install Layer
FROM --platform=linux/amd64 node:18 as node_modules

WORKDIR /app

COPY package.json yarn.lock ./

COPY prisma ./prisma

RUN yarn install --non-interactive --frozen-lockfile --prod

RUN yarn prisma generate

#==================================================
# Run Layer
FROM --platform=linux/amd64 node:18-slim as node

WORKDIR /app

ENV NODE_ENV=production

COPY --from=build /app/dist /app/dist
COPY --from=build /app/prisma /app/prisma
COPY --from=node_modules /app/package.json /app/yarn.lock ./
COPY --from=node_modules /app/node_modules /app/node_modules

CMD ["/usr/local/bin/yarn", "start:prod"]

まずは、Segmentation fault について調べてみます。

Segmentation fault とは

  • プログラムがOSによって設定された基本的な規則に違反したときに発生するエラーです。
    • このエラーが起きたとき、OSはプロセスにシグナルを送信し、プロセスはシャットダウンします。
  • 基本的には、低レベルの問題(ポインタやメモリ管理など)なので、JavaScriptを書くときは気にする必要はありませんが、Node.js で segfault が発生するパターンはいくつかあるようです。
    • Nodejsのネイティブのアドオン実装で、そのアドオン自体にバグがあるか、Nodeのバージョンと互換性がない場合
    • Node内部の状態の操作で、前提の状態が崩れ、Node の組み込みネイティブ コードが間違った動作をして、セグメンテーション違反が発生する場合
    • Node.js 自体にバグがある場合

https://httptoolkit.com/blog/how-to-debug-node-segfaults/

https://github.com/ddopson/node-segfault-handler

原因

ログには、Segmentation fault となっているものの、なぜ Segmentation faultになっているかの原因がわからず、社内Slackで困り果てていたところ、同僚に以下のIssueを教えてもらいました。

このIssueに原因とワークアラウンドが書かれてた(大感謝!!!)

https://github.com/prisma/prisma/issues/10649#issuecomment-1247961614

直接的な原因は、NodeJS自体にバンドルされたOpenSSLシステムで使われてるOpenSSLのバージョンが異なることに起因してるようです。

Node.js 17.0.0 以降、Node.jsにバンドルされてるOpenSSLのバージョンは3なのですが、システムが使っているOpenSSLのバージョンが1.1.xの場合、バージョンがずれて本事象が起こるようでした。

https://github.com/nodejs/node/commit/66da32c045035cf2710a48773dc6f55f00e20c40

ワークアラウンド

回避策は以下の3つがあるようで、どちらかのOpensslのバージョンに合わせる、もしくはクエリエンジンタイプにバイナリエンジンを指定することで回避できるようです。

  1. Nodeのバージョンを 16.x に下げ、Node.jsにバンドルされてるバージョンが 1.1.x になるようにして、Nodeとシステムで使われてるOpenSSLのバージョンを合わせる。
  2. システムで使われてるOpenSSLのバージョンを3に上げて、Nodeとシステムで使われてるOpenSSLのバージョンを合わせる。
  3. デフォルトの設定では、PrismaクライアントはクエリエンジンをNodeAPIとして利用しますが(engineType = library)、クエリエンジンを利用する別の方法として、実行可能なバイナリーをサイドカープロセスとして実行するという方法もあり(engineType = binary)、デフォルトの設定から、engineType = binary に切り替えると、システムで使っているOpenSSLとNodeで使ってるOpenSSLのバージョンが異なっていても、この問題を回避できます。

https://github.com/prisma/prisma/issues/10649#issuecomment-1249209025

クエリエンジン

クエリエンジンのタイプをバイナリーエンジン(engineType = binary)に切り替えると、Node.jsに含まれてるOpenSSLとシステムで使われてるOpenSSLのバージョンが異なっても Segmentation faultのエラーが回避できるのがなぜか気になったので、クエリエンジンについてのドキュメントをあらためて読んでみた。

クエリエンジンとは

  • prisma client からdbへのconnectionが呼び出された時に、dbとのコネクションを管理する
  • prisma client からのqueryをSQLに変換、DBに送信する
  • rust実装でOSに依存してる

以下、ドキュメント

https://www.prisma.io/docs/concepts/components/prisma-engines/query-engine#defining-the-query-engine-type-for-prisma-client

クエリエンジンのタイプ

クエリエンジンのタイプは以下の2パターンから選択することができます。
Prisma クライアントとクエリ エンジン間の通信オーバーヘッドが削減されるため、Node-API ライブラリ アプローチがおすすめのようです。(デフォルトの設定)

  • Prisma Client にロードされる Node-API ライブラリとしてクエリエンジンを利用する方法(デフォルトの設定)
  • 独自のプロセスで実行される実行可能バイナリとしてクエリエンジンを利用する方法
generator client {
  provider   = "prisma-client-js"
  engineType = "binary"
}

https://www.prisma.io/docs/concepts/components/prisma-engines/query-engine#defining-the-query-engine-type-for-prisma-client

https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#fields-1

気になっていたところとして、engineType = binary の場合は、Node-API ではなく独自のプロセスでクエリエンジンが実行されるので、Node.jsが内包してるOpenSSLが使われず、バージョンが異なっていても動くって感じっぽさそうと思ったのですが、詳細はあってるか自信ないのでもう少し調べてみます。

おわりに

エラーを調べてて初めて知ったことが多かったので勉強になった。Prismaのクエリエンジンのタイプや環境に依存があったりなどそのあたりあまり意識せず使っていたので、ドキュメントを改めて読んで色々発見があってよかった。

参考

  • Node.js OpenSSL Strategy

https://github.com/nodejs/TSC/blob/main/OpenSSL-Strategy.md

GitHubで編集を提案
株式会社モニクル

モニクルは、金融サービステック企業です。 金融の専門知識がなくても、正しい意思決定ができる社会へ。 そのために必要なのは、人によるサービスとテクノロジーの掛け合わせ。 既存のフィンテックとは異なる挑戦に取り組みます。

Discussion

ログインするとコメントできます