Dock Stay
レイヤーキャッシュを活かす - layer cache の使い方・オプション・サンプル

レイヤーキャッシュを活かす - layer cache

Dockerfile の命令順序と BuildKit のマウント機能を使い、再ビルドを最短化する。

概念図

layer cache diagram

構文

bash
# 依存定義を先に COPY
COPY package.json package-lock.json ./
RUN npm ci
COPY . .

# BuildKit のキャッシュマウント
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt

実例

依存定義を先に COPY してキャッシュを温存する

bash
# BAD: アプリ全体を先に COPY するとソース変更のたびに npm ci が走る
COPY . .
RUN npm ci

# GOOD: 依存マニフェストだけ先に COPY
COPY package.json package-lock.json ./
RUN npm ci
COPY . .

BuildKit のキャッシュマウントで pip のダウンロード結果を共有する

bash
# syntax=docker/dockerfile:1.7
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt
COPY . .

旧 Docker では環境変数で BuildKit を明示的に有効化する

bash
DOCKER_BUILDKIT=1 docker build -t myapp .

レイヤーキャッシュの仕組み

Docker は Dockerfile の各命令をレイヤーとして保存し、入力(命令文字列と参照するファイルのハッシュ)が同一ならキャッシュを再利用する。

ある行でキャッシュが外れると、それ以降の行はすべて再実行される。

つまり上流の行が頻繁に変わる構造はキャッシュの寿命を縮める。

COPY . . のように広い範囲を取り込む命令を早く置くと、ソースの 1 行変更で RUN npm ci のような重い工程まで再実行されてしまう。

変更の少ない行を上、多い行を下に並べるのが基本方針になる。

変更頻度の低い順に並べる

OS パッケージの導入、言語ランタイムの追加、依存マニフェスト(package.json / requirements.txt / go.mod)のコピーと依存インストール、アプリケーションソースのコピー、ビルド、という順に並べると、ソース変更時も依存インストールをスキップできる。

go mod downloadnpm ci のように時間のかかる命令の直前に、その入力だけを COPY するのがコツ。

グローバルな ENVARG は値が変わるだけで以降が丸ごと再実行されるので、頻繁に変わる変数は使う直前に配置する。

bash
FROM node:20-alpine
WORKDIR /app
# 1. 依存マニフェストだけ
COPY package.json package-lock.json ./
RUN npm ci
# 2. 残りをコピー
COPY . .
RUN npm run build
CMD ["node", "dist/server.js"]

BuildKit の `--mount=type=cache`

BuildKit の RUN --mount=type=cache はビルド間で永続化される書き込み可能ディレクトリを提供する。

/root/.cache/pip~/.npm/go/pkg/mod/root/.cache/go-build などのパッケージマネージャのキャッシュをここに逃がすと、初回以外のビルドでダウンロードが大幅に短縮される。

キャッシュはイメージには含まれないためサイズも増えない。

Docker Desktop や近年の Docker Engine はデフォルトで BuildKit を使うが、古い環境では DOCKER_BUILDKIT=1 を指定するか docker buildx build を使う。

# syntax=docker/dockerfile:1.7 などのシバン行で構文バージョンを明示することが必要。

bash
# syntax=docker/dockerfile:1.7
FROM golang:1.22 AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod \
    --mount=type=cache,target=/root/.cache/go-build \
    go mod download
COPY . .
RUN --mount=type=cache,target=/go/pkg/mod \
    --mount=type=cache,target=/root/.cache/go-build \
    go build -o /out/app ./cmd/app

関連トピック