kickflow Tech Blog

株式会社kickflowのプロダクト開発本部によるブログ

鉄は熱いうちに打て、CI は Blacksmith で鍛えろ — runs-on 1行で1.76倍高速化

鍛冶職人が金床の上で赤く熱された鉄をハンマーで打ち、火花が飛び散っている様子を描いた油彩風イラスト。背景には炉の炎と壁に掛けられた鍛冶道具が見える
Blacksmith 氏が runs-on を打ち直す様子(イメージ)

こんにちは、プロダクト開発本部でエンジニアをしている秋山です。

昨今のプロダクト開発では AI は切っても切れない存在となりました。kickflow でも掲げているバリューの一つに AI 1st があり、日々 Claude Code や Codex をフル活用してプロダクト開発をしています。

昨今の AI モデルの高性能化、コーディング AI のチームナレッジ集積による業務効率化に加えて、開発メンバーの増員(ありがたいことにここ1年で倍の人数になりました!)による稼働量増加などの要因のかけ合わせで、リポジトリのプルリクエスト数は日に日に増え続けています。


大量のプルリクエストで CI/CD がボトルネックになる

こうして開発の効率が上がることで大量のプルリクエストが作られてレビューが大変になる、といった話は良く聞きます。実際、レビューをいかに効率良く行うかは kickflow でもよく課題に挙がりますが、 大量のプルリクエストがもたらす問題はそれだけではありません。CI/CD の実行時間が業務効率のボトルネックになるケースを目の当たりにすることが多くなってきました。増加し続けるプルリクエストに加えて、Renovate のオートマージや自動リベースが CI/CD の実行回数の増加に拍車をかけています。

CI/CD の実行時間改善には発火するワークフロー条件の見直しや実行ジョブのパフォーマンスチューニングを実施するのがセオリーですが、シンプルに実行基盤の性能を上げるというのも選択肢の一つです。ランニングコストに転嫁されますが、開発コストをかけずにすぐに成果が得られるのが魅力です。いわゆる札束で殴るというヤツです。

Blacksmith が良いらしい

そんな中、最近 X のポストで Blacksmith がとても良いという話を見かけることがありました。

www.blacksmith.sh

何でもワークフローのランナー指定を一行変更するだけでコストがおおよそ半分になるので、倍スペック乗せても GitHub Actions のコストとそんな変わらないのだと…

Slack のポストのスクリーンショット。投稿者:秋山 領/投稿日時:6月2日 09:21/メッセージ:界隈で Blacksmith マジ速いの話が伝わってきて、runs-on: を1行差し替えるだけで従来と同コストで CI の実行時間を50%近く圧縮できるってホンマかいな…

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

Slack のポストのスクリーンショット。投稿者:kobakei/投稿日時:6月2日 11:03/メッセージ:めっちゃ興味ある

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

Slack のポストのスクリーンショット。投稿者:kobakei/投稿日時:6月2日15:38/メッセージ:新規SaaS申請出してクレカ登録しといたので、kickflow側でも使いたければどうぞ

なんと当日のうちに爆速で導入が決定しました。検証・実行・決断の速さビックリしました…!

ということで、早速 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 の詳細は以下の記事をご参照ください。

tech.kickflow.co.jp

基本的にランナー選択の基準は

  • ubuntu-slimblacksmith-2vcpu-ubuntu-2404
  • ubuntu-latestblacksmith-4vcpu-ubuntu-2404
  • ubuntu-latest-4coreblacksmith-8vcpu-ubuntu-2404

のように、スペック倍付けの設定になってます。 ただし RSpec に関しては 1 プロセス内でテストを直列実行するので、CPU コア数を増やしてもあまり効果がないため、2vcpu で据え置きにしています。

余談ですが、Blacksmith には GitHub Organizations 連携をすると管理画面からリポジトリを選択できるようになり、そこから移行対象のワークフローランナーを自動で検出してくれます。 後は移行したいランナーを個別に指定すると自動で移行用のプルリクエストを発行してくれます。便利!

見せてもらおうか、Blacksmith の性能とやらを

さて、実際にどれくらいの変化があったのか、導入前後の2週間で実行速度を比較してみました。 結果としては約 1.4 〜 2.7 倍高速化され、平均実行時間の合計が 1,206s から 682s に削減、おおよそ 1.76 倍高速になりました!

ランナーを一行変えただけでこの成果はかなり美味しいですね。

Blacksmith移行前後のジョブ別平均実行時間の比較棒グラフ。バックエンド・フロントエンド・共通ライブラリの全11ジョブで実行時間が短縮され、高速化倍率は1.42倍から2.67倍。最も時間のかかるRSpec 30並列は349秒から207秒に短縮

ワークフロー ジョブの内容 平均実行時間 (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 (実行倍率)で見てみます。

ワークフロー単位の実行時間Before/After比較チャート。バックエンド・フロントエンド・共通ライブラリのいずれも中央値で1.7〜2.6倍、平均値で3.3〜4.5倍高速化。特にp90は5.9〜7.9倍と改善幅が大きく、実行時間のばらつきが解消されたことを示す

ワークフロー 中央値 平均値 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 の利用状況に刺さったようです。

info.blacksmith.sh

ここで、ジョブのキュー待ちがどれくらい改善されたのかをワークフロー単位で集計して Before/After で示してみました。

ジョブのキュー待ち時間Before/After比較チャート。全体のp90は703秒から11秒へ64分の1に短縮。フロントエンドと共通ライブラリの中央値はVM起動時間によりわずかに増加したものの、p90以上の長いキュー待ちは各ワークフローでほぼ消滅

ワークフロー 中央値 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は0〜5秒が35.9%と120秒超が44.0%に二極化していたが、Afterは92.7%が15秒以内に収まり120秒超は1.5%に減少。待ち時間が短時間側に均質化したことを示す

待機時間 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.com

サービスを開発・運用する仲間を募集しています。株式会社kickflowはソフトウェアエンジニアリングの力で社会の課題をどんどん解決していく会社です。こうした仕事に楽しさとやりがいを感じるという方は、カジュアル面談・ご応募お待ちしています!

careers.kickflow.co.jp