GitHub Actions を高速化した3つの考え方
ある日 GitHub Actions が失敗していた。。
GitHub Actions の実行結果が表示されるページにThis job failed
と表示されていました。
Re-run jobs
してもジョブは失敗したまま。よく見ると、エラーメッセージが。
The job was not started because recent account payments have failed or your spending limit needs to be increased. Please check the 'Billing & plans' section in your settings.
GitHub Actions の無料枠を使い切っていました。とりあえず追加料金を払う設定をしてエラーは解決しました。
ですが、ちょうど最近 GitHub Actions の実行が終わるまで20分以上かかるようになっていて、待つのがしんどくなってきていた所だったので改善に取り組むことにしました。
結果、 21分かかっていた処理が、12分程度 まで短縮できました。この記事はその時の記録です。
GitHub Actions の構成
運用していた GitHub Actions の構成です。
- Nuxt3 をセットアップ(
npm ci
等) - ユニットテスト(Vitest)
- e2e テスト(Playwright)
- Dockerfile からイメージをビルド
- Cloud Run にアップロード
やったこと
- 使ってないライブラリを依存関係から削除
node_modules
をキャッシュ- job の並列実行
1. 使ってないライブラリを依存関係から削除
GitHub Actions では基本的に毎回依存関係を都度ダウンロードする必要があります。キャッシュを使っていたとしても初回は必ずダウンロードします。
通信の無駄なので、以前使っていて今は使っていないライブラリがpackage.json
に残っていたのでアンインストールしました。
npm uninstall [使っていないパッケージ名]
2. node_modules
をキャッシュする
次はnode_modules
のデータをキャッシュします。次回からキャッシュから復元することで、npm ci
するより早くnode_modules
を構成できます。
jobs:
setup:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: 1. node_modules をキャッシュする
id: cache-npm-node-modules
uses: actions/cache@v3
env:
cache-name: node-modules
with:
path: "node_modules"
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('package-lock.json') }}
- name: 2. キャッシュがなかったら npm ci を実行
if: steps.cache-npm-node-modules.outputs.cache-hit != 'true'
run: |
npm ci
ポイント 1. node_modules をキャッシュする
actions/cache
を使用してデータをキャッシュします。
id
: Step2で Step1の結果を取得env
cache-name
:key
内で使用
with
path
: キャッシュするディレクトリを指定key
: キャッシュした時のディレクトリ名
actions/cache
はkey
に合致するキャッシュがあったら、キャッシュから復元します。key
にhashFiles('package-lock.json')
を含めることで、package-lock.json
の変更、つまり依存関係の変更を検知します。
key
に合致するキャッシュがなかったらactions/cache
はキャッシュを復元しません。
ポイント 2. キャッシュがなかったら npm ci を実行
npm ci
はnode_modules
があったとしても依存関係を再ダウンロードします。なので、実行時間を短縮するためにはキャッシュがあった場合はnpm ci
を実行したくありません。
node_modules
があった場合はnpm ci
を実行しないnode_modules
がなかった場合はnpm ci
を実行する
という挙動をif
を使って作っています。
そうすることで初回実行時や、依存関係に変更があった時だけnpm ci
を実行しています。
5. job の並列実行
元々、ユニットテストと、e2e テストを直列で実行していましたが、並列で実行するように変更しました。
jobs:
setup:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# 依存関係のインストール等
# 依存関係をキャッシュ
test-unit:
needs: [setup]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# 依存関係を復元
# ユニットテストを実行
test-e2e:
needs: [setup]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# 依存関係を復元
# e2e テストを時効
deploy:
needs: [setup, test-e2e, test-unit]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# 依存関係を復元
# ビルドしてデプロイ
ポイント
needs
でジョブが実行されるまでに完了している必要があるジョブを定義しています。例えばdeploy
ジョブはsetup
、test-e2e
、test-unit
が正常に終わっていないと実行されません。
また、各ジョブではインストールしたnode_modules
などは引き継げないので、キャッシュして復元する必要があります。
さいごに
全部の対策を行なった結果、 21分かかっていた処理が12分程度まで短縮できました!