Dockerのレイヤーとキャッシュ — 変えた行から下だけ作り直す
Dockerイメージは、1枚の塊ではありません。Dockerfileの命令を上から1つ実行するごとに薄い「レイヤー」が積み重なってできています。そして各レイヤーにはキャッシュが効きます――前回と内容が同じなら、作り直さず使い回すのです。
この仕組みは、「どの命令を上に書くか」でビルド速度が劇的に変わるという設計のコツに直結します。下の図1で何度かビルドし、ファイルを変更してみてください。変更した行より下のレイヤーだけが作り直される様子が見えます。
npm install は時間がかかります。一度ビルドした後、もう一度ビルドすると何が起きるか試してください。
キャッシュは「上から順に」効く
Dockerは各レイヤーに「この命令+それまでの結果」から決まる鍵を付けます。ビルド時は上から順に、鍵が前回と一致すればキャッシュを再利用(CACHED)します。ところが、あるレイヤーが変わると、その鍵が変わり、それ以降のレイヤーはすべてキャッシュが無効になります。下のレイヤーは上のレイヤーの上に積まれているので、土台が変われば積み直しになるからです。
だから図1で「アプリのコードを変更」すると、最後の COPY . . 以降だけが作り直され、重い npm install はキャッシュのまま一瞬で済みます。ところが「package.json を変更」すると、COPY package.json 以降がすべて無効になり、npm install までやり直しに。これが「変更頻度の低い命令ほど上に書く」という鉄則の理由です。依存のインストールを、ソースのコピーより上に置くだけで、毎回のビルドが何十秒も速くなります。
- レイヤー
- Dockerfileの1命令でできる差分の層。イメージはこの層の積み重ねでできている。
- ビルドキャッシュ
- 前回と同じ内容のレイヤーを作り直さず再利用する仕組み。上から順に判定される。
- キャッシュ無効化
- あるレイヤーが変わると、その下(以降)のレイヤーが全部作り直しになること。
- レイヤー共有
- 同じレイヤーは複数イメージで共有され、保存も転送も1回で済む。
まとめ
Dockerイメージはレイヤーの積み重ねで、各レイヤーにはキャッシュが効きます。ポイントは「変わった行より下は全部作り直し」という一方向のルール。だから変わりにくいもの(依存のインストール)を上に、変わりやすいもの(ソースコード)を下に置くと、キャッシュが最大限効いてビルドが速くなります。図1で順序を意識しながらビルドすれば、Dockerfileを書く手が変わるはずです。