Docker Composeの爆速化:なぜお前の環境は「重い」のか?
「Docker Composeのビルドが遅い」「ホットリロードのたびにPCが離陸しそうになる」。そんな地獄のような開発体験を、いつまで放置するつもりだ?
ネットに転がっている「Dockerfileの書き方」をなぞるだけでは、この問題は解決しない。Dockerの仕組みを理解せず、ただマウントして動かしているだけなら、それは「開発環境」ではなく「ただの重い箱」だ。
本質は、「Dockerのファイルシステムとどう付き合うか」という一点に集約される。今日でその泥沼から脱出しよう。
1. マウントの「落とし穴」を埋める
開発環境でホットリロードが重い最大の原因は、Dockerのファイルシステム(VirtioFSやgRPC FUSE)への過剰な負荷にある。ローカルの全ファイルをマウントし、`node_modules`までホストと同期させようとするのは、Dockerに対する「自爆テロ」に近い。
匿名ボリュームという「隔離壁」
ホスト側の`node_modules`をコンテナにマウントしてはいけない。ローカルのゴミファイルやOS依存のバイナリがコンテナ内のプロセスと干渉し、ファイル監視(inotify)がパンクするからだ。修正前(死ぬコード):
volumes:
- .:/app # これをやるとnode_modulesまで共有され、監視負荷が爆増する
修正後(最適解):
services:
app:
build: .
volumes:
- .:/app
- /app/node_modules # 匿名ボリュームでローカルの干渉を遮断する
これで、コンテナ内のライブラリはコンテナ内で完結する。たったこれだけで、ファイル変更時の反応速度は劇的に変わる。
2. マルチステージビルドで「ビルドキャッシュ」を支配する
マルチステージビルドを単なる「イメージ軽量化の道具」だと思っているなら、それは大きな誤解だ。真の価値は「ビルドプロセスの依存関係の切り分け」にある。
開発環境と本番環境でDockerfileを分ける必要はない。一つのDockerfileをステージ分けし、`docker-compose.yml`からターゲットを指定するだけでいい。
Dockerfileの再構成
# ステージ1: 依存関係のインストール(ここが一番重い)
FROM node:18-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm install
# ステージ2: 開発環境用(ホットリロード用)
FROM node:18-alpine AS runner
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
CMD ["npm", "run", "dev"]
docker-compose.ymlでのターゲット指定
services:
app:
build:
context: .
target: runner # ここでステージを切り替える
こうすれば、`package.json`を書き換えない限り、`deps`ステージはキャッシュされ続ける。コードの変更が反映されるのは`runner`ステージだけ。これが爆速ビルドの正体だ。
3. .dockerignoreをサボるな:ビルドコンテキストの闇
俺が過去に一番ハマったのは、`.dockerignore`の放置だった。ある日、プロジェクトルートに数GBの`.git`ディレクトリや動画素材を放置したままビルドを回し、「なんで毎回Dockerデーモンへの転送に1分もかかるんだ?」と頭を抱えた。
`docker build`を実行した瞬間、Dockerはプロジェクト内の全ファイルを「コンテキスト」としてデーモンに転送する。不要なファイルが含まれていれば、当然その転送時間はビルド時間に加算される。
必須の`.dockerignore`:
.git
node_modules
dist
*.log
.DS_Store
tmp
この一行を書くか書かないかで、ビルドのスタートラインが数秒単位で変わる。まずはここを見直せ。
明日から導入すべき「爆速化チェックリスト」
環境構築に時間を溶かすのはもう終わりにしよう。以下の3項目を今すぐ適用してくれ。
| 項目 | アクション | 効果 |
|---|---|---|
| ボリューム隔離 | `/app/node_modules` を匿名ボリューム化 | ファイル監視負荷の激減 |
| ターゲット分離 | マルチステージビルドで `target` を定義 | ビルドキャッシュの最適化 |
| コンテキスト削減 | `.dockerignore` を徹底的に書く | 初回ビルド・転送時間の短縮 |
最後に
複雑な同期ツールを入れるのは、これら全てをやった後の「最終手段」でいい。道具に頼る前に、Docker本来の仕組みを使い倒せ。エンジニアの時間は、環境設定のデバッグではなく、プロダクトの価値を最大化するために使うものだろ? 今日紹介した手法は、特別なツールもコストも不要だ。今すぐ設定ファイルを書き換えて、その「軽さ」を体感してほしい。