GitHub Actionsのキャッシュを極限まで使い倒せ:ビルド時間を50%削る「真の最適化」戦略
「CIが遅い」――。この言葉は開発チームの死刑宣告だ。
GitHub Actionsの実行時間が10分を超えると、開発者は集中力を切らし、コンテキストスイッチが発生する。そして「プッシュした後の待ち時間」が、開発者のフロー体験を破壊する。
本稿では、GitHub Actionsの`actions/cache`を単なる「ディレクトリを保存する場所」としてではなく、「ビルドパイプラインの心臓部」として捉え、キャッシュヒット率を極限まで高めるための実戦的なハックを伝授する。
—
1. `actions/cache`の「本当の」仕組みを知る
多くのエンジニアは「ファイルを保存して読み込むだけ」だと考えている。だが、実際には以下の3つのフェーズでキャッシュは動いている。
1. Restore: `key`と`restore-keys`に基づき、最も適したキャッシュを検索・ダウンロードする。
2. Execute: ジョブが走る。
3. Save: ジョブ終了時にキャッシュを作成・アップロードする。
ここで最も重要なのは、「キャッシュの保存はジョブの最後に行われる」ということだ。つまり、ジョブが失敗したりキャンセルされたりすると、そのセッションで生成された新しいキャッシュは保存されない。この制約を理解した上で、戦略を組む必要がある。
—
2. パッケージマネージャー別:キャッシュ戦略の極意
npm / yarn (Node.js)
`node_modules`を丸ごとキャッシュするのは避けるべきだ。依存関係の解像度が低いと、整合性が取れなくなるリスクがある。「キャッシュすべきは依存パッケージのインストール済みディレクトリではなく、マネージャーのキャッシュディレクトリだ」。
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ’20’
# setup-nodeは内部でキャッシュを自動管理してくれる。
# しかし、大規模プロジェクトでは明示的に指定すべき場合がある。
cache: ‘npm’
cache-dependency-path: ‘/package-lock.json’
pip (Python)
`pip`の場合は、`PIP_CACHE_DIR`を環境変数で指定し、GitHub Actionsのキャッシュパスと同期させるのが定石だ。
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
# ロックファイルか、requirements.txtのハッシュをキーにするのが鉄則
key: ${{ runner.os }}-pip-${{ hashFiles(‘/requirements.txt’) }}
restore-keys: |
${{ runner.os }}-pip-
—
3. キャッシュヒット率を劇的に上げる「3つの黄金律」
① `restore-keys`を軽視するな
`key`が完全一致しなかった場合でも、`restore-keys`を使えば部分一致する最新のキャッシュを引っ張ってこれる。
key: ${{ runner.os }}-node-${{ hashFiles(‘package-lock.json’) }}
restore-keys: |
${{ runner.os }}-node-
これにより、`package-lock.json`にわずかな変更があったとしても、完全にゼロからのビルドを回避できる。
② ディレクトリ構造の「絞り込み」
キャッシュ対象のパス(`path`)は、必要最小限に絞れ。ビルド成果物(`dist/`や`.next/`)をキャッシュに含めるな。これらは`actions/upload-artifact`の担当領域だ。キャッシュは「依存関係」と「コンパイル済みオブジェクト」に特化させろ。
③ 巨大なキャッシュを分割せよ
単一の巨大キャッシュはアップロード・ダウンロード時間を増大させる。複数の`actions/cache`ステップを定義し、依存関係、ビルド中間体、テストデータを分離することで、並列処理の恩恵を最大化できる。
—
4. プロのテックリードが教える「隠れた神テクニック」
開発効率を上げる VSCodeプラグイン: `GitHub Actions`
[GitHub Actions Extension for VSCode](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-github-actions)は必須だ。ローカルでワークフローのログを直接参照でき、ビルド失敗時のデバッグが段違いに早くなる。
チーム開発における設定の共有化ルール
設定ファイル(YAML)は、「Reusable Workflows」に分離しろ。
プロジェクトごとにキャッシュ設定をコピペするのは悪手だ。CIのベストプラクティスを共通リポジトリに集約し、各プロジェクトからは以下のように呼び出すのが正解だ。
.github/workflows/ci.yml
jobs:
build:
uses: my-org/ci-templates/.github/workflows/npm-build.yml@main
キャッシュの「掃除」を自動化する
キャッシュは無限に増えるわけではない。7日間アクセスがないキャッシュは自動削除されるが、開発が激しいプロジェクトでは、特定のブランチをマージした際にキャッシュをクリアする運用を検討せよ。
—
最後に:なぜ「爆速」が重要なのか
CIのスピードは、開発者の「思考の深さ」に直結する。
ビルドが終わるまで数分待つ間、エンジニアは他のタブを開き、Slackを見、思考が中断される。この「5分」が1日10回繰り返されれば、それは致命的な損失だ。
キャッシュを最適化し、CIを「待機時間」から「フィードバックループ」へと昇華させること。それが、DevOpsスペシャリストとしてのあなたの責務だ。
さあ、今すぐYAMLを開き、`actions/cache`のキー戦略を見直してほしい。その50%の短縮が、チームの未来を変えるはずだ。