Dockerビルドが遅いと嘆くお前へ。その「儀式」を今すぐ捨てろ

Dockerビルドが遅いと嘆くお前へ。その「儀式」を今すぐ捨てろ

「Dockerのビルドが遅い」。そうこぼすエンジニアに限って、決まって同じ地雷を踏んでいる。お前がやっているその作業、効率化などではない。ただの非効率な「儀式」を毎日繰り返しているだけだ。

ソースコードを一行書き換えるたびに数分のビルドを待つ……そんな無駄な人生を送りたいか? 俺は嫌だ。今回は、俺が血反吐を吐きながら学んだ、Dockerビルドの最適化と、その裏にある「キャッシュの深層心理」について語る。


1. キャッシュの「順番」という名の地雷原

なぜビルドが遅いのか。答えはシンプルだ。Dockerfileの命令順序が、Dockerのキャッシュ機構を殺しているからだ。

Dockerのキャッシュは、各命令を「ハッシュ値」として管理している。`COPY`命令であれば、ファイルの内容が一行でも変わればハッシュが変わり、それ以降のすべてのレイヤーが無効化される。

# 最悪のパターン
COPY . .
RUN npm install

これでは、`README.md`を修正しただけで`npm install`が走り出す。これが「ビルドが遅い」の正体だ。キャッシュを活かすには、変更頻度の低いものから先に書くのが鉄則だ。

# 鉄則:変更頻度が低いものを上に
COPY package*.json package-lock.json ./
RUN npm install
COPY . .

これで、ソースコードをいくら書き換えようと、`package.json`が変わらない限り`npm install`はスキップされる。この数行の順序を変えるだけで、お前のビルド時間は秒単位まで短縮されるはずだ。


2. マルチステージビルドという「禁じ手」

イメージサイズが1GBを超えるようなコンテナをデプロイしている奴、正直言ってセンスがない。本番環境には不要なビルドツールやソースコードを詰め込むのは、荷物を満載した状態でスポーツカーを走らせるようなものだ。

マルチステージビルドを使い、ビルド環境と実行環境を「完全に分断」せよ。

# ビルドステージ:重い環境
FROM node:18-bullseye AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# 実行ステージ:軽量な環境
FROM node:18-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm install --production
CMD ["node", "dist/index.js"]

これで、最終的な成果物にはビルド用の`node_modules`やソースコードは一切含まれない。デプロイのたびに数GBの転送を待つ時代は終わりだ。


3. Alpine Linuxという「甘い罠」

「とりあえずAlpineにしておけば軽量で最強」……そう信じていた時期が俺にもあった。だが、これは大きな間違いだ。

以前、画像処理ライブラリの`Sharp`やブラウザ自動化の`Puppeteer`をAlpineで動かそうとして地獄を見た。`libc`(glibc)ではなく`musl libc`を採用しているAlpineでは、バイナリの互換性が取れず、「Shared library not found」というエラーが連発する。

解決に何時間も溶かすくらいなら、素直に `debian-slim` を選べ。数MBの軽量化のために、数時間のデバッグ工数をドブに捨てるのか? 効率を追い求めすぎて、本質を見失うな。


4. Docker Compose:ボリュームマウントの優先順位

ローカル開発でコンテナを毎回再ビルドしている奴は、今すぐやめろ。Docker Composeのボリュームマウントを使えば、変更は即座に反映される。

services:
  app:
    build: .
    volumes:
      - .:/app
      - /app/node_modules  # 匿名ボリューム
    command: npm run dev

ここで重要なのは、`/app/node_modules` を匿名ボリュームとしてマウントすることだ。

なぜこれが必要か? 理由は「マウントの優先順位」にある。ローカルのディレクトリをマウントすると、コンテナ内の`node_modules`がローカルのそれによって上書きされる。もしローカルに`node_modules`がなければ、コンテナ内のものが隠されてしまい、「モジュールが見つかりません」という謎の不具合で3時間は消える。匿名ボリュームを置くことで、コンテナ内の`node_modules`をローカルの汚染から守れるんだ。


最後に:お前はまだ「儀式」を続けるのか?

ここまで読んで「なるほど、やってみるか」と思った奴は、まだマシだ。だが、この記事を読んでもなお「忙しいから」と言い訳して今の遅いビルドを放置する奴は、一生その「儀式」から抜け出せない。

技術とは、苦痛を自動化し、時間を捻出するためにある。ビルドが遅いのは、お前がその環境をハックすることを諦めた結果だ。

さあ、今すぐDockerfileを開け。そして、不要なレイヤーを削ぎ落とし、キャッシュのロジックを最適化しろ。それでもまだビルドが遅いと言うなら、お前のコードが重すぎるだけだ。それは、また別の話だな。