pnpm + TurborepoのモノレポCIが遅くなってきたのでUbicloudに移行したら3倍速くなった
GitHub Actions公式ランナーからUbicloudに移行してCI時間を約4分から1分14秒に短縮した話。Turborepoのキャッシュと相性が良く、Transparent Cacheで自動的にキャッシュが4倍速くなる。
pnpm + Turborepoでモノレポを運用していると、パッケージが増えるにつれてCIが遅くなっていきます。ビルド対象が増えれば増えるほど時間がかかる。これはモノレポの宿命みたいなもの。
最近、PRのCIが4分くらいかかるようになってきて、ちょっとストレスを感じていました。そこで試しにUbicloudに移行してみたら、劇的に改善したので共有します。
Ubicloudとは
Ubicloudは、GitHub Actionsのサードパーティランナーサービスです。
特徴:
- GitHub公式の約1/10の価格($0.0008/分 vs $0.008/分)
- 毎月$1分の無料クレジット(約1,250分)
- Transparent Cacheでキャッシュが自動的に4倍速
- 個人アカウントでも利用可能(Organizationは不要)
Blacksmithという類似サービスもあるのですが、そちらはGitHub Organizationが必須。個人リポジトリで使いたい場合はUbicloud一択でした。
導入方法
驚くほど簡単です。
- UbicloudでGitHubアカウントでサインアップ
- リポジトリを連携
- ワークフローの
runs-onを変更
# 変更前
runs-on: ubuntu-latest
# 変更後
runs-on: ubicloud-standard-2
これだけ。Transparent Cacheは自動で有効になるので、actions/cacheやactions/setup-nodeがそのまま高速化されます。
検証環境
今回検証したリポジトリの構成:
- 構成: pnpm + Turborepo モノレポ
- パッケージ数: 14個(apps 4個 + packages 10個)
- PRワークフローの処理内容:
- PostgreSQL serviceコンテナ起動
- pnpm install
- Turborepo経由でgenerate → build → type-check → lint → test
キャッシュの種類
このワークフローでは3種類のキャッシュが関係します:
-
pnpm store(
actions/setup-nodeのcache: 'pnpm')node_modulesの依存関係キャッシュ- サイズ: 約324MB
-
Turboキャッシュ(
.turboディレクトリ、actions/cacheで保存)- Turborepoのタスク実行結果キャッシュ
- サイズ: 約14MB
-
Turborepo内部キャッシュ
- 同一ジョブ内でのタスク間キャッシュ(generate → buildなど)
- これはCI実行ごとに揮発する
検証結果
GitHub公式 vs Ubicloud x64
まず、GitHub公式ランナー(ubuntu-latest)とUbicloud(ubicloud-standard-2)を比較しました。
GitHub公式ランナー(キャッシュあり: pnpm store: hit、.turbo: hit)
移行前の直近PR CIの実行時間:
| 実行 | 実行時間 |
|---|---|
| 1回目 | 4分22秒 |
| 2回目 | 3分56秒 |
| 3回目 | 3分56秒 |
→ キャッシュが効いた状態で約4分
Ubicloud x64
| 条件 | 実行時間 |
|---|---|
| キャッシュなし(pnpm store: miss、.turbo: miss) | 2分18秒 |
| キャッシュあり(pnpm store: hit、.turbo: hit) | 1分14秒 |
キャッシュが効いた状態同士で比較すると約3倍の高速化。
移行後の実運用結果
Ubicloud移行後、実際のPRやDeployワークフローの実行時間も確認しました。
PRワークフロー(直近4件)
| 実行 | 実行時間 |
|---|---|
| 1回目 | 2分4秒 |
| 2回目 | 2分8秒 |
| 3回目 | 2分50秒 |
| 4回目 | 2分55秒 |
→ 約2〜3分で安定稼働
Deployワークフロー(直近3件)
| 実行 | 実行時間 |
|---|---|
| 1回目 | 2分26秒 |
| 2回目 | 2分38秒 |
| 3回目 | 2分45秒 |
→ 約2分半〜3分で安定稼働
最速の1分14秒は.turboキャッシュが完全にヒットしたケース。実運用では変更内容によってキャッシュの効き具合が変わるため、2〜3分程度に落ち着いています。それでもGitHub公式の約4分と比べると十分速い。
キャッシュヒット時のログ
Ubicloudでキャッシュがヒットした際のログを確認すると:
# pnpm storeのリストア
Cache restored from key: node-cache-Linux-x64-pnpm-c0f281e9...
# .turboのリストア
Cache Size: ~14 MB (15035501 B)
Cache restored from key: turbo-Linux-X64-c0f281e9...
# Turborepoの各タスク
Generate: cache hit, replaying logs
Build: cache hit, replaying logs
Type Check: cache hit, replaying logs
Lint: cache hit, replaying logs
Test: cache hit, replaying logs
pnpm storeと.turboの両方がリストアされ、Turborepoの全タスクがキャッシュヒット。ほぼ全ての処理がリプレイで終わっています。
なぜこんなに速くなったのか
- Transparent Cache: Ubicloudは
actions/cacheのリストアが約4倍高速。GitHub公式はAzure Blob Storageを使っているのに対し、UbicloudはCloudflare R2にキャッシュを配置していて、ランナーに近い - 新しいハードウェア: GitHub公式はAzureの古めなVMを使用しているのに対し、Ubicloudは新しいCPUを採用
- Turborepoキャッシュとの相性: .turboがリストアされると、ほぼ全タスクがリプレイで終わる
ARMランナーも試してみた
Ubicloudはubicloud-standard-2-armでARMランナーも使えます。せっかくなので試してみました。
条件1: キャッシュなし(pnpm store: miss、.turbo: miss)
| アーキテクチャ | 実行時間 |
|---|---|
| x64 | 2分18秒 |
| ARM | 4分31秒 |
条件2: キャッシュあり(pnpm store: hit、.turbo: hit)
| アーキテクチャ | 実行時間 |
|---|---|
| x64 | 1分14秒 |
| ARM | 1分45秒 |
意外にもARMの方が遅い結果に。
ARMのログを確認すると、pnpm storeのキャッシュサイズが324MBと大きく、リストアに時間がかかっていました。また、Node.jsのネイティブモジュール(esbuild、sharpなど)のARM版ビルドにオーバーヘッドがあるのも原因っぽいです。
今回は素直にx64を採用しました。
ワークフローの変更点
最終的なワークフローの変更はこれだけ:
jobs:
test:
runs-on: ubicloud-standard-2 # ubuntu-latest から変更
# ...
キャッシュキーにアーキテクチャを含めるようにしたのがちょっとした工夫:
- name: Restore Turbo cache
uses: actions/cache@v4
with:
path: .turbo
key: turbo-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
turbo-${{ runner.os }}-${{ runner.arch }}-
これで将来ARMに切り替えてもキャッシュが混ざらない。
コスト面
GitHub Freeプランでは月2,000分の無料枠があります。Ubicloudは月$1分(約1,250分)の無料クレジット。
単純な無料枠の比較ではGitHub公式の方が多いのですが、Ubicloudは3倍速いので実質的な作業量は同等かそれ以上。
さらに、無料枠を超えた場合:
- GitHub: $0.008/分
- Ubicloud: $0.0008/分(10分の1)
CI使用量が多いプロジェクトほどUbicloudのメリットが大きくなります。
注意点
PostgreSQLなどのserviceコンテナ
GitHub Actionsのservices機能(テスト用DBなど)はUbicloudでもそのまま動きます。
services:
postgres:
image: postgres:17
env:
POSTGRES_DB: test
POSTGRES_USER: test
POSTGRES_PASSWORD: test
特に設定変更なしで動いたので助かりました。
Docker Buildx
docker/setup-buildx-actionもそのまま使えます。Deployワークフローでイメージビルドしていますが、問題なく動作しています。
まとめ
pnpm + Turborepoのモノレポ構成でCIが遅くなってきたら、Ubicloudへの移行を検討する価値があります。
- 導入は
runs-onを1行変えるだけ - pnpm store + .turboキャッシュが効いた状態同士で比較して約3倍高速
- 個人アカウントでも使える
- 無料枠あり、超過しても安い
4分待っていたCIが1分ちょっとで終わるのは、開発体験として結構違います。PRを出してすぐ結果がわかるのは気持ちいい。