yasuworks.com

Docker × pnpm × Next.js の環境構築

Cover Image for Docker × pnpm × Next.js の環境構築

こちらのブログを立てたとき、書いてある手順通りにやっただけだといまいち何をしているかよくわからなかったので、今回は手順を順番に調べながら、かつちょっと前の手順とはずらしながら環境構築とサイトの公開をしてみます。

https://yasuworks.com/posts/first-post

ペンギンってなんとなく自立のイメージがありますよね。

方針

今回は以下の点をおさえてやってみます。

  • DockerでNodeの実行環境を立てて、コンテナ内でセットアップする
  • パッケージマネージャーにpnpmを使ってみる

環境構築

まずNodeとpnpmを動かせるDockerコンテナを作ります。シンプルにこんな感じ。

Dockerfile
FROM node:22-slim AS base
WORKDIR /app
RUN yarn global add pnpm
compose.yaml
services:
  app:
    build:
      context: .
    volumes:
      - .:/app
    working_dir: /app

最初は pnpm 公式の手順に則って、corepackを使って色々試してみていたのですが、調べるとcorepackは非推奨になったらしく、なんとなく避けて yarn で直接追加してます。どっちも同じくらい出てくるので、良し悪しはいずれ調べます。

https://pnpm.io/ja/docker

Dockerの書き方についてはまだ無駄がありそうですが、気にすると一生ここで止まってしまいそうなので、Dockerについてはいずれしっかり勉強しようと思います。理解して使いこなせたらかなり気持ちよさそう。

続いて、Docker内の pnpm で下記コマンドを実行して、Nextの環境構築を行います。

docker compose run --rm app pnpm create next-app@latest tmp && mv tmp/* . && mv tmp/.* . && rm -r tmp

一時的にtmpディレクトリ内に展開してからファイルを移動しているのは、下記のコンフリクトエラー対策になります。例えば、

docker compose run --rm app pnpm create next-app@latest .

このようにcreate next-appの展開先にカレントディレクトリを指定すると、

The directory app contains files that could conflict:

  Dockerfile
  compose.yaml

Either try using a new directory name, or remove the files listed above.

こんな感じでエラーになって終了してしまいます。コマンド上で指定してもCLIで指定しても同じ結果になります。
ディレクトリ内にファイルがあるとダメみたいです。この場合はDocker系のファイルですね。なんとかそのまま展開してほしかったのですが、素直に一時ディレクトリに展開したあとに移動するのが良さそうです。
(package.jsonnameがここで指定した名前になるので、そこだけ注意が必要です。)

また、.pnpm-storeディレクトリがルートに作成されているので、コミット前に.gitignoreに追加しておきます。

.gitignore
.pnpm-store

諸々完了後、コンテナでpnpm devを実行して、localhost上でNextの初期画面が表示されれば無事環境構築完了です。やったね。

以降は公開するまでの過程でひっかかったところをメモしておきます。

Firebase Hosting に複数サイトがある場合の設定

Firebaseの同じプロジェクト内に複数サイトがある場合、Firebase CLI の firebase initそのままだとメインのサイト?にデプロイされてしまうようでした。

https://yasuworks.com/posts/sub-domain-xserver-firebase

またその場合は、構成ファイルにサイトIDを指定する必要があります。

firebase.json
{
  "hosting": {
    "public": "out",
+    "site": "SITE_ID",
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"]
  }
}

まず、サイトのIDを確認します。Firebase Hosting上で確認するか、以下のコマンドを実行することで、サイトの一覧を確認できます。

firebase hosting:sites:list

サイトIDを構成ファイルに追加したら、あとはfirebase deployを実行して、公開できていれば問題ありません。

GitHub Actions の修正

Github Actionsに pnpm のコマンドを追加しても、そのままだと動かないらしいので、@pnpm/action-setupを追加します。

https://github.com/pnpm/action-setup?tab=readme-ov-file#use-cache-to-reduce-installation-time

上記公式のサンプルを参考に、Firebase CLIで自動生成されるGAに対して以下のように修正を加えました。

firebase-hosting-merge.yml
# This file was auto-generated by the Firebase CLI
# https://github.com/firebase/firebase-tools

name: Deploy to Firebase Hosting on merge
on:
  push:
    branches:
      - main
jobs:
  build_and_deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
+      - uses: pnpm/action-setup@v4
+        with:
+          version: 9.7.0
+          run_install: false
+      - uses: actions/setup-node@v4
+        with:
+          node-version: 22
+          cache: "pnpm"
      - run: pnpm install --frozen-lockfile && pnpm run build
      - uses: FirebaseExtended/action-hosting-deploy@v0
        with:
          repoToken: ${{ secrets.GITHUB_TOKEN }}
          firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_MY_PORTFOLIO_C7FB6 }}
          channelId: live
          projectId: my-portfolio-c7fb6

pnpmの実行環境用のステップを追加してあげただけですね。キャッシュを使ってもらうようにして高速化しています。

感想

いまのところpnpmの旨味を理解するところまでできていないですが、ほどほどにモダンくらいの環境が3割くらいの自力で構築できて一旦満足しております。

また、Dockerにはだんだん親しんできたような、まだまだ使いこなすには先が遠いような…。確実に前進している実感はありますが、いまいちDockerfilecompose.yamlのどっちにどんなことを書けばいいのかよくわかっていないですね。

内容的に勉強ノート的な側面が強く、ファクトチェックも甘々なので、いつか自分で見返したときに、な〜に言ってるだコイツは、とかなりそうでこわいなあ。