🎧 Listen to this article: हिंदी · English · தமிழ் · తెలుగు · ಕನ್ನಡ · മലയാളം · ଓଡ଼ିଆ · 日本語 · 中文
🌍 Read this in your language: हिंदी · தமிழ் · తెలుగు · ಕನ್ನಡ · മലയാളം · ଓଡ଼ିଆ · 日本語 · 中文
誰も気づかない問題
ワークフローが失敗するとGitHubからメールが届きます。しかし、CIに本来の2倍の時間がかかっている場合はどうでしょうか?何も通知されません。すべてのプルリクエストやコミットで低速なビルドが走り、同じ依存関係を何度もコンパイルしているのは、目に見えているのに見過ごされている無駄です。
2026年6月30日現在、CIのパフォーマンスはかつてないほど重要になっています。開発チームは大規模化し、デプロイは高速化しており、フィードバックループの遅れによるコストはチーム全体に蓄積していきます。CIが5分ではなく15分かかり、チームが週に40件のPRを作成する場合、マシンを待つためにチーム全体で週に8時間を費やしていることになります。これは丸1人分のアイドル時間に相当します。しかし、ほとんどのチームはこの問題を警告するダッシュボードを目にすることがありません。
なぜ起こるのか
GitHub Actionsはセットアップが簡単なため、幅広く普及しています。しかしそのシンプルさゆえに、適切なデフォルト設定の陰にパフォーマンスの急落が隠れてしまいがちです。ワークフローを作成して正常に動作すれば、何か大きなエラーが発生しない限り、意識しなくなります。その間にも、ワークフローは不要な処理を行っている可能性があります。毎回パッケージを再インストールしたり、巨大なキャッシュアーティファクトをアップロードしたり、ワークフローの行列(matrix)のタイポによってテストを2回実行したり、起動に非常に時間がかかる古いイメージを使用したりしているかもしれません。
よくある原因の特定と修正は簡単です。順番に見ていきましょう。
よくある原因
依存関係を毎回再構築している
最も発生しやすいミスの1つは、依存関係をキャッシュしないことです。ワークフローが実行されるたびに同じnpmパッケージ、Goモジュール、またはPythonライブラリをインストールしている場合、まったく同じファイルに対して、ネットワーク通信、解凍、検証のコストを日に何度も支払っていることになります。
ほとんどの言語にはキャッシュ戦略が組み込まれています。Node.jsの場合、GitHub Actionsは以下を保存するキャッシュアクションを提供しています node_modules または実行間でロックファイルを保存します。Pythonの場合はpipのwheelをキャッシュできます。JavaにはMavenやGradleのキャッシュがあります。これらを使用していない場合、CIは冗長な処理を行っています。
低速または肥大化したイメージ
GitHub Actionsのワークフローはコンテナ内で実行されます。選択するイメージによって、起動にかかる時間のベースラインが決まります。古くて肥大化したベースイメージ(不要なビルドツールが含まれる完全なUbuntuなど)を使用すると、一回の実行ごとに数分の時間が失われます。
可能な場合は最小限のイメージに切り替えてください。特定のツールが必要な場合は、カスタムDockerイメージを一度ビルドし、GitHub Container Registryなどのレジストリにプッシュして再利用します。最初の実行ではイメージのビルドに時間がかかりますが、それ以降の実行では即座にプルされます。
誤ってジョブを2回実行している
ワークフローのmatrix機能は強力ですが、設定ミスが起こりやすい部分です。よくある誤りは、異なるNodeバージョンやOS用にmatrixをセットアップしたものの、実際には1回しか実行するつもりがなかったことに後から気づくパターンです。重複した実行は、CPU、ストレージ、および時間を無駄に消費します。
ワークフローのYAMLを確認してください。不必要に処理を重複させているmatrixがある場合は、それをフラット化するか、特定のブランチでのみ実行する条件を追加してください。
大容量または不要なアーティファクト
ワークフローが実行ごとに巨大なビルドアーティファクトやログをアップロードしている場合、GitHubはその保存と圧縮に時間を費やします。後続の処理でそれらのアーティファクトを実際に使用していない場合、それは完全に無駄です。
アップロードするものを明確にしてください。ビルドディレクトリ全体が必要ですか、それとも最終的なバイナリだけで十分ですか?成功した実行のログが必要ですか、それとも失敗時のみで十分ですか?古いアーティファクトが溜まらないように保持ポリシーを設定してください。
並列実行可能なステップの直列実行
ワークフローがlint、テスト、ビルドをすべて順番に実行している場合、一定時間のうち単一のCPUコアしか使用していません。GitHub Actionsはデフォルトで複数のジョブを並列実行できます。独立したステップがある場合は、それらを別々のジョブに分割してください。
簡単な例として、lintingは高速であり、完全なビルドを必要としません。独立したジョブとして実行してください。失敗した場合は、テストを待つことなくすぐに結果が分かります。通過した場合は、テストとビルドが同時に実行されます。
遅さを計測する方法
最適化を行う前に、ベースラインが必要です。GitHubはActionsタブでワークフローのタイムラインを提供しています。最近の実行を開き、各ジョブとステップの所要時間を確認してください。本来よりもはるかに遅いステップが少なくとも1つは見つかるはずです。
数値を書き留めておきましょう。変更を加えた後、戻って比較します。「CIが12分から5分に短縮された」ことを確認できれば満足感があり、修正が機能したことの証拠になります。
ステップバイステップ最適化チェックリスト
ステップ1: 依存関係のキャッシュを有効にする
お使いの言語用にキャッシュステップを追加します。Node.jsの場合、ワークフローに以下を追加します:
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
この cache: 'npm' 行は、実行間で自動的に node_modules を保存および復元するようGitHubに指示します。他の言語にも同様のオプションがあります。お使いの言語の公式アクションを確認してください。
ステップ2: ベースイメージを監査する
ワークフローで runs-on: ubuntu-latestを使用している場合は、そのイメージに含まれている内容を確認してください。Node.jsのみが必要な場合は、公式のNodeイメージ(docker://node:20)またはGitHubの軽量バージョンへの切り替えを検討してください。カスタムセットアップが必要な場合は、最小限のイメージを一度ビルドしてプッシュしてください。
ステップ3: 遅いジョブを並列ジョブに分割する
ワークフローのYAMLを確認します。互いに依存していない直列のジョブがある場合は、分割します。例:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run test
これにより、lintを実行してからテストを実行するのではなく、lintとテストが同時に実行されるようになります。
ステップ4: 冗長なアーティファクトを削除する
ワークフローで actions/upload-artifactを確認します。アーティファクトが他のジョブで使用されていない場合や手動でダウンロードされていない場合は、削除してください。デバッグ用にアーティファクトを保持する場合は、短い保持期間を設定します:
- uses: actions/upload-artifact@v4
if: failure()
with:
name: logs-on-failure
retention-days: 7
これにより、ログは失敗時にのみアップロードされ、1週間後に削除されます。
ステップ5: matrixの重複を確認する
ワークフロー内で matrix: を検索し、それぞれを確認してください。matrixが期待する動作を実際に変更していない場合は、削除してください。すべてのビルドを2回実行するmatrixは無駄な負荷です。
ステップ6: ステップの順序を見直す
ジョブ内では、各ステップが順番に実行されます。順序が適切であることを確認してください。ステップが遅くても、その結果がかなり後になってからしか使用されない場合は、使用される場所の近くに移動することを検討してください。ステップが高速で独立している場合は、早期に失敗させるために前に移動します。
継続的なモニタリング
最適化が完了しても、そのまま放置しないでください。CIワークフローは変化します。依存関係の肥大化、新しいステップの追加、イメージの変更などが起こります。毎月リマインダーを設定し、最も遅いワークフローをスポットチェックしてください。遅くなる傾向が見られる場合は、問題が深刻化する前に調査してください。
チームによっては、GitHub Actionsのメトリクスをモニタリングダッシュボードにエクスポートしています。これは発展的な手法ですが、多数のワークフローがある場合、性能低下を自動的に可視化できるため、かける労力に見合う価値があります。
まとめ
遅いCIは、請求書には表示されないチームの生産性(ベロシティ)に対する「税金」です。週に数百回もの実行を通じて静かに蓄積されていきます。キャッシュ、並列ジョブ、無駄の削減といった修正作業自体は難しくありませんが、まずは問題を認識することが必要です。今週1時間を割り当てて、ワークフローを見直してみてください。おそらく、1回の実行につき5〜10分の無駄が見つかるでしょう。それは将来の自分へのプレゼントになります。
メリット
- フィードバックループの高速化により、開発者の手を止めることなく集中状態(フロー)を維持できます。
- 実行完了が早くなることで、クラウドのコンピューティングコストが削減されます。
- 問題の早期発見が可能になります(例: 分割されたジョブがより早い段階で失敗する)。
- 開発者体験が向上します。15分ではなく3分待つ方がストレスが少なくなります。
- 小さな変更でも週に何十回もの実行で積み重なり、大幅な時間の節約につながります。
- 計測が容易です。ダッシュボードデータはGitHubですでに利用可能です。
デメリット
- 監査とメンテナンスが必要です。定期的な見直しなしには、ワークフローの最適化状態を維持できません。
- 過度なキャッシュにより、本番環境にデプロイされるまで依存関係のバグが隠蔽される可能性があります。
- ジョブを分割しすぎると複雑さが増します。ジョブが増えるほど、デバッグ対象も増えます。
- 最小限のイメージには後から必要になるツールが含まれていないことがあり、手戻りが発生する場合があります。
- アーティファクトのクリーンアップポリシーにより、デバッグに必要なログを紛失するリスクがあります。
- 並列化は十分な同時実行枠がある場合にのみ有効です。GitHubの無料プランには制限があります。
注意事項
本記事に含まれるすべての名前、設定値、およびプレースホルダー(例: node:20, ubuntu-latest, app.example.com)は例示のみを目的としています。本番環境にワークフローをデプロイする前に、ステージングブランチで十分にテストしてください。特定のセットアップ、言語のバージョン、およびインフラストラクチャは異なる場合があります。あるプロジェクトでうまく機能するキャッシュ戦略が、別のプロジェクトに適しているとは限りません。常に実際の環境でパフォーマンスの改善を検証し、副作用(例:古いキャッシュが原因で依存関係が古くなるなど)を監視してください。自己責任で実行してください。
よくある質問
- GitHub Actionsにおけるリポジトリごとの最大キャッシュサイズはどれくらいですか?
- GitHub Actionsのキャッシュがワークフローで実際に使用されているかどうかをどのように確認できますか?
- カスタムDockerイメージを使用してCIを高速化できますか?
- キャッシュを作成した後の初回実行時、時間が短縮されずに長くなるのはなぜですか?
- 同時実行枠の使用量を増やさずにGitHub Actionsのジョブを並列化するにはどうすればよいですか?
- node_modulesをキャッシュするのと、ロックファイルのみをキャッシュするのとでは、どちらが良いですか?
- CIの実行が遅いと、GitHubによってワークフローが自動的にキャンセルされることがありますか?
- GitHub Actionsのワークフローはどれくらいの頻度で見直しや更新を行うべきですか?
タグ
#github #actions #ci #cicd #devops #performance #optimization #workflows #automation #testing
Prompt-Injection Defense Checklist
The controls that actually reduce the blast radius when your app feeds untrusted text to an LLM. Enter your email — you'll get the PDF instantly, plus new posts on AI, security & Linux.
Free. No spam — unsubscribe in one click.


Responses
Sign in to leave a response.