「コンテナ起動でコーヒーを淹れる」のはもう終わりにしよう。Docker開発環境を爆速にするための全技術
「Docker Compose up」を叩いてから、コーヒーを淹れに席を立つ。戻ってきてもまだビルド中。そんな「待ち時間」を前提にした開発スタイルに、いい加減うんざりしていないか?
かつての僕は、まさにその地獄にいた。コードを一行変えるたびに数分の再ビルドが走り、ホットリロードは保存から数秒遅れて発火する。モダンな開発環境を構築したはずが、実際にはPCのファンを唸らせるだけの「苦行製造機」を動かしていたに過ぎなかった。
この記事は、僕がその地獄から脱出し、開発サイクルを「爆速」に引き上げるために叩き込んだ、泥臭い最適化の全記録だ。
1. なぜ、あなたのDockerは重いのか?
最大の敗因は、「プロダクション用のDockerfileをそのまま開発に使っている」ことだ。
多くの人がやってしまいがちなのが、全ファイルをCOPYして`npm install`を叩く構成。これだと、たった一つのCSSを修正しただけで、依存関係のインストールからやり直しになる。Dockerのレイヤーキャッシュを知らない頃の僕が犯した最大の間違いだ。
開発と本番を切り離す「ステージ分け」の極意
Dockerfileは、開発(dev)と本番(prod)で明確に分けるべきだ。`target`を指定することで、必要なレイヤーだけを使い分ける。
# 共通ベース
FROM node:18-alpine AS base
WORKDIR /app
COPY package*.json ./
# 開発用ステージ
FROM base AS dev
# 依存関係をインストールしてからソースをマウントする
RUN npm ci
# ホットリロードのための環境変数
ENV CHOKIDAR_USEPOLLING=true
CMD ["npm", "run", "dev"]
# 本番用ステージ
FROM base AS prod
RUN npm ci --only=production
COPY . .
RUN npm run build
CMD ["node", "dist/main.js"]
ここがポイント:
`package.json`を先にCOPYして`npm ci`を実行する。これで、`package.json`に変更がない限り、依存関係のレイヤーはキャッシュされる。ソースコードを何度書き換えても、重いインストール処理はスキップされる。これだけで、ビルド時間は劇的に短縮される。
2. ファイル同期の「見えない壁」を突破する
Docker Desktopのファイル共有パフォーマンスは、ここ数年で劇的に進化した。特にMacを使っているなら、VirtioFS(設定の「General」から有効化可能)を使わない手はない。これだけで、旧来のgRPC FUSEとは比較にならないほど高速に同期される。
それでも遅いと感じる場合、それは「ファイルの監視(inotify)の制限」が原因だ。
delegatedオプションと正しいマウント戦略
`docker-compose.yml`でボリュームをマウントする際、`delegated`オプションを付与しよう。これは「コンテナ側の変更をホストへ即時反映しなくていいから、ホスト側の変更を優先的にコンテナへ渡せ」という命令だ。
services:
app:
build:
context: .
target: dev
volumes:
- .:/app:delegated
- /app/node_modules # 名前付きボリュームで分離
user: "${UID}:${GID}" # 環境変数で動的に指定
environment:
- NODE_ENV=development
【重要】絶対にやってはいけないこと:
`node_modules`をホストからマウントしてはいけない。ホストOSが大量のファイル監視に追われてリソースを食い尽くす。`node_modules`はコンテナ内のみに存在する「名前付きボリューム」に隔離するのが鉄則だ。
3. 権限トラブルで消耗しないために
「`.env`ファイルに`UID=$(id -u)`を書いておけ」。
これがチーム開発における唯一の正解だ。Dockerfileでユーザーをrootのまま動かすと、コンテナが生成したファイルがroot所有になり、ホスト側で削除できなくなる。
.envファイル:
UID=1000
GID=1000
これを`docker-compose.yml`で`user: "${UID}:${GID}"`と指定すれば、ホストとコンテナ間でファイルのパーミッションが一致し、ストレスフリーな開発環境が手に入る。
4. 「なんとなく」でCHOKIDAR_USEPOLLINGを使うな
よく「ホットリロードが効かないから」と`CHOKIDAR_USEPOLLING=true`を盲目的に追加する人がいるが、これは最終手段だ。
これはポーリング(定期的なファイル確認)を行う設定だが、CPU負荷が極端に高い。ファンが爆音で回り、PCが熱暴走しかけるのはこれが原因だ。まずはVirtioFSの有効化や、マウント設定の見直しを先に行うべき。どうしても検知できないケースにのみ、このオプションを検討しよう。
最後に:なぜ「速さ」にこだわるのか
開発中の数秒の待ち時間は、単なるロスではない。思考のコンテキストスイッチが強制的に発生し、集中力が削がれる「クリエイティビティの敵」だ。
- `.dockerignore`を徹底する: `node_modules`や`.git`をイメージに混ぜるな。ビルド時間は確実に削れる。
- マルチステージビルドを活用する: 開発と本番のDockerfileを分けろ。
- ボリューム設定を最適化する: `delegated`と名前付きボリュームでホストOSを解放しろ。
さあ、今すぐ`docker-compose.yml`を開いて、無駄なレイヤーを削ぎ落としてくれ。