「コンテナ起動でコーヒーを淹れる」のはもう終わりにしよう。Docker開発環境を爆速にするための全技術

「コンテナ起動でコーヒーを淹れる」のはもう終わりにしよう。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`を開いて、無駄なレイヤーを削ぎ落としてくれ。