動かない時
イメージが肥大化している
数 GB に膨らんだイメージを `dive` と `docker history` で分析し、不要なレイヤーとキャッシュを剥がして劇的に縮小する手順。
症状
- 簡単な Node.js / Python アプリなのにイメージが 2GB を超える
- CI からのプッシュ・プルが毎回遅く、デプロイのボトルネックになっている
- レジストリのストレージ料金が膨らんでいる
- コンテナ脆弱性スキャン(Trivy など)の検出数が異常に多い(不要ツールが大量に入っている兆候)
原因
イメージが肥大化する典型パターン。
- ベースイメージがフル版:
node:20(Debian フル)は 1GB 超。node:20-slimやnode:20-alpineで大幅に減る - ビルド依存を削ぎ落としていない:
gcc/make/python-devなどネイティブモジュールビルドにだけ必要なパッケージが最終イメージに残っている - キャッシュを消していない:
apt-get installした後の/var/lib/apt/lists/が 100MB 以上、pipのキャッシュが~/.cache/pipに数百 MB 残っている - レイヤー分離の失敗: 機密やログを含む巨大ファイルを一度
ADDしてから次のレイヤーでrmしている → 削除しても前のレイヤーには残るためサイズは減らない - マルチステージビルドを使っていない: ビルド用ツールチェインと実行用ランタイムが同じイメージに同居している
- node_modules / target / pycache などを丸ごとコピー:
.dockerignoreが甘い
確認コマンド
現状把握が大事です。
どのレイヤーで何 MB 食っているかが分かれば、8 割は解決したようなもの。
bash
# サイズ全体
docker image ls myapp
# レイヤーごとのサイズと生成コマンド
docker history myapp:latest --no-trunc --format 'table {{.CreatedBy}}\t{{.Size}}'
# 対話的に全レイヤーの中身を探索(wagoodman/dive)
docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
wagoodman/dive:latest myapp:latest
# イメージをエクスポートして巨大ファイルを特定
docker save myapp:latest -o myapp.tar
tar -tvf myapp.tar | awk '{print $3, $6}' | sort -n | tail -30
# 間接的: コンテナ起動後にサイズの大きいファイルトップ 30 を探す
docker run --rm myapp:latest sh -c \
'find / -xdev -type f -printf "%s %p\n" 2>/dev/null | sort -n | tail -30'解決策
- ベースを
-slim/-alpine/distrolessに変える: 200〜800MB が 50〜150MB まで縮む。distrolessは最小だがデバッグ用シェルが無いので運用知識が必要 - マルチステージビルドを使う: ビルド用ステージとランタイムステージを分け、最終イメージにはビルド済み成果物だけをコピーする
FROM node:20 AS build WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM node:20-slim WORKDIR /app COPY --from=build /app/dist ./dist COPY --from=build /app/node_modules ./node_modules CMD ["node", "dist/server.js"] - apt のキャッシュを同じ RUN で削除する: 別 RUN だと削除されないので 同一レイヤー内 で消す
RUN apt-get update \ && apt-get install -y --no-install-recommends curl ca-certificates \ && rm -rf /var/lib/apt/lists/* pip install --no-cache-dir: Python のキャッシュを最初から作らない.dockerignoreを厳しくする:node_modules,.git,dist,coverage,*.log,.env*などは最低限除外diveで CI に回帰防止を入れる:--ci --highestUserWastedPercent 0.1で無駄なレイヤーがしきい値を超えたら CI を落とせる- **
docker build --squash(実験的)やbuildxの--output type=image,rewrite-timestamp=trueなどでレイヤー数を抑える選択肢もあるが、まずは上の王道から
