CIにおける依存関係キャッシュとは?
依存関係キャッシュは、buildがダウンロードするパッケージを保存し、次の実行でregistryからすべてを再取得する代わりに再利用できるようにします。
ほとんどのCIジョブは依存関係のインストールから始まります - npmパッケージ、Pythonのwheel、Mavenのjarなどです。これを毎回ゼロから行うのは遅く無駄です。依存関係はコミット間でめったに変わらないからです。依存関係キャッシュは、1回の実行後にそれらを保存し、次の実行の開始時に復元します。
解決する問題
新しいCI runnerはパッケージディレクトリが空です。キャッシュがないと、各ジョブは同じ依存関係を再ダウンロード(そして多くの場合は再コンパイル)し、各実行に数分とregistryへの負荷が加わります。忙しいリポジトリでは、このコストが1日に数百回繰り返されます。
仕組み
- インストール後、パッケージディレクトリ(例えば
~/.npmやnode_modules)がkeyの下でキャッシュストレージにアップロードされます。 - 次の実行では、ジョブがそのkeyを検索し、インストール前にディレクトリを復元します。
- keyが一致すれば、インストールはほぼ瞬時の"already satisfied"パスになります。
cache keyがすべて
良いkeyはlockfileのハッシュから導出されるため、依存関係が変わったときだけ変化します。例: node-${{ hashFiles('package-lock.json') }}。keyが広すぎると古いパッケージを提供し、狭すぎると決してヒットしません。
小さな例
GitHub Actionsでは、key: npm-${{ hashFiles('package-lock.json') }}とnpm-のrestore-keyで~/.npmをキャッシュできます。lockfileが変わらなければ正確なkeyがヒットし、変わった場合はフォールバックが最も近い過去のキャッシュを復元するので、差分だけがダウンロードされます。
何をキャッシュすべきか(そして何をしないか)
パッケージストアやインストール済みモジュールをキャッシュし、ソースやbuildの出力(これらは独自のキャッシュを持ちます)はキャッシュしないでください。secretやマシン固有のものはキャッシュを避け、インストール手順が安価でもダウンロードが遅い場合は、インストール済みバイナリよりダウンロードキャッシュを優先しましょう。
Latchkeyについてのメモ
Latchkeyのマネージドrunnerでは、依存関係キャッシュは公開インターネットではなく同一リージョンの高速なローカルパス経由で復元されるため、大きなパッケージツリーでもキャッシュヒットが数秒で完了します。
重要なポイント
- 依存関係キャッシュは、CI実行間でダウンロード済みパッケージを再利用します。
- cache keyは正しく無効化されるよう、lockfileから導出すべきです。
- パッケージストアをキャッシュし、secretsやソースは決してキャッシュしないこと。