
こんにちは、プロダクト開発本部でエンジニアをしている秋山です。
昨今のプロダクト開発では AI は切っても切れない存在となりました。kickflow でも掲げているバリューの一つに AI 1st があり、日々 Claude Code や Codex をフル活用してプロダクト開発をしています。
昨今の AI モデルの高性能化、コーディング AI のチームナレッジ集積による業務効率化に加えて、開発メンバーの増員(ありがたいことにここ1年で倍の人数になりました!)による稼働量増加などの要因のかけ合わせで、リポジトリのプルリクエスト数は日に日に増え続けています。
- 大量のプルリクエストで CI/CD がボトルネックになる
- Blacksmith が良いらしい
- 一番導入効果の出そうなジョブを選定する
- 見せてもらおうか、Blacksmith の性能とやらを
- CI/CD が詰まる元凶はジョブ待機時間だった
- まとめ
- We are hiring!
大量のプルリクエストで CI/CD がボトルネックになる
こうして開発の効率が上がることで大量のプルリクエストが作られてレビューが大変になる、といった話は良く聞きます。実際、レビューをいかに効率良く行うかは kickflow でもよく課題に挙がりますが、 大量のプルリクエストがもたらす問題はそれだけではありません。CI/CD の実行時間が業務効率のボトルネックになるケースを目の当たりにすることが多くなってきました。増加し続けるプルリクエストに加えて、Renovate のオートマージや自動リベースが CI/CD の実行回数の増加に拍車をかけています。
CI/CD の実行時間改善には発火するワークフロー条件の見直しや実行ジョブのパフォーマンスチューニングを実施するのがセオリーですが、シンプルに実行基盤の性能を上げるというのも選択肢の一つです。ランニングコストに転嫁されますが、開発コストをかけずにすぐに成果が得られるのが魅力です。いわゆる札束で殴るというヤツです。
Blacksmith が良いらしい
そんな中、最近 X のポストで Blacksmith がとても良いという話を見かけることがありました。
何でもワークフローのランナー指定を一行変更するだけでコストがおおよそ半分になるので、倍スペック乗せても GitHub Actions のコストとそんな変わらないのだと…

ホントにそんな上手い話があるのかと半信半疑ながらも、社内の Slack でカジュアルに呟いたところ、

さっそく弊社 CTO の小林からリアクションがあり、

なんと当日のうちに爆速で導入が決定しました。検証・実行・決断の速さビックリしました…!
ということで、早速 kickflow に Blacksmith を導入することになりました。
一番導入効果の出そうなジョブを選定する
kickflow の技術スタックですが、バックエンドは Ruby on Rails の API Mode、フロントエンドは Nuxt の SPA という構成になっています。修正がリモートにプッシュされると、差分ファイルから実行するべきジョブを判定したうえで必要なジョブだけが実行される仕組みです。
GitHub Actions で稼働しているジョブはいくつかありますが、一律で全てのジョブのランナーを Blacksmith に移行するのではなく、高頻度かつ実行時間が多いものをピックアップして効果のありそうなものを選別します。
Blacksmith 公式では GitHub Actions のランナーよりも 60% 安いと謳っています。 今回はパフォーマンスの向上が目的なので、ランナーのスペックを上げつつランニングコストは従来ベースに近い形になるようにランナーを設定した結果、今回の Blacksmith 移行対象のジョブは以下のようになりました。
| カテゴリ | ジョブの内容 | GitHub Runner (Before) | Blacksmith Runner (After) |
|---|---|---|---|
| バックエンド | リンター(Rubocop) | ubuntu-latest | blacksmith-2vcpu-ubuntu-2404 |
| テスト(RSpec 30並列) | ubuntu-latest | blacksmith-2vcpu-ubuntu-2404 | |
| i18n バリデーション | ubuntu-slim | blacksmith-2vcpu-ubuntu-2404 | |
| フロントエンド | リンター(ESLint) | ubuntu-latest | blacksmith-4vcpu-ubuntu-2404 |
| テスト(Vitest 4並列) | ubuntu-latest | blacksmith-4vcpu-ubuntu-2404 | |
| 型チェック (tsc) | ubuntu-latest | blacksmith-4vcpu-ubuntu-2404 | |
| i18n バリデーション | ubuntu-slim | blacksmith-2vcpu-ubuntu-2404 | |
| ビルド(nuxt build) | ubuntu-latest-4core | blacksmith-8vcpu-ubuntu-2404 | |
| 共通ライブラリ(mini_racer *) | リンター・型チェック・テスト(ESLint/tsc/Vitest) | ubuntu-slim | blacksmith-2vcpu-ubuntu-2404 |
| ビルド(tsdown) | ubuntu-slim | blacksmith-2vcpu-ubuntu-2404 | |
| テスト(RSpec) | ubuntu-latest | blacksmith-2vcpu-ubuntu-2404 |
※ mini_racer とは V8 JavaScript エンジンを Ruby に組み込む拡張ライブラリです。フロントエンドとバックエンドの処理を SSoT するために利用しています。mini_racer の詳細は以下の記事をご参照ください。
基本的にランナー選択の基準は
ubuntu-slimはblacksmith-2vcpu-ubuntu-2404ubuntu-latestはblacksmith-4vcpu-ubuntu-2404ubuntu-latest-4coreはblacksmith-8vcpu-ubuntu-2404
のように、スペック倍付けの設定になってます。 ただし RSpec に関しては 1 プロセス内でテストを直列実行するので、CPU コア数を増やしてもあまり効果がないため、2vcpu で据え置きにしています。
余談ですが、Blacksmith には GitHub Organizations 連携をすると管理画面からリポジトリを選択できるようになり、そこから移行対象のワークフローランナーを自動で検出してくれます。 後は移行したいランナーを個別に指定すると自動で移行用のプルリクエストを発行してくれます。便利!
見せてもらおうか、Blacksmith の性能とやらを
さて、実際にどれくらいの変化があったのか、導入前後の2週間で実行速度を比較してみました。 結果としては約 1.4 〜 2.7 倍高速化され、平均実行時間の合計が 1,206s から 682s に削減、おおよそ 1.76 倍高速になりました!
ランナーを一行変えただけでこの成果はかなり美味しいですね。

| ワークフロー | ジョブの内容 | 平均実行時間 (Before/After) | 実行倍率 |
|---|---|---|---|
| バックエンド | リンター(Rubocop) | 60s → 31s | x 1.94 |
| テスト(RSpec 30並列) | 349s → 207s | x 1.69 | |
| i18n バリデーション | 40s → 20s | x 2.00 | |
| フロントエンド | リンター(ESLint) | 113s → 76s | x 1.49 |
| テスト(Vitest 4並列) | 192s → 72s | x 2.67 | |
| 型チェック (tsc) | 124s → 73s | x 1.69 | |
| i18n バリデーション | 40s → 22s | x 1.82 | |
| ビルド(nuxt build) | 93s → 63s | x 1.48 | |
| 共通ライブラリ | リンター・型チェック・テスト(ESLint/tsc/Vitest) | 52s → 28s | x 1.85 |
| ビルド(tsdown) | 51s → 25s | x 2.04 | |
| テスト(RSpec) | 92s → 65s | ×1.42 | |
| 合計 | 1,206s → 682s | x 1.76 |
また、これらのジョブは実際にはワークフロー単位でグルーピングされ、今回 Blacksmith に移行していない他のジョブなども含めて実行されています。ワークフロー単位の実行時間も Before → After (実行倍率)で見てみます。

| ワークフロー | 中央値 | 平均値 | p90 |
|---|---|---|---|
| バックエンド | 919s → 355s (x 2.58) | 1,377s → 420s (x 3.27) | 3,275s → 552s (x 5.93) |
| フロントエンド | 334s → 166s (x 2.01) | 672s → 159s (x 4.22) | 1,623s → 223s (x 7.27) |
| 共通ライブラリ | 138s → 79s (x 1.74) | 314s → 70s (x 4.48) | 838s → 106s (x 7.90) |
全体的にパフォーマンスは向上しているのはもちろんのこと、特筆すべきは中央値だけでなく p90 の短縮が際立っていることです。実行時間のばらつきが改善され、CI/CD の安定性が劇的に向上しています。
CI/CD が詰まる元凶はジョブ待機時間だった
この実行時間のばらつきのは、GitHub ランナーが組織全体で同時実行ジョブ数に制限があることが原因です。ジョブ数が上限に達すると各ジョブがランナーを確保できるまで待機します。このキュー待ちジョブが滞留していくと CI/CD の実行が遅くなっていきます。前述の p90 の実行が極端に遅いのはこれが原因でした。
Blacksmith の公式の記事では Blacksmith は同時実行数に制限がないことが明示されていて、これが kickflow の利用状況に刺さったようです。
ここで、ジョブのキュー待ちがどれくらい改善されたのかをワークフロー単位で集計して Before/After で示してみました。

| ワークフロー | 中央値 | p90 | p99 |
|---|---|---|---|
| バックエンド | 152s → 8s | 778s → 11s | 1,286s → 163s |
| フロントエンド | 4s → 7s | 502s → 9s | 1,107s → 39s |
| 共通ライブラリ | 6s → 7s | 274s → 20s | 941s → 70s |
| 全体 | 56s → 8s | 703s → 11s | 1,237s → 138s |
After の Blacksmith には約 8 秒の下限があり、フロントエンドと共通ライブラリの中央値は微増しています。これはおそらくBlacksmith のオンデマンド VM 起動時間と考えられます。 下限は Before の GitHub ランナーよりも高いものの、その代わり p90 以上の長いキュー待ちを消滅させていて、全体では安定性が向上しています。 特にバックエンドはテストの実行に RSpec の 30並列ジョブが一斉にランナーを要求するため、キュー待ち時間の影響が大きかったようです。
以下は全ジョブの待ち時間の分布の Before/After です。

| 待機時間 | Before | After |
|---|---|---|
| 0–5s | 35.9% | 21.6% |
| 5–15s | 5.5% | 71.1% |
| 15–30s | 4.8% | 1.6% |
| 30–60s | 5.0% | 1.3% |
| 60–120s | 4.8% | 2.9% |
| >120s | 44.0% | 1.5% |
Before は ランナーが空いていれば即時・ 飽和していれば数分待ちの二峰性だったのが、After は 15 秒以下に均質化しているのが分かります。 Before は全ジョブの 約半数近く(44%)が ランナー確保に 120 秒以上待っていましたが、After は 1.5% にまで低下していて、キュー待ちのジョブはほぼ解消された形になります。
これらは重量級のワークフローを Blacksmith に逃がしたことで、GitHub ランナーの共有同時実行枠の競合が減り、移行していない他ワークフローのキュー待ちも緩和されていることも改善の要因の一つと考えられます。
まとめ
- Blacksmith でランナーを1行変更するだけで CI/CD が速くなる、は本当でした!
- ワークフローの内容にもよりますが、kickflow のケースでは約1.4〜2.7倍の速度改善を実現できました。
- 単純な移行であればコスト削減を、ランナーのスペックを調整すればパフォーマンスアップを狙えます。
- GitHub ランナーの同時実行ジョブ数上限に悩まされている方は、ランナーのスペックを上げなくともこの恩恵を受けられます。
- 費用対効果がバツグンに良いので Blacksmith、全力でオススメできます!
We are hiring!
kickflow(キックフロー)は、運用・メンテナンスの課題を解決する「圧倒的に使いやすい」クラウドワークフローです。
サービスを開発・運用する仲間を募集しています。株式会社kickflowはソフトウェアエンジニアリングの力で社会の課題をどんどん解決していく会社です。こうした仕事に楽しさとやりがいを感じるという方は、カジュアル面談・ご応募お待ちしています!