self-hosted runnerで無限CIのカバー画像
self-hosted runnerで無限CI
2026/7/13
このエントリーをはてなブックマークに追加

最近はIssueをAIに拾わせて調査させて直させてPRまで出させて、条件が揃えば自動でマージする、というループを無限に回しています。便利なんだけど、これがGitHub ActionsのCIに完全依存していて、無料枠を使い切るとループごと止まってしまう。そんな悩みごとを解決してくれるのはやはりオンプレ。

オンプレは全てを解決する。

self hosted runnerを動かすことにした。

開発ループとボトルネック

今のAI開発ループとそのボトルネックはこんな感じ。

開発ループとボトルネック

CIがgreenにならないとPRはマージされない設計にしているので、CIが1つ止まるだけでループ全体が止まる。AIがどれだけ元気にIssueを拾って直しても、CIの無料枠が尽きた瞬間に全部渋滞する。これがずっと気になっていた。

self-hosted runner

自宅にはもういろんなサービスをオンプレで動かしている。だったらself-hosted runnerもそこに住まわせればいいじゃん、ということでActions Runner Controllerを入れることにした。KubernetesのCustom Resourceとしてrunnerを管理してくれる仕組みで、既存のArgoCDにHelm chartを1つ足すだけで済むのがありがたい。

認証はGitHub Appにした。個人アカウントなので組織単位のrunner共有みたいな機能は使えないんだけど、Appを作っておけばインストールするリポジトリを増やすだけで他のリポジトリにも使い回せる。地味に楽。

これで開発ループはこう変わった。

修正後の開発ループ

無料枠切れで詰まっていたCIの実行が自宅のRunner Podに移っただけで、ジョブディスパッチも自動マージも変わらず素通りするようになった。無料枠を気にする必要がなくなったので、ループはもう止まらない。

実際、こうやって支払い上限で完全に止まっていたジョブが

支払い上限に達しGitHub Actionsのジョブが起動できないエラー画面

self-hosted runnerに移してからはちゃんと起動するようになった。

self-hosted runnerでジョブが実行されている画面

(CI自体は別の理由で転けてるけど)

Runnerの設計

Runnerのpodはジョブが1つ来るたびに使い捨てられる仕組みになっている。レプリカを増やしてキャッシュ用のボリュームを持たせようとすると、みんなで1つのボリュームを取り合ってスケジューリングが詰まってしまう。うーん、どうしようかな、と考えた末、最初はレプリカ数を1固定にすることにした。その代わりキャッシュ(cargoのレジストリとビルド成果物、あとuvのキャッシュ)は永続化ボリュームに逃がして使い回す。並列実行は諦めたけど、無料枠が尽きてCIが1個も動かないよりはマシ。

とはいえうちは単一ノード構成。同じノードに乗っているPod同士なら、同じボリュームを同時マウントしても問題ないことに気づいた。というわけで今はレプリカを2台に増やして動かしている。

リソースは普段ほとんど遊んでいるので、requestは絞って、実際にジョブが来た時だけGitHub-hosted相当(2 vCPU/7GB)までバーストできるようにしている。そこそこの速度でちゃんと動いてくれるから今のところ不満ないかも。

今後

キャッシュが実際にどれくらいビルド時間を縮めてくれるか、継続して見ていきたい。他のリポジトリでも同じ仕組みを使い回せるように作ってあるので、必要になったら増やしていく。

self-hosted runnerで無限CI - ねこの部屋