TL;DR
Before (詰み) | After (解決) |
---|---|
context が .devcontainer 直下 → ビルド時に package.json が見えない
|
context: .. で プロジェクトルート を丸ごと渡す |
volumes: ./:/usr/src/app → コードが実体化しない
|
volumes: ../:/usr/src/app に合わせてマウント |
スクリプト完了でコンテナ終了 → VS Code が接続できない |
tail -f /dev/null を最後に添えて常駐 |
依存解決をすべて RUN npm install で実行 → build キャッシュが無効
|
COPY package*.json → npm i → COPY . の三段構えで高速化 |
1. 環境
項目 | バージョン |
---|---|
OS | Windows 11 + WSL2(Ubuntu) |
Docker Desktop | 4.42.1 (engine 28.2.2) |
VS Code | 1.102.1 + Dev Containers 0.422.1 |
Node | 18 |
Puppeteer | ^22 |
Dev Container 構成 | .devcontainer/{dockerfile, docker-compose.yml, devcontainer.json} |
2. 事象とエラーログ
2-1. MODULE_NOT_FOUND: puppeteer/install.js
Error: Cannot find module '/usr/app/node_modules/puppeteer/install.js'
...
Node.js v18.20.8
2-2. npm ERR! enoent Could not read package.json
npm ERR! enoent Could not read package.json: ENOENT: no such file or directory, open '/usr/src/app/package.json'
2-3. Dev Container が起動後すぐ停止
Error response from daemon: container ... is not running
VS Code: "An error occurred setting up the container."
3. 原因を掘る 🔍
症状 | 真因 |
---|---|
puppeteer の post-install が失敗 |
Dockerfile が .devcontainer 内だけをビルド → node_modules 生成前に install.js を叩こうとして空振り |
package.json が見つからない |
context を誤指定 (. ) したため ルートがビルドに含まれず
|
コンテナが即死 |
command に指定した Node スクリプトが完了 → PID 1 が終了 → コンテナ停止 |
4. 施した4つの修正
4-1. docker-compose.yml
を全面修正
services:
scraper:
# ▶︎ ビルド対象はプロジェクト全体
build:
context: .. # ← 変更!
dockerfile: .devcontainer/dockerfile
tty: true
ports:
- "80:80"
# ▶︎ マウントも親ディレクトリへ合わせる
volumes:
- ../:/usr/src/app
# ▶︎ スクリプト完了後もプロセスを残す
command: >
bash -c "npm install &&
node scripts/scrape_comenu.js || echo 'script ended'; tail -f /dev/null"
4-2. Dockerfile
をベストプラクティス寄りに
FROM node:18
# ① Chrome とフォント類を最小インストール
RUN apt-get update \
&& apt-get install -y wget gnupg --no-install-recommends \
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub \
| gpg --dearmor -o /usr/share/keyrings/googlechrome-linux-keyring.gpg \
&& echo "deb [arch=amd64 signed-by=/usr/share/keyrings/googlechrome-linux-keyring.gpg] \
http://dl.google.com/linux/chrome/deb/ stable main" \
> /etc/apt/sources.list.d/google.list \
&& apt-get update \
&& apt-get install -y google-chrome-stable fonts-ipafont-gothic --no-install-recommends \
&& rm -rf /var/lib/apt/lists/*
# ② 依存解決をキャッシュ効く順に
WORKDIR /usr/src/app
COPY ../package*.json ./ # ← まず package.json だけ
RUN npm ci --unsafe-perm
# ③ 残りのコードを最後にコピー
COPY .. .
CMD ["node","scripts/scrape_comenu.js"]
4-3. devcontainer.json
{
"name": "MealMax Scraper Dev",
"dockerComposeFile": ["docker-compose.yml"],
"service": "scraper",
"workspaceFolder": "/usr/src/app",
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
},
"extensions": [
"dbaeumer.vscode-eslint",
"eamodio.gitlens"
],
"skipFeatureAutoInstall": true
}
5. 動作確認 ✅
# devcontainer CLI が生成した compose プロジェクト
$ docker compose -p scraper_devcontainer ps
NAME STATUS PORTS
scraper_devcontainer-scraper-1 Up About a minute 0.0.0.0:80->80/tcp
VS Code からもコンテナに入り、node scripts/scrape_comenu.js
が正常に動作することを確認。
6. Ultra-Thinking💡: もう二度とハマらないための5箇条
-
context に渡すディレクトリ = コンテナに必要な最小ソースの集合
→docker-compose config
で実際のパス解決を 必ず チェック。 -
COPY
手順はキャッシュを意識して二段構え
1️⃣COPY package*.json
→npm ci
2️⃣COPY .
(コード変更時だけ再ビルド) -
Dev Containers では volume と context を “同じ階層” に合わせると楽
→ ホスト側で変更して即反映、ビルド時も同じパス基準。 -
コンテナを落とさないワンライナー
cmd || true; tail -f /dev/null
… 開発時の定番テク。 -
エラーは 上から順に 直す
MODULE_NOT_FOUND
は大抵「コピー漏れ」or「mount ミス」。焦って npm を疑わない。
7. まとめ 📝
-
“コンテキストとマウントのズレ” が原因で、
-
package.json
不在 → ビルド失敗 - スクリプト終了 → コンテナ停止
に連鎖していた。
-
-
context
とvolumes
をプロジェクトルートに合わせ、Dockerfile
を段階的コピーに直しただけで すべて解決。 -
Dev Containers は「設定フォルダ(.devcontainer)とアプリ本体を切り分ける」のが王道。
これであなたも “Docker コンテキスト地獄” とはお別れ!
Happy Container Hacking🐳✨