こんにちは。現場の最前線でコードを書き、パイプラインの待ち時間にコーヒーを淹れる暇も惜しんでいる皆さん。
CI/CDのパイプラインが「遅い」というのは、エンジニアにとって最大のストレスですよね。ビルドが完了するのを眺めている時間は、生産性がゼロになるだけでなく、開発者の集中力を削ぐ最大の要因です。
今回は、GitHub Actionsの標準機能である`actions/cache`を使い倒し、「ビルド時間を物理的に半分以下にする」ための極限のキャッシュ戦略を伝授します。単なるマニュアルの焼き直しではありません。現場で明日からすぐに使える、実践的な最適化の技術です。
—
1. なぜ「キャッシュ」がビルドを劇的に速くするのか?
GitHub Actionsは、ジョブが走るたびに「クリーンな仮想環境」を一から構築します。つまり、`npm install`や`pip install`で毎回インターネットの海から数千個のライブラリをダウンロードしているのです。
キャッシュの役割は、「過去の遺産を再利用すること」です。
特定のディレクトリ(`node_modules`や`~/.cache/pip`など)を保存し、次回以降の実行時に「ダウンロード済み」の状態からスタートさせる。これだけで、ネットワーク通信のオーバーヘッドをほぼゼロにできます。
—
2. まずはここから!キャッシュ設定の「黄金律」
`actions/cache`を使う際の最も重要なポイントは、「何をキーにするか(Cache Key)」です。
- name: Cache dependencies
uses: actions/cache@v3
with:
# キャッシュ対象のディレクトリ
path: ~/.npm
# キーが同じならキャッシュがヒットする
key: ${{ runner.os }}-node-${{ hashFiles(‘/package-lock.json’) }}
# キーが不一致の際、過去のキャッシュから部分一致で復元する(重要!)
restore-keys: |
${{ runner.os }}-node-
なぜこの設定が「最強」なのか?
- `hashFiles`の魔法: `package-lock.json`(または`yarn.lock`)の中身が変わった時だけ、キャッシュを無効化します。これにより、ライブラリの追加・更新時だけ再インストールを行い、それ以外は爆速でパスします。
- `restore-keys`の活用: 仮にロックファイルが変更されても、直前のキャッシュをベースにすれば差分更新だけで済みます。ゼロからやり直す必要はありません。
—
3. 言語別・現場で愛用される最適化テンプレート
それぞれの言語環境で、「どこをキャッシュすべきか」を迷う必要はありません。以下の設定が、最もヒット率が高く安定する「定番」です。
Node.js (npm/yarn)
- name: Cache node_modules
uses: actions/cache@v3
with:
path: ~/.npm # npmのキャッシュディレクトリを指定
key: ${{ runner.os }}-node-${{ hashFiles(‘/package-lock.json’) }}
Python (pip)
- name: Cache pip
uses: actions/cache@v3
with:
path: ~/.cache/pip # pipのデフォルトキャッシュ場所
key: ${{ runner.os }}-pip-${{ hashFiles(‘/requirements.txt’) }}
—
4. キャッシュヒット率を上げるための「極意」
キャッシュを導入しても、「なぜかヒットしない」という事態に陥ることがあります。以下の3点を守るだけで、ヒット率は劇的に向上します。
1. ディレクトリを絞り込む: 大量にゴミが含まれるプロジェクトルート全体をキャッシュしてはいけません。必ずパッケージマネージャが利用する「専用のキャッシュディレクトリ」をピンポイントで指定してください。
2. キーをシンプルに保つ: OS名、パッケージマネージャの種類、ロックファイルのハッシュ値。この3つで十分です。キーに複雑な情報を詰め込むと、わずかな環境差でヒットしなくなります。
3. CIの実行フローを統一する: プロジェクト内で複数のWorkflowがある場合、`path`や`key`の形式を揃えておくと、プロジェクト全体でのキャッシュの再利用性が高まります。
—
5. HelloWorld的実装:まずはここから試そう
まずは、あなたのリポジトリの `.github/workflows/main.yml` に以下のステップを追加してみてください。
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ’18’
# ※実は setup-node はキャッシュ機能を内蔵しています!
cache: ‘npm’
- name: Install dependencies
run: npm ci # –frozen-lockfile と同義。CIでは必ずciコマンドを!
先輩エンジニアからのアドバイス:
実は、`actions/setup-node`や`actions/setup-python`などの公式ツールには、すでに`cache: ‘npm’`のようなオプションが用意されています。まずはこれを利用し、より複雑なキャッシュ制御が必要になった段階で`actions/cache`を直接触るのが、最も効率的かつ安全な道です。
—
最後に
CIの待ち時間は、あなたの「思考のコンテキスト」を破壊します。キャッシュを最適化し、ビルドを爆速にすることは、単なる効率化ではなく、あなた自身の集中力を守り、最高のプロダクトを作るための環境投資です。
今日からこの設定を取り入れて、ビルド完了の通知が来る前に「お茶を淹れる時間」を削り、その分、少しだけコードの品質を高めることに時間を使ってみてください。
何か詰まったら、いつでも聞いてくださいね。一緒に最強のパイプラインを作り上げましょう!