Next.jsをYarn 2のPnP/Zero-Installs + Dockerで動かす
Yarn 2ではPnP(Plug'n'Play)という機能が導入され、これに基づくZero-Installsというワークフローが提案されています。
この機能を使っている場合、Next.jsが公式で用意しているDockerfileのサンプルでは動作しません。
本記事では、PnP/Zero-Installsの概要について紹介した上で、Zero-Installsで動くよう改造したDockerfileを紹介します。
なお、本記事で使用しているプロジェクトは下記GitHubリポジトリでも公開しています。
結果だけ知りたい方は Dockerfile を参照してください。
使用しているソフトウェアのバージョン
- Yarn 1: 1.22.0
- Yarn 2: 2.4.1
- Next.js: 10.1.3
- Node.js: 15.13.0
- Docker: 20.10.5
- macOS: 11.2.3
PnPとは
PnP(Plug'n'Play)とは、Yarn 2で導入された、Node.jsモジュールの解決方法です。
Yarn 1では、npmと同様、node_modulesディレクトリに全ての依存を展開して管理していました。この方法にはさまざまな問題があり、解決策として考案されたのがPnPです。
PnPでは、依存関係の解決のために .pnp.js というスクリプトを用意します。このスクリプトをNode.jsアプリケーションの起動時に読み込むことで、node_modulesを使わず、独自の方法でモジュールのロードを行います。
既存の node_modules とは異なるモジュールロードの仕組みを用いることで、従来の node_modules にあった問題(モジュールインストールの遅さ、不安定さなど)を解決することを目指しています。
Zero-Installsとは
Zero-Installsは、PnPに基づいた新しいワークフローで、端的にいうと開発のワークフローで yarn install
を使わないことをいいます。
具体例を見た方がわかりやすいので、Yarn 2のZero-Installsを使ってプロジェクトをセットアップする方法を見ていきます。
Yarn 2でプロジェクトを作成する
まず、yarnコマンドをインストールして、プロジェクトを作成し、プロジェクトでYarn 2を使用する設定を行います。2021/04/17現在、Yarnのデフォルトバージョンは1系で、オプションで2系を有効化できるようになっています。
# yarn コマンドをグローバルにインストール( Homebrew 等を使ってもOK )
npm install -g yarn
# yarnのバージョン確認。 1.22.10 など1系のバージョンになるはず
yarn -v
# プロジェクトのディレクトリに移動
cd ~/path/to/project
# Yarn 2を有効化
yarn set version berry
# yarnのバージョン確認。 2.4.1 など2系のバージョンになるはず
yarn -v
yarn set version berry
すると、 .yarn ディレクトリが作成され、その中にYarn 2のスクリプトがダウンロードされます。
.yarn
└── releases
└── yarn-2.4.1.cjs
あわせて .yarnrc.yml というファイルも作成され、ここにYarn 2のスクリプトへのパスが記載されます。
yarnPath: .yarn/releases/yarn-2.4.1.cjs
この状態で yarn init
すると、package.jsonに加え、.gitignoreなどいくつかのファイルが生成されます。
.
├── .editorconfig
├── .gitattributes
├── .gitignore
├── README.md
├── package.json
└── yarn.lock
これでYarn 2のプロジェクト作成は完了です。
Next.jsの導入
作成したプロジェクトにNext.jsを導入してみます。
yarn add next react react-dom
これによって、Yarn 1と同様 package.json と yarn.lock が更新されますが、Yarn 1と異なり node_modules は作成されません。
その代わり、 .pnp.js という依存解決のためのスクリプトが生成され、さらに .yarn/cache の中にライブラリがインストールされます。
Zero-Installs では、 .pnp.js 及び .yarn/cache をリポジトリに入れて管理します。これによって、リポジトリからソースコードをチェックアウトすれば必要なライブラリが一式ダウンロードされるので、CI環境等でチェックアウト後にライブラリをインストールするする必要がなくなります。
次に、Next.jsを実行するのに最低限必要な pages/index.js を追加します。
const Index = () => "hello"
export default Index
ついでに package.json によく使うスクリプトを追加しておきましょう。
{
...
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
...
}
準備ができたら yarn dev
でNext.jsアプリケーションが起動できることを確認します。
Zero-Installsの動作確認
本当にZero-Installsが実現できているか確認するため、別の場所にプロジェクトを作って動かしてみます。
cd /tmp
git clone git@github.com:ryo-utsunomiya/nextjs-yarn2-zero-installs.git
cd nextjs-yarn2-zero-installs
上手くいっていれば、ここで yarn dev
を実行すればすぐにNext.jsアプリケーションが起動するはずです。従来のように yarn install
を実行する必要はありません。
Dockerfileの改造
Next.jsには公式にサンプルDockerfileが用意されています。これは yarn install
の実行を前提としているので、Zero-Installsではそのままでは使えません。
改造の必要なポイントは3点あります: (1) yarn install
を使わない (2) Yarn 2を有効化する (3) PnPのためのファイルをコピーする
FROM node:alpine AS builder
WORKDIR /app
COPY . .
# (1) yarn installは不要。buildだけ
RUN yarn build
FROM node:alpine AS runner
WORKDIR /app
ENV NODE_ENV production
# (2) Yarn 2を有効化
COPY /app/.yarnrc.yml ./.yarnrc.yml
COPY /app/.yarn ./.yarn
# (3) PnPのために必要なファイル
COPY /app/.pnp.js ./.pnp.js
COPY /app/yarn.lock ./yarn.lock
# 以降は通常のNext.jsに必要なファイル
COPY /app/.next ./.next
COPY /app/package.json ./package.json
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
RUN chown -R nextjs:nodejs /app/.next
USER nextjs
EXPOSE 3000
CMD ["yarn", "start"]
最後に docker build . -t my-next-js-app
でDockerイメージをビルドし、 docker run -p 3000:3000 my-next-js-app
でコンテナを立ち上げます。
先ほどと同様、 http://localhost:3000 でアプリケーションが立ち上がることを確認できたらOKです。
以上、Next.jsでYarn 2のZero-Installsを使う方法と、その際に必要なDockerfileについて解説しました。
Discussion
Thank you, I spent the whole evening with this problem, and you explained it so simply! 🙏
Hi, from Russia.