0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Yarn 4 移行で出会った落とし穴と Docker 開発環境の再現性を支えるために工夫したこと

Posted at

こんにちは、Tsucchiiです。

とあるプロジェクトで Yarn 4 (Berry) へ移行した際、Docker 環境で
yarn prisma が動かない」「node_modules が古いまま」といった問題に直面しました。
この記事では、その原因と対処法、Corepack・Volume運用を通じて得た知見を紹介します。


1. 移行の背景

Next.js + Docker の開発環境で、node_modules名前付きボリューム(named volume) で切り離して管理しています。
(ソースコードは bind mount、依存は named volume という構成)

# docker-compose.yml(抜粋)
services:
  web:
    volumes:
      - ./web:/app
      - node_modules:/app/node_modules
      # ...
volumes:
  node_modules:
  # ...

この構成では、entrypoint.sh でコンテナ起動時に /app/package.json/app/yarn.lock のハッシュを計算し、
前回保存した値と比較します。差分があれば(または .yarn_hash が無ければ)
yarn install --immutable を実行して node_modules を最新化する仕組みになっています。

#!/bin/bash
# web/entrypoint.sh(移行前のイメージ)

if [ -f /app/.yarn_hash ]; then
  OLD_HASH=$(cat /app/.yarn_hash)
  CURRENT_HASH=$(md5sum /app/package.json /app/yarn.lock | sort | md5sum | cut -d' ' -f1)

  if [ "$OLD_HASH" != "$CURRENT_HASH" ]; then
    echo "Dependencies changed, updating node_modules..."
    yarn install --immutable
    echo "$CURRENT_HASH" > /app/.yarn_hash
  fi
else
  yarn install --immutable
  md5sum /app/package.json /app/yarn.lock | sort | md5sum | cut -d' ' -f1 > /app/.yarn_hash
fi

exec "$@"

しかし当時は Dockerfile で Yarn のバージョンを固定していなかったため、
docker-compose up 時に Yarn 1 が実行され、yarn.lock が書き換わる問題が発生。
再現性を高めるために Yarn 4 (Berry) へ統一することにしました。


2. 最初に行った変更 ─ Corepack で Yarn を固定化

「Docker 上でも Yarn 4.6.0 を使う」ため、まず次の対応を行いました。

2-1. Dockerfile で Corepack を有効化

# docker/web/Dockerfile.dev(一部抜粋)
ARG YARN_VERSION=4.6.0

RUN corepack enable && corepack prepare "yarn@${YARN_VERSION}" --activate
RUN YARN_ENABLE_SCRIPTS=false yarn install --immutable

2-2. entrypoint でも Yarn 4 を起動時に有効化

# web/entrypoint.sh(Yarn 4 強制後)
YARN_VERSION=${YARN_VERSION:-4.6.0}

if command -v corepack >/dev/null 2>&1; then
  corepack enable >/dev/null 2>&1
  corepack prepare "yarn@${YARN_VERSION}" --activate >/dev/null 2>&1
fi

# この時点では 後述のPATH 補正はまだ入っていません

この時点で Dockerfile と entrypoint の双方で Yarn 4.6.0 が使われる状態になりました。


3. 変更によって発生した問題

しかし「これで移行完了」とはいかず、複数のトラブルが顕在化しました。

3-1. yarn prisma / yarn nextcommand not found

$ yarn prisma migrate dev
Usage Error: Couldn't find a script named "prisma".

3-2. PATH 補正を忘れると next が見つからない

Corepack で Yarn 4 を有効にしただけでは /app/node_modules/.binPATH に含まれず、
command not found: next が頻発。

3-3. named volume の初期化で古い node_modules がコピーされる

docker-compose down -v → docker-compose up をすると、
新しく追加した依存だけ module not found になるケースが発生(理由は後述)。


4. 各トラブルの原因と解決策

4-1. CLI 呼び出しを npx / yarn exec に統一

Yarn 1 までは yarn prismayarn next のような 省略形 CLI が使えましたが、
Yarn 4 (Berry) ではこの形式が廃止されました。

そこで、package.jsonscriptsnpx に統一し、Makefile や README といったドキュメントも更新します。

- "dev": "prisma generate && next dev --turbopack",
+ "dev": "npx prisma generate && npx next dev --turbopack",
コマンド Yarn 1 Yarn 4
yarn next ✅ 可 ❌ エラー
yarn exec next ✅ 可 ✅ 可
npx next ✅ 可 ✅ 可

4-2. entrypoint で PATH を補正

corepack enable / corepack prepareYarn 4 の実行ファイルを用意する だけで、
シェルの PATH には手を加えません。

Yarn 4(Berry)は yarn run 実行時に一時的に .bin を追加しますが、
exec "$@" で普通のシェルコマンドを起動する場合は PATH に .bin が入らず、
command not found: next になります。

# web/entrypoint.sh(最終形の抜粋)
export PATH="/app/node_modules/.bin:$PATH"

💡 補足: PATH を補正しないと command not found: next エラーが解消されません。


4-3. named volume の初期化を正しく扱う

Docker の named volume は、初回マウント時にイメージ内の /app/node_modules を丸ごとコピー します。
そのため、古いイメージを使って docker-compose up を行うと、古い依存が volume に複製されたままになります。

※ entrypoint のハッシュチェックは「lockfile と node_modules が一致」と誤認し、
yarn install をスキップしてしまいます。

運用ルール

依存を追加・更新したときは必ず以下をセットで実行します。

docker-compose down -v   # volume 削除
docker-compose build     # イメージ再ビルド
docker-compose up -d     # 最新依存で起動

5. Volumes と bind mount の違い

項目 bind mount (./web:/app) named volume (node_modules:/app/node_modules)
中身の実体 ホストのファイルを直接参照 Docker が管理する専用ストレージ
反映タイミング ホスト変更が即反映(ホットリロード向き) 初期化時にイメージからコピー、その後は独立
I/O パフォーマンス macOS/Windows は遅くなりがち Linux 上で完結するため高速
ネイティブモジュール ホストと OS 不一致で不整合発生 コンテナ内ビルドで再現性が高い

named volume を使うと再現性は上がりますが、
“最初にイメージ内の /app/node_modules がコピーされる” という仕組みを理解しておく必要があります。
古い依存を持ったイメージのまま down -v → up すると、そのイメージの内容が volume に複製されてしまいます。


6. CI/CD にも効果 ─ ビルド時間が半分に

Yarn 4 へ移行し、yarn install --immutable に統一した結果、
ステージングデプロイ時間が 12〜15分 → 約7分 に短縮 しました。

主な理由は以下の通りです。

  • Dockerfile から不要な yarn add canvas などを削除し、キャッシュ効率を改善
  • CI ランナーでも Yarn 4 を使用し、lockfile の揺れや再インストールを防止

7. 学びとまとめ

  • Yarn 4 では CLI を npx / yarn exec に統一

  • Corepack で Yarn バージョンを固定し、PATH を補正

    • corepack enable → corepack prepare → export PATH=/app/node_modules/.bin:$PATH
  • named volume 初期化時は イメージが初期値としてコピーされる

    • docker-compose down -v → build → up を徹底し、古い依存を防ぐ

8. トラブルシューティングに役立ったコマンド

# Yarn 4(Berry)が有効か確認
docker-compose run --rm web yarn --version

# Prisma / Next CLI の動作確認
docker-compose run --rm web npx prisma --version
docker-compose run --rm web npx next --version

# named volume の中身を確認(Volume 名は環境に合わせて)
docker volume ls
docker run --rm -v <project>_node_modules:/data alpine ls /data

# 依存を確実に反映
docker-compose down -v
docker-compose build
docker-compose up -d

9. 同じ課題に直面している方へ

Yarn 4 への移行は lockfile 揺れを防ぐ うえで非常に有効ですが、
従来の CLI や Docker 運用とは挙動が異なります。

特に named volume の初期化の仕組み を理解しておくことで、
依存追加時のトラブルを未然に防ぐことができます。

この記事が同じ課題に直面している方の一助になれば幸いです。
質問やフィードバックはお気軽にどうぞ!

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?