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へ入るか |
| image | build結果と最終stage | 稼働containerが使うか |
| container | 実際のimage IDとmount | processがその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 mount | daemon側のsource pathと実内容 | image再buildだけを繰り返す |
| named volume | mount先と保存内容の用途 | 古い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の供給元を直すなど、影響範囲を絞った変更を選べます。
