記事一覧に戻る
pnpm + TurborepoのモノレポCIが遅くなってきたのでUbicloudに移行したら3倍速くなった

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一択でした。

導入方法

驚くほど簡単です。

  1. UbicloudでGitHubアカウントでサインアップ
  2. リポジトリを連携
  3. ワークフローのruns-onを変更
# 変更前
runs-on: ubuntu-latest

# 変更後
runs-on: ubicloud-standard-2

これだけ。Transparent Cacheは自動で有効になるので、actions/cacheactions/setup-nodeがそのまま高速化されます。

検証環境

今回検証したリポジトリの構成:

  • 構成: pnpm + Turborepo モノレポ
  • パッケージ数: 14個(apps 4個 + packages 10個)
  • PRワークフローの処理内容:
    • PostgreSQL serviceコンテナ起動
    • pnpm install
    • Turborepo経由でgenerate → build → type-check → lint → test

キャッシュの種類

このワークフローでは3種類のキャッシュが関係します:

  1. pnpm storeactions/setup-nodecache: 'pnpm'

    • node_modulesの依存関係キャッシュ
    • サイズ: 約324MB
  2. Turboキャッシュ.turboディレクトリ、actions/cacheで保存)

    • Turborepoのタスク実行結果キャッシュ
    • サイズ: 約14MB
  3. 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の全タスクがキャッシュヒット。ほぼ全ての処理がリプレイで終わっています。

なぜこんなに速くなったのか

  1. Transparent Cache: Ubicloudはactions/cacheのリストアが約4倍高速。GitHub公式はAzure Blob Storageを使っているのに対し、UbicloudはCloudflare R2にキャッシュを配置していて、ランナーに近い
  2. 新しいハードウェア: GitHub公式はAzureの古めなVMを使用しているのに対し、Ubicloudは新しいCPUを採用
  3. Turborepoキャッシュとの相性: .turboがリストアされると、ほぼ全タスクがリプレイで終わる

ARMランナーも試してみた

Ubicloudはubicloud-standard-2-armでARMランナーも使えます。せっかくなので試してみました。

条件1: キャッシュなし(pnpm store: miss、.turbo: miss)

アーキテクチャ実行時間
x642分18秒
ARM4分31秒

条件2: キャッシュあり(pnpm store: hit、.turbo: hit)

アーキテクチャ実行時間
x641分14秒
ARM1分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を出してすぐ結果がわかるのは気持ちいい。