Docker build でキャッシュが効いて変更が反映されない
Docker build はレイヤーごとにキャッシュを再利用するため、COPY 元のファイル変更を検知させないと古いレイヤーが使われる。
--no-cache で全レイヤー無効化、--build-arg にビルド日時を渡す、COPY 順を依存とソースで分ける、で対処する。
#build#cache#dockerfile#buildkit
公開:
要約
Docker build は各命令を「レイヤー」として保存し、入力(命令文 + 参照ファイル)が一致する限り再利用する。
変更を反映したいのに古いレイヤーが使われる場合は、--no-cache で全無効化するか、--no-cache-filter <stage> で対象 stage だけやり直す。docker build --build-arg CACHEBUST=$(date +%s) で特定の ARG 以降を強制再実行する手法も常套。
よくある原因
.dockerignoreで対象ファイルを除外しており、変更があってもビルドコンテキストに入らないため、Docker からは「入力が変わっていない」と見える。RUN apt-get install ...のような命令は引数が同じ限りキャッシュされる。
外側のミラーでaptリポジトリが更新されていても、Docker はキャッシュを使い続ける。COPY . .を依存インストールより前に書いてしまうと、ソースを 1 行直すたびに後段のnpm installまで毎回やり直しになる。
逆に依存ファイルだけ更新したつもりが、依存レイヤーの後ろの工程まで再評価されない。- BuildKit の
--mount=type=cache,target=/root/.cacheを別ステージ間で共有していて、片方の更新が反映されないように見える。
解決策
1. 全レイヤーを無効化する
docker build --no-cache -t myimg .確実だが当然遅い。
CI や検証ビルドのみで使うのが無難。
2. 特定 stage だけ無効化する(Docker 23+)
docker build --no-cache-filter builder -t myimg .AS builder と書いた stage だけキャッシュを捨て、他は再利用する。
詳細は 公式ドキュメント (Cache invalidation)。
3. 依存とソースで COPY を分ける
FROM node:20
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run buildpackage*.json を先に COPY して install を済ませてからソースをコピーすれば、ソースだけ変えたときは npm ci のレイヤーが再利用される。
4. ARG でキャッシュを意図的に壊す
ARG CACHEBUST=1
RUN curl -fsSL https://example.com/install.sh | shdocker build --build-arg CACHEBUST=$(date +%s) -t myimg .ARG 行の値が変わるとそれ以降の RUN がキャッシュミスになるので、外部 URL から毎回最新を取りたい場面で便利。