この記事をシェア
この記事は、10月25日の記事(人間とAIの非同期ワーク)の再掲です。Google Gemini 3 Proに、わずか数行のプロンプトで、サムネイル込みで作ってもらったものをお見せします。人類はものすごい道具を手に入れました。
序章:終わらないCI、積み上がるタスク、そして絶望
金曜日の午後4時。あなたは週末のリリースに向けて、最後のプルリクエストをマージした。あとはCIが通るのを待つだけだ。
しかし、あなたの表情は晴れない。なぜなら、このCIパイプラインが完了するまでに、たっぷり3時間はかかることを知っているからだ。
あなたのチームが開発しているプロダクトは、ここ数年で急成長した。コードベースは肥大化し、Markdownで書かれたドキュメントは数千ファイルに及び、多言語対応の要望も日々増えている。さらに最近では、「AIを活用してコードレビューを自動化したい」「ドキュメントの翻訳をClaudeにやらせたい」「PRの内容からリリースノートを自動生成したい」といった、LLM(大規模言語モデル)を活用したタスクがCIに次々と追加された。
当初は素晴らしいアイデアに思えたこれらのAIタスクだが、現実にはCIのボトルネックとなっていた。
# これまでの絶望的な直列処理ワークフロー
jobs:
ai-tasks:
runs-on: ubuntu-latest
steps:
- name: 全ファイルの翻訳
run: |
for file in $(find docs -name "*.md"); do
# 1ファイルずつClaude APIを叩く…終わらない…
python translate_with_claude.py "$file"
done
- name: 全コードのレビュー
run: # これも直列…
1つのファイルに対してClaude APIを呼び出し、応答を待ち、次のファイルへ移る。この直列処理(シーケンシャル実行)が、あなたの貴重な時間を奪っていたのだ。しかも、GitHub-hostedランナーの課金タイマーは、ただAPIの応答を待っている間も無情に回り続ける。
「このままでは、開発スピードが維持できない……」
あなたは気付く。問題はAIの性能ではない。「処理のさせ方」そのものが間違っていたのだ。
転機:Matrix戦略という「思考の転換」
週末、あなたは悶々としつつも、以前読んだブログ記事(人間とAIの非同期ワーク)のことを思い出していた。そこには「人間とAIの非同期ワーク」についての概念が書かれていた。
「待てよ。これをCIの世界に適用したらどうなる?」
CIにおける「非同期」とは何か。それは、一つの巨大なジョブが終わるのを待たずに、複数のジョブを同時に走らせることだ。
そこであなたが目をつけたのが、GitHub ActionsのMatrix戦略(Matrix Strategy)だった。
Matrix戦略は、一般的には「複数バージョンのNode.jsでテストする」あるいは「UbuntuとWindowsで動作確認する」といった、クロスプラットフォームテストの文脈で語られることが多い。
しかし、あなたの脳裏に閃きが走る。
「Matrixは、単なるテスト環境の切り替え機能じゃない。タスクを無限に分割し、並列実行するための『分散コンピューティングエンジン』なんじゃないか?」
もし、1000個のドキュメント翻訳タスクがあるなら、1つのジョブで1000回ループするのではなく、1000個のジョブを同時に立ち上げて、それぞれに1ファイルずつ処理させればいい。
理論上、処理時間は1/1000になるはずだ。
実装:ClaudeCodeActions × Matrix の融合
あなたは早速、アーキテクチャの再設計に取り掛かった。
目指すは、巨大なデータを個別のチャンク(塊)に分割し、それぞれを独立したGitHub Actionsジョブとして並列起動し、その中でClaude(ClaudeCodeActions)に処理させるシステムだ。
1. 動的Matrixの生成
まず必要なのは、「何を並列化するか」を定義するMatrixの配列を動的に生成することだ。静的に [file1, file2, ...] と書くわけにはいかない。
最初のジョブで、対象となるファイル一覧を取得し、それをJSON配列として出力する。
jobs:
# 1. 並列化の「種」を作るジョブ
setup-matrix:
runs-on: ubuntu-latest
outputs:
# ここで生成したファイルリストを次のジョブに渡す
matrix-files: ${{ steps.set-matrix.outputs.files }}
steps:
- uses: actions/checkout@v4
- id: set-matrix
run: |
# 例えば、変更があったMarkdownファイルだけを抽出
files=$(git diff --name-only HEAD^ HEAD | grep '.md' | jq -R -s -c 'split("\n")[:-1]')
echo "files=$files" >> $GITHUB_OUTPUT
このジョブは一瞬で終わる。重要なのは、ここで作られた ["docs/A.md", "docs/B.md", "docs/C.md", ...] という配列だ。
2. Matrixによる爆発的並列実行
次に、この配列を受け取り、Matrix戦略を使ってジョブを「爆発」させる。ここで、仮称「ClaudeCodeActions」が登場する。これは、指定された入力を受け取り、Claude APIに投げ、結果を適切な形で保存するカスタムActionだ。
jobs:
# ... setup-matrix ジョブは省略 ...
process-with-claude:
needs: setup-matrix
runs-on: ubuntu-latest
strategy:
fail-fast: false # 1つ失敗しても他は止めない
matrix:
# setup-matrixで生成したファイルリストを展開!
file: ${{ fromJson(needs.setup-matrix.outputs.matrix-files) }}
steps:
- uses: actions/checkout@v4
# ここが並列処理の核心!
- name: ClaudeCodeActionsによる個別処理
uses: my-org/claude-code-actions@v1
with:
task: "translate_to_english"
target_file: ${{ matrix.file }} # Matrix変数を渡す
api_key: ${{ secrets.CLAUDE_API_KEY }}
このワークフローをプッシュした瞬間、GitHub ActionsのUIは壮観な眺めとなった。10個、50個、いや100個を超えるジョブが同時に Queued になり、次々と In progress に変わっていく。
これまで3時間かかっていた翻訳タスクが、最も時間のかかる1ファイルの処理時間(例えば3分)で完了するようになったのだ。
障壁:コストとスケーラビリティの壁
しかし、ここで新たな問題に直面する。
GitHub-hostedランナー(GitHubが用意してくれる標準の実行環境)は便利だが、並列度を極端に上げると二つの問題が発生する。
APIレート制限: 短時間に数百のジョブから同時にClaude APIを叩けば、当然レート制限に引っかかる。(これはリトライ処理である程度カバーできるが、効率は落ちる)
従量課金の恐怖: GitHub Actionsの無料枠はすぐに使い果たされる。数百並列でジョブを走らせれば、課金額は青天井だ。APIの待ち時間にも課金されるモデルは、AIタスクとは相性が悪い。
「並列化はできた。だが、このままでは破産する」
ここで、最後のピースが必要になる。「定額」と「Self-Hosted」だ。
到達点:Self-Hostedランナーによる「俺たちの計算資源」
この巨大並列システムをサステナブル(持続可能)にする唯一の解は、Self-Hostedランナーの導入だった。
AWS EC2、Google Kubernetes Engine (GKE)、あるいはオフィスに転がっているハイスペックPCでもいい。自分たちが管理するインフラ上にGitHub Actionsのランナーエージェントをインストールし、ジョブの実行をそちらに向けるのだ。
Self-Hostedのメリット:
実質的な「定額」化: インフラ費用はかかるが、ジョブの実行時間に応じた従量課金からは解放される。APIの応答を待つだけのアイドルタイムに怯える必要はもうない。
圧倒的なスケーラビリティ(K8sの場合): Kubernetes上で actions-runner-controller などを利用すれば、キューに溜まったジョブの数に応じて、ランナー(Pod)を自動的に数百、数千単位でスケールアウト・スケールインできる。
セキュリティと実行環境の制御: プライベートネットワーク内のリソースへのアクセスや、AIモデルのキャッシュなども自由自在だ。
ワークフローの修正は簡単だ。runs-on を書き換えるだけ。
process-with-claude:
needs: setup-matrix
# 自前で構築した強力なランナー群を指定
runs-on: [self-hosted, high-cpu, k8s-scale-set]
strategy:
matrix:
file: ${{ fromJson(needs.setup-matrix.outputs.matrix-files) }}
# ... (以下同じ) ...
結実:非同期×並列×定額×SelfHosted がもたらす未来
こうして構築されたシステムは、もはや単なるCIツールではなく、巨大な分散AIデータ処理基盤へと進化した。
非同期: 開発者はPRを投げたら、あとはシステムが裏でよしなにやってくれる。完了通知をSlackで受け取るだけ。
並列 (Matrix): 1000ファイルの処理も、1ファイル処理するのとほぼ同等の時間で完了する。
定額 & Self-Hosted: インフラコストは予測可能範囲内に収まり、自分たちの支配下にある計算資源をフル活用できる。
金曜日の夕方。再びプルリクエストがマージされた。
以前なら絶望していたその瞬間、あなたの目の前のダッシュボードでは、Kubernetesクラスタが唸りを上げて数百のPodを立ち上げ、Matrix戦略によって分割されたClaudeへのリクエストが同時多発的に飛んでいく。
ほんの数分後。全てのステータスがグリーンに変わった。
あなたはコーヒーを一口飲み、定時でPCを閉じる。週末は、このシステムを使って次に何を自動化しようかと、ワクワクした空想に耽りながら。
さあ、次はあなたの番だ。直列処理の呪縛から解き放たれ、GitHub Actions Matrixという名の武器を手に取ろう。あなたのリポジトリは、まだその真価を発揮していないだけなのだから。
