Skip to content
Latchkey

Dockerレイヤーcacheの解説: CIでのより速いイメージbuild

Dockerはイメージをcacheされたレイヤーの積み重ねとしてbuildする。めったに変わらない部分が先に来るようDockerfileを並べれば、ほとんどのbuildがほぼすべてを再利用する。

Dockerfileの各命令はレイヤーを生成する。入力が変わっていなければ、Dockerは前のbuildのレイヤーを再利用できる - だが1つの早期の変更が、その後のすべてを無効化する。並べ順がすべてだ。

レイヤーがどうcacheされるか

Dockerは各命令とその入力をhashする。hashがcacheされたレイヤーに一致すれば、stepを再実行せずにそれを再利用する。あるレイヤーの入力が変わった瞬間、そのレイヤーとその下のすべてのレイヤーが再buildされる - cacheは選択的にではなく、下方向に無効化される。

cache hitのために並べる

安定した命令を先に、変わりやすいものを最後に置く。依存関係のマニフェストをcopyし、アプリケーションのsourceをcopyする*前*に依存関係をインストールして、コード変更が依存関係インストールのレイヤーを壊さないようにする。

Cache-friendly ordering
COPY package*.json ./
RUN npm ci          # cached unless deps change
COPY . .            # changes often; only this layer rebuilds
RUN npm run build

CIがcacheをよく外す理由

  • エフェメラルrunnerは、実行ごとにローカルのイメージcacheなしで始まる。
  • 外部cacheバックエンドが設定されておらず、何も復元されない。
  • depsをインストールする前にsourceをcopyし、commitごとにdepsを無効化する。
  • ファイル上部近くの変わるbuild argumentがすべてを壊す。

CI実行をまたぐcache

エフェメラルrunnerにはローカルcacheがないため、外部cacheが必要だ: BuildKitのregistryバックのcache(--cache-from / --cache-to)はレイヤーをregistryに保存し、次の実行がそれらを復元する。それなしでは、あらゆるCIのbuildは事実上cold buildだ。

重要なポイント

  • 各Dockerfile命令はレイヤー; 1つの変更がその下のすべてを無効化する。
  • 安定したstepを先に、変わりやすいstep(sourceのcopy)を最後に並べる。
  • 確実なhitのために、アプリのsourceをcopyする前に依存関係をインストールする。
  • エフェメラルCIは、そもそもhitするために外部(registryバックの)cacheが必要だ。

関連ガイド