Dockerのキャッシュを学ぶ
Date2026/08/25 Last Modified2026/08/25
概要
Dockerの環境をAIでとりあえず作成してもらって、その通りに作業していたらスタイルがうまく反映されないという事態になった。
スーパーリロードしたら反映されるけど、また通常リロードすると戻ってしまうという現象。
原因を調べてみたところ、Dockerの構成やキャッシュなどの問題であるということはわかったが、具体的に何をどうしたら直るのかはよくわからなかった。
これは自分がちゃんと理解して、直し方を提示しないことにはきれいな形にならないな……ということで、原因を追究してみることにした。
現象とAIの調査結果をまとめる
うーん、これを読んでいる現時点ではさっぱりである。コンテナ、ホスト、Volumeというのがそもそも、ぼんやりとしか理解してないのもある。
ひとつずつ紐解いていこうと思う。
間違った仮説を反証してもらおう
わからないことがあるときは、自分で断定的な理論を作って誤りを指摘してもらうのがいい。
「コンテナはホストのディレクトリをマウントしているはず」というのは、一般的な性質ではないよ!
なるほど。Dockerは必ずディレクトリをマウントしていると思っていたけれど、バインドを明示しない限りはホストのファイルと同期していないんだ。
そうなると、それまでの設定っていったいどこにファイルを保持していたのか?
「仮想的なファイルシステムで保持」とはいうものの、その"仮想"っていうのは単なるコピーなのか、実体が無いのかで言うとどっちなのだろう。
実体のあるファイルだよ!でも、単純にエクスプローラーでパスを辿っていける場所ではなく、コンテナのファイルシステム内にあるんだ。
ファイルシステム、というのがよくわからなかったので調べたところ「ファイルやフォルダが存在する、ある保存空間・世界」と、ざっくり説明された。
つまるところ、ホスト(ローカルPC)が持っている世界とは別に作られた世界がDockerの中にあり、その中のファイルを参照しているということだと思う。
なので、Docker DesktopはこのPCに入っているから、当然このPCの世界の中にDockerがあるわけだけども、そのDockerの中身はまた別世界として扱う……というマトリョーシカみたいな構造なんだと思う……多分。
それでいうと、ちょっと合点がいく部分があって、以前Dockerについて調べたとき「Docker Engineさえ入れれば、アプリケーション環境が作れる」と知った。
これはつまり、bindする方式だったら/var/www/にアプリが無いといけないから、そこで矛盾する。
なので、Dockerの中にファイルシステムが構築される(/var/wwwの部分も含めて)というのは、ある程度納得ができるともいえる。
VolumeってMySQLみたいなものだと思ってたけど違うの?
次はVolumeという概念がよくわからない。データを格納することはわかるけれど、具体的に何のデータなのだろうか。
“Volumeに古いキャッシュが残っていた"というのは、いったいどういう現象なのだろう。今回はビルドキャッシュだし、ファイルのはずなのに。
VolumeはDockerコンテナのデータを永続化するための保存場所
永続化、というとピンとこなかったけれど、紐解いてみたらはっきりわかった。
compose.yamlの中でこんな記述がある。
これはまず、.:/var/www/htmlによってソースコード全体をホストからコンテナへbind mountしている。
つまり、これだけなら全てのソースコードがホストをマウントしていることになる。
けれど、そうすると不都合なこともある。
例えば、node_modulesというのはnpm installする環境によって差異が出るものである。
ホストではv20だけどコンテナはv24とか。
Docker内と環境が違うとなると、そのままバインドしたものを持ってくるとバージョンが合わなくなったりする。
そこで登場するのがVolumeである。
Volumeにコンテナ専用のソースコードを予め用意しておくことで、マウントしたときにかぶせるような形でnode_modulesのファイルを置き換えてしまえばよいというわけだ。
コンテナは再構成するたびにキャッシュ等は失われてしまう。なのでVolumeで永続化をすることによって解決する。
ということで「Volumeに古いキャッシュが入っていたのでスタイルが反映されなかった」という問題を振り返ってみると、
このようなビルド成果物が入る場所を永続化の対象とした上で更新していなかった場合、いくらホスト側でビルドファイルをマウントさせたところで、Volumeによって上から被せて古い状態に戻ってしまう。
だから正確には「本来は永続化すべきでないビルド成果物をVolumeに置いてしまい、それをマウントしていたことによって新しい成果物か隠されてしまった」ということになる。
マウントってどのタイミング?
この問題を整理して行くうえで一つ疑問がさらに浮上。
「コンテナのファイルってどの段階で生成されるんだろう?」
わざわざbind mountで消えてしまうファイルも置くんだろうか。マウントされたものは消えてしまうんだろうか。
Dockerが構築されるまでには2段階ある
Dockerを作る段階は「Dockerイメージを作る段階」と「コンテナを起動する段階」がある。
流れとしてはこのようになる。
ここで重要なのが、imageをビルドする段階で完成済みのファイルシステムができるということ。
つまり、このimageというものの中にPHPなどのファイルが入っているということになる。
これを起動するのがcompose upコマンドであり、ここでマウントの指定があれば上から被せる形で取り込む。
そして、これらのファイルの準備が終わってからようやくnpm ciなどのコマンドを実行する。
これでようやく動く形になる。
mount前提ならソースを生成しない可能性もあるよ
これはDockerfileの定義によるもので、仮にCOPYをしているのであればマウントに拘わらずImageにも存在することになる。
ホストのソースを信頼することによって、Imageを軽くしていると言える。
しかし、本番環境ではマウント前提であってもImageに焼き込むことが多いという。
本番とローカルで環境を変える理由とは
ここまで来ると、当初の疑問であった「ローカルと本番でcompose.yamlが違うのはなぜ」という点もほとんど解消したようなもの。
いうなれば、力関係が逆転しているということになるだろうか。
ローカルではローカル用の実行環境とDocker用の実行環境が2つ存在しており、主にローカル用の実行環境に依存して開発など行うからして、ローカルのホストをマウントして色々と構成していくほうが多い。
しかし本番に近づくにつれて、永続化やキャッシュ保持など、あらゆるものがDocker一つに集約されていきホストを参照することが減ってくる。
このグラデーションが本番とローカルの差になってくるのだと思う。
ひとまずここまででDockerの学びをひと段落させて、実際に手を動かしてみたくなってきた。