LoginSignup
2
3

Next.js + Prisma の Docker を OpenSSL 3.0 対応する

Last updated at Posted at 2023-07-14

はじめに

開発マシンを Intel Mac から M2 Mac に切り替えたら、色々問題が起きた。これまで、サンプルを元に適当にやっていたが、OpenSSL 1.1.1 のサポートが9月11日に終了するので、OpenSSL 3.0 対応を含めて解消しつつ、きちんと Docker と向き合うことにした。

最終的にやりたいことは、ローカルで build した image を AWS ECR に push し、それを AWS ECS に deploy したい。

結論だけ > TAKT-R-D / nextjs-prisma3-busterslim-openssl3

対応方針

対応方針は、Prisma のバージョンによって変わってくる。

本当は、Prisma のバージョンを上げるのが良いと思うが、対応・検証に時間がかかるし、色々考えているうちに、一昨日 5.0.0 がリリースされて絶望したので、いったん Prisma の Upgrade は先延ばしにする一次対応とする。

まず、SSL 3.0.x に対応した Prisma の binaryTarges を確認する。

node のバージョンも色々だが、ここでは説明簡略のため 18.16.1 とする。

Prisma のバージョン >= 4.10.0 の場合

base となる node イメージは、node:18.16.1-alipine3.17 とし、schema.prisma の binaryTargets は、linux-musl-openssl-3.0.x, linux-musl-arm64-openssl-3.0.x を追加する。native とそれ以外はいらない。

このケースは、たぶん特筆することはないと思われるので、今回はやらない。時間があったらやってみる。

Prisma のバージョン < 4.10.0 の場合

この場合、alpine が使えない。イメージはどれでも良いが、なるべく安定していて、軽量にしたいので、Debian の現LTS(2024年6月30日まで)の Buster (Debian 10) の slim な node:18.16.1-buster-slim を使うことにする。

binaryTargets は、M2 Mac 用に linux-arm64-openssl-3.0.x (Linux (most distributions except Alpine), ARM64), ECS 用に debian-openssl-3.0.x (Linux (Debian), x86_64) を入れる。

ECS に Deploy するには、docker image を build する際に platform=linux/amd64 というオプションをつける必要があるが、このオプションをつけると、build する環境(Intel Mac or M2 Mac)に関わらず、実際に使われるイメージは、x86_64 になるので、debian-openssl-3.0.x が必要になる。

また、buster-slim には OpenSSL がインストールされておらず、apt でインストールできる OpenSSL のバージョンは、現時点では、1.1.1n-0+deb10u3 であり(こちら)、3.0 は apt ではインストールできないため、ソースコードを取ってきて、make install する必要がある。そのうち apt-get install できるようになると思うので、以下を読む前に、できるかできないかは確認して欲しい。なぜなら悶絶するくらい build に時間がかかるので。

Debian 10 buster-slim に OpenSSL3.0 をインストールする

まず、インストールするバージョンの検討であるが、OpenSSLのページを見ると、3.0 系は 3.0.9、3.1 系は 3.1.1 が最新となっている。ちょっと悩ましいところだが、今回は FIPS Validated な最新 3.0.8 を入れることにする。どれでも手順は一緒だと思うので、入れたいものを選んで貰えればよい。

とりあえず、OpenSSL3.0 をインストールする Dockerfile を作る。

FROM node:18.16.1-buster-slim AS base

FROM base AS deps
# 何も入ってないので、必要なものを入れる
RUN apt-get update && apt-get install wget build-essential zlib1g-dev make -y

# source を取ってくる。予めローカルにダウンロードしておいて、COPY でも良い
WORKDIR /usr/local/src
RUN wget https://www.openssl.org/source/openssl-3.0.8.tar.gz
RUN tar -zxf /usr/local/src/openssl-3.0.8.tar.gz

WORKDIR /usr/local/src/openssl-3.0.8
RUN ./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared zlib
RUN make && make install

# build
FROM base AS builder
COPY --from=deps /usr/local/ssl /usr/local/ssl
RUN echo "/usr/local/ssl/lib\n/usr/local/ssl/lib64" > /etc/ld.so.conf.d/openssl-3.0.8.conf && ldconfig
ENV PATH="$PATH:/usr/local/ssl/bin"

ここで注意点としては、最後から2行目の

RUN echo "/usr/local/ssl/lib\n/usr/local/ssl/lib64" > ...

make install した際に、ARM64 では、/usr/local/ssl/lib に、x86_64 では /usr/local/ssl/lib64 とパスが変わるため、2行書いている。

/etc/ld.so.conf.d/openssl-3.0.8.conf
/usr/local/ssl/lib
/usr/local/ssl/lib64

ldconfig した際にないものは無視されるが、エラーにはならないので、これで。Dockerfile を分けるべきな気もするが横着した。気になる人は、分けてください。

PATH は、ENV としているが、

RUN echo "$PATH:/usr/local/ssl/bin" > /etc/environment && . /etc/environment

などでも良いはず。もっと良い方法もありそうだが、私の Debian 力ではいまはこれが精一杯。

なお、make, make install はめちゃくちゃ時間がかかるので、実運用では、/usr/local/ssl ディレクトリはローカルから COPY が良いのかも知れないが、本旨からはそれるので、後々考えることとする。

ここまでできれば、あとつまるところはないはず。

runner にて addgroupgroupadd かなどあるが、たいした問題ではない。

成果物

というわけで、サンプルを元に、image を alpine から buster-slim に変更し、OpenSSL3.0.8 に対応した Next.js + Prisma が動く Dockerfile と schema.prisma は以下のようになる。PORT はお好きに。サンプルは 3000 だ。

イメージサイズは、まだまだチューニングの余地がありそうだが、まぁ良いか…というレベルで、アプリを載せて、alpine の 2 倍、slim じゃない buster の 1/3 程度で、ECR 上で 130 MB くらいだったので、暇があったら検討するかも案件。

なお、じゃあこれを Production 用に使って良いか?というと、それはまた別の問題で、以下の Dockerfile のサンプルが、御社のセキュリティ要件を満たしているかは検証が必要だ。

以下のようになると書いたが、ふと我に返って、github に置いた。

TAKT-R-D / nextjs-prisma3-busterslim-openssl3 > Dockerfile

ローカル用 build

$ docker build ./ -t {your_container_name}

ECS 用 build

$ docker build --platform linux/amd64 ./ -t {your_container_name}

builder の yarn build でこける場合、prisma generate が通ってて、ローカルで build できるなら、yarn build できるので、再度 docker build しましょう。なんでこけることがあるのかは、まだ分かってない。

schema.prisma
generator client {
  provider      = "prisma-client-js"
  binaryTargets = ["native", "debian-openssl-3.0.x", "linux-arm64-openssl-3.0.x"]
}
...

なお、next.js の docker で、

ENV NEXT_TELEMETRY_DISABLED 1

は、匿名情報収集を許可しないという設定で、yarn build 中に

Attention: Next.js now collects completely anonymous telemetry regarding usage.

と、完全に匿名だから大丈夫的なメッセージが出てくるが、情報提供して良いかはアプリケーションのオーナーが決めるべきことだし、Next.js の発展にどうしても貢献したい!という情熱をお持ちでない限り、DISABLED にしておくのが無難だ。

おまけ

Intel Mac から M2(/M1) Mac に切り替えた際に詰まる点は、上記以外にもいくつかあった。

node, yarn のバージョン

ローカルの yarn のバージョンがいつから 4.0.0-rc.42 だったか記憶にないが、alpine でも buster-slim でも、yarn のバージョンは、1.22.19 である。4と1はだいぶ違うので、--fronzen-lockfile する際にうまく行かない。

というわけで、Project ごとに node, yarn のバージョンは定期的に見直す前提で、固定した方が間違いがない。Intel Mac では node のバージョン管理には、nodebrew を利用していたが、volta に切り替えた。

インストール方法は各自ググっていただくとして、プロジェクトごとに、

$ volta pin node@18.16.1
$ volat pin yarn@1.22.19

などとすると、バージョンを固定できる。package.json に、

package.json
{
  ...,
  "volta": {
    "node": "18.16.1",
    "yarn": "1.22.19"
  }
}

こんなものが追記される。
もちろん、volta を利用していなければ、無視される設定ではあるが、便利である。

実行 platform

ECS に Deploy する場合、platform=linux/amd64 にしないといけない。docker build の場合は前述の通りで、docker compose の場合は、以下のようにする

docker-compose.yaml
version: '3'

services:
  app:
    build:
      context: ./
      dockerfile: Dockerfile
    image: app_local
    platform: linux/amd64
    ports:
      - '8080:8080'

インストールできない package もある。

bycrypt など、M1/2 Mac ではインストールできない package もある。そのうちできるようになるかも知れないが対応が必要だ。ググると無理矢理インストールする方法などが出てはくるが、今回は、代替 package を使うことにした。bycryptjs 。大した使い方はしていなかったので、きちんと違いを読み込んではいない。

ChatGPT に聞くと…

動かなそうだし、試していないが、割りと的を得た回答だった。今どきは、色々ググって調べるよりも、ここからスタートするのも良いかも知れない。

(おしまい)

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3