Dockerを再buildしても古いコードが動くとき、Build Cache・Volume・Containerを切り分ける

Dockerの古いコード実行を、build context、image、container、mount、実行processの順に追い、削除に頼らず原因を絞ります。

AIものさし編集部

Dockerで再buildしたのに古いコードが動くとき、すぐにcacheやvolumeを全削除せず、ソースから実行processまでの経路を確認します。新しいimageを作ったこと、そのimageからcontainerが起動したこと、mount先にあるファイルをprocessが読んだことは、それぞれ別の確認です。

この記事はDocker Composeを使う開発・検証構成を中心に、codeの供給経路を切り分ける手順です。以下のpathや識別子は架空例で、Docker buildやcontainerの作成・再起動・volume操作は実行していません。

どの段階で古くなったかを並べる

最初にCompose project、対象service、実行コマンド、build context、Dockerfile、imageの識別、稼働containerを固定します。同じtag名でも中身は変わり得るため、tagが合うだけで同じimageと判断しません。別projectや別Docker contextを見ていないかも確認します。

表は横にスクロールできます

段階調べるもの成功だけでは分からないこと
編集元変更した実ファイルbuild contextに含まれるか
build入力context・COPY・ignore条件最終imageへ入るか
imagebuild結果と最終stage稼働containerが使うか
container実際のimage IDとmountprocessがそのpathを読むか
process・応答entrypoint・読込path・版表示別replicaの応答が混ざらないか

公開してよいbuild識別子や検証用マーカーを使い、各段階で同じ版を追います。実ファイルに秘密や利用者情報が含まれる場合、全文をログやチケットへ出さず、必要なpath・hash・版情報だけにします。

build contextと最終stageを確認する

編集したファイルがDockerfileのCOPY元にあるか、.dockerignore等で除外されていないか、multi-stage buildの最終stageへ渡るかを確認します。別ディレクトリをcontextにしていれば、期待したファイルを編集してもそのbuildの入力にはなりません。

Dockerのcache invalidation資料(新しいタブで開く)では、COPYやADDなどは対象ファイルのmetadataを使ってcache判定し、mtimeだけの変化は判定に使わないとしています。更新時刻を触るだけの操作を、正しいcodeが入力された証拠にはしません。

表は横にスクロールできます

観測最初に見る場所修正候補の方向
buildログに対象COPYがないDockerfile・stage必要ファイルの供給経路
image内のファイルが古いcontext・ignore・COPY元build入力の取り違え
別stageにだけ新codeがある最終stageへのCOPY最終成果物の構成
外部取得内容だけ古いRUNとcache条件必要な層の再取得方針

--no-cacheはbuild cacheを使わない指定で、誤ったcontextやmount、稼働containerの選択まで直すものではありません。base imageの取得を試みる--pullとも意味が違います。実行時間や取得量を増やす前に、どの層を確認するための操作かを決めます。

imageのbuildとcontainerの更新を分ける

docker compose build(新しいタブで開く)はserviceのimageを作る操作です。docker compose up(新しいタブで開く)は、変更されたimageや設定を使ってcontainerを作成・再作成する動作を説明しています。buildだけで、すでに動いているprocessが新しいファイルへ切り替わったとは扱いません。

--no-recreateなどの指定、対象service、profile、projectが更新を妨げていないかを調べます。必要な再作成を行う場合も、対象と永続データへの影響を確認してから選びます。全serviceの停止・削除を最初の診断手順にしません。

mountがimage内のcodeを隠していないかを見る

Docker公式資料は、既存のファイルがあるpathへbind mountや空でないvolumeをmountすると、元の内容が隠れると説明しています。新imageの/appが更新済みでも、実行時に別の/appをmountすれば、processが読むのはmount側の内容かもしれません。

表は横にスクロールできます

保存先の種類確認すること誤った対処
bind mountdaemon側のsource pathと実内容image再buildだけを繰り返す
named volumemount先と保存内容の用途古いcode対策でDBごと削除
container writable layer実行中に変更されたファイル手編集を再現可能なbuildとみなす
imageのファイルmountされない状態での版稼働中の読込内容と同一と決める

volumeはcontainerと別の寿命を持つため、containerを再作成しても保存内容が残ります。codeを永続volumeへ置く設計なら、どの処理がいつ内容を更新するかを確認します。DBや利用者uploadとcodeの保存先が混在している場合、削除で解決しようとせず責務を分けます。

processと応答の供給元まで追う

container内の目的ファイルが新しくても、processが別pathや生成済みbundleを読んでいれば応答は変わりません。entrypoint、working directory、実行引数、workerの再読込、アプリ固有のcacheを確認します。複数replicaや別port、proxyがあるなら、どのcontainerの応答を見ているかも記録します。

架空例では、sourceのマーカーB、最終imageのマーカーB、稼働containerのimage ID一致、mount側のマーカーAという結果なら、次はmountの供給元を調べます。cacheを全消去する必要があるという結論には進みません。ファイルがBなのに応答がAなら、processや配信経路へ切り分けを進めます。

診断の完了条件を残す

変更した箇所、buildの識別、稼働container、mount source/destination、processの読込先、観測した応答を対応付けます。「再build成功」だけで終わらず、次の通常起動でも同じ版を再現できるか確認します。

診断のためのvolume prune、永続volume削除、全cache削除を定型手順にしません。原因が分かれば、正しいcontextを指定する、最終stageへ必要ファイルを渡す、対象containerを更新する、mountの供給元を直すなど、影響範囲を絞った変更を選べます。

確認した公式資料