Prisma.jsを使ったアプリケーションでなるべくDockerイメージを小さくする
やりたいこと
Node.jsアプリケーションでPrismaを使用しています。
このアプリケーションをDockerイメージにしたいと考えたのですが、けっこう大変だったので共有します。
方針
- Node.jsを使う
- Prismaを使う
- pnpmを使う
- TypeScriptを使う
これらの方針のおかげでちょっと大変だったので、解説します。
とくに、大変だったのは、Prismaとpnpmの組み合わせでした。
Prismaとpnpmがうまく組み合わない
pnpmは、npmやyarnと同じくNode.js用のパッケージマネージャですが、パッケージがハードリンクされることにより、同じパッケージがいろんなところにインストールされない、という利点があります。
また、Prismaは prisma.schema
というスキーマファイルを独自のスキーマ言語で記述して、さまざまなデータベースに同じようにアクセスできるORMapperです。TypeScriptに対応しており、スキーマファイルに書いたモデル定義に合わせてTypeScriptの型とクライアントコードをnode_modules下に吐き出してくれます。
このような状況でDockerfileを書くことを考えます。
Prismaのクライアントコードはローカルで生成する前提のものであるため、Dockerfile内で生成する必要があります。
ざっくり以下のようなコードを書きました。
FROM node:20.11.0-slim as base
RUN npm i -g pnpm
WORKDIR /app
COPY package.json .
COPY pnpm-lock.yaml .
FROM base as builder
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm run gen # 1
RUN pnpm run build
FROM base as runner
RUN pnpm install --frozen-lockfile --prod && npm un -g pnpm
COPY /app/node_modules/.prisma ./node_modules/.prisma # 2
COPY /app/dist ./dist
CMD ["node", "dist/index.js"]
#1
と #2
で示したところがPrismaクライアントを生成(#1)し、それを実行イメージにコピー(#2)しているところです。
Prismaのドキュメントによると、クライアントコードは node_modules/.prisma/client
下に生成されると書いてあるので、これでいけると思ったのですが、これは間違いでした。
実際には、 node_modules/.pnpm/@prisma+client@5.9.0_prisma@5.9.0/node_modules/.prisma/client
の下に生成されてしまいました。原因は推測ですが、パッケージが丸ごとハードリンクされているため、リンク先に生成されてしまったものと思われます。
node_modulesに余計なものが入っていても気にしない
いや、気になる。気になるので、この方法はパス。
無理やりコピーしてみる
Dockerfileで上記パスからファイルをコピーして、実行イメージに置けばよいのでしょうが、Prismaのバージョンらしき文字列が含まれているため、Dockerfileが壊れやすい状態になることが想像できます。
そのため、この方法は避けることにしました。
出力先を変更する
出力先は schema.prisma
で指定できます。
以下の記述で、schema.prismaファイルがあるディレクトリから相対パスで ../.prisma/client
というところに出力されるようになります。
generator client {
provider = "prisma-client-js"
output = "../.prisma/client"
}
これはこれでうまく動いたのですが、JavaScriptが出力されるため、TypeScriptのコンパイル時にディレクトリが合わなくなり、残念ながら失敗しました。
出力先をデフォルトと同じパスで明示する
結論、これで解決しています。
Prismaのデフォルトの出力先を指定することで、pnpmの余計なディレクトリ移動が挟まらないようにしています。
generator client {
provider = "prisma-client-js"
output = "../node_modules/.prisma/client"
}
まとめ
Prismaの出力先を変更することで、パッケージマネージャの仕様の違いに対応することができました。
また、このおかげで、node_modulesの軽量化もできています。
ほかにもいくつかハマったところがあったのですが、それは別のお話ということで。
以上です。よろしくお願いします。
Discussion