VPSSpark ブログ
← 開発日記へ戻る

クラウドMac 3台で、1日500回のiOS CIを支えた

サーバーメモ · 2026.06.02 · 約 14 分

クラウドMac上のGitHub ActionsセルフホストMac RunnerとiOS CIキュー

結論から:1 日あたり約 500 回の iOS CI ビルドなら、多くのチームに Mac mini 8 台は不要で、Apple Silicon のクラウドMac 3 台で足ります——前提は GitHub Actions の Job を階層化し、セルフホスト Runner で高速キューと Release キューを分離すること。各 Mac でフル Archive を 3 本同時に回さないことです。

3 か月前、ある iOS チームの Xcode CI 環境を引き継ぎました。当時の前提は次のとおりです。

  • 開発者 18 名
  • アプリ 2 本(Monorepo 共有)
  • CI は GitHub Actions のみ
  • 1 日約 500 回の macOS Job(PR・単体テスト・夜間リリース含む)

調達案は Mac mini を 8 台購入してオンプレの セルフホスト Runner にする案でした。まず 2 週間の workflow ログを Job 種別で分解したところ、体感と実態が大きくずれていました——約 70% が PR 検証と統合ビルド、20% が Simulator 単体テスト、Archive + アップロードは約 10%(TestFlight 投入を別計上すれば Archive と Upload はさらに分割できます。下図参照)。

最終構成:Fast Mac Runner 2 台(PR / 単体テスト)+ Release Mac Runner 1 台(Archive・公証・TestFlight)——クラウドMac 3 台で全 iOS CI/CD を安定運用。以下は容量の算術、Xcode CloudBitrise を選ばず セルフホスト Mac Runner に専用ノードを載せた理由です。

図 1 · 1 日約 500 回の iOS CI 構成(当該チーム GitHub Actions 2 週間実測)

PR Build
65%
Unit Test
20%
Archive
10%
Upload
5%
3
台クラウドMac(実測で十分)
8→3
調達案の縮小
63
機時/日(キャッシュ後)

容量の算術:91 機時から 63 機時へ

500 回/日を 24 時間に平すと平均約 21 回/時——しかし詰まるのはピークです。欧米の午前マージ、リリース前日の PR 嵐では平均の 3~5 倍になりがち。容量はピーク待ち行列で設計し、深夜の平均だけ見てはいけません。

第 2 の変数は1 Job あたりの機時です。Simulator 単体テストは 4~8 分、依存解決付き PR ビルドは 12~20 分、Release の Archive + 公証 + アップロードは 25~45 分に達することがあります。当該チームの 2 週間 P50 では軽 Job 6 分・重 Job 35 分。Archive が 30% あると誤認すると「Mac 8 台必須」になりますが、500 回の内訳では重 Job は圧倒的に少ないです。

キャッシュなしでは軽 325 + 重 175 で 1 日約 91 機時。3 台 Mac の 24 時間上限 72 機時を超えます。DerivedData / SPM キャッシュ、L2 直列、ラベル分離後は軽 Job 平均 4 分・重 Job ホットパス 22 分で合計約 63 機時。ピーク係数 1.3 前後なら許容——「3 台で足りる」根拠です。

図 3 · 1 日の macOS 機時需要(同一 500 回/日モデル)

キャッシュなし
91 機時
キャッシュあり
63 機時

Xcode CI の階層化:Archive と PR を同一 Mac Runner キューに入れない

500 回/日を支える GitHub Actions パイプラインは、ほぼ必ず 3 段の Xcode CI に分けます。

  • L0:SwiftLint、単モジュールビルド——macos-fast ラベル。
  • L1:PR 統合ビルド(Archive なし)——同じ Fast Mac Runner、1 台あたり軽 Job 2 並列。
  • L2:Archive、公証、TestFlight——macos-archive1 台同時 1 本まで

GitLab CI、BuildkiteJenkins の macOS Agent も同様で、ラベルでルーティングし、雑多な 1 キューにしないこと。Buildkite のバーストは Buildkite 自前 macOS Agent と日割りクラウドMac、Runner プールの常駐 vs 弾力は GitHub Actions macOS Runner 弾力プール vs 常駐 を参照してください。

クラウドMac 3 台の役割分担(トポロジ図)

本番で採用した静的 3 ノードは次のとおりです(ホスト名は任意、役割は維持推奨)。

ノード Runner ラベル 役割 並列
Mac-A macos-fast PR Build、Unit Test L0/L1 ×2
Mac-B macos-fast Mac-A と対称、高速キュー L0/L1 ×2
Mac-C macos-archive Archive、公証、Upload L2 ×1

図 2 · 3 ノードとキュー(GitHub Actions セルフホスト Runner)

Mac-AFast Mac Runner
Mac-BFast Mac Runner
Fast QueuePR Build · Unit Test · L0/L1
Mac-CRelease Mac Runner
Archive QueueArchive · 公証 · TestFlight Upload
実測・本番で使ったのは VPSSpark クラウドMac mini M4M4 Pro(fast 2 + release 1)です。以下の容量・queue_wait P95・キャッシュ命中率は Apple Silicon クラウドMac 上の セルフホスト Runner 環境に基づき、非正規 VM ではありません。

fast 2 台がスループット、release 1 台が確定的な出荷を担います。リリース日に L2 が溢れたら Mac-A に一時的に macos-archive を足せますが、その間 L0 並列は止めてください。Keychain と DerivedData の競合で署名がランダムに失敗します。3 台の Xcode マイナーは Xcode リリースノート で固定してください。

16GB M4 での GitHub Actions Mac Runner 並列の目安
軽量 Xcode CI:1 台あたり Job 2 本。フル Archive:1 台 1 本。16GB M4 で Archive 2 本同時は、500 回/日の「偽の飽和」の主因です。

GitHub Actions Mac Runner が待ち行列になりやすい理由

多くのチームはまず GitHub ホスト macOS Runner の分パックを買い、PR が増えると列に並びます。原因は「GitHub が遅い」ではなく、(1)組織の macOS 並列上限、(2)各 workflow が毎回フル Archive、(3) セルフホスト Mac Runner を Job 種別で分けず、軽 Job が重 Job に塞がれることです。専用 クラウドMac 3 台へ移す前、ホスト Runner の queue_wait P95 は 40 分以上。fast / archive 二段キュー後、L1 の P95 は 8 分以内になりました。

定石は クラウドMac 3 台を基線のセルフホストプールとし、OSS 週や極端な尖峰だけホスト Runner で L0 を補うこと——コストを抑えつつ、秘密を毎週移し替えません。

セルフホスト Runner と Xcode Cloud の比較

Xcode Cloud は Apple エコシステムに深く寄せ、運用ゼロを狙うチーム向け。セルフホスト Mac Runner はキュー・キャッシュキー・Archive と社内 Jenkins/Buildkite の混在を自分で握りたい組織向けです。

観点 Xcode Cloud クラウドMac + セルフホスト Runner
課金 分パック + 並列上限 クラウドMac サブスク/日割り、機時が見える
キュー プラットフォーム共通 fast / archive ラベルを自管
秘密 ASC 連携が楽 Match / Keychain は自前 runbook
向くチーム 低頻度リリース・少カスタム 1 日 500+ 回の iOS CI/CD

分パックが枯れる切り替えシグナルは Xcode Cloud 上限と日割りクラウドMac で Archive を引き継ぐ FAQ を参照してください。

このチームが Xcode Cloud を選ばなかった理由

Xcode Cloud は評価しましたが、GitHub Actions + セルフホスト Runner が残りました。(1) バックエンドと Android が既に GitHub、CI を二重化したくない。(2) カスタムキャッシュキーと Monorepo マトリクスが必要。(3) リリース週の Job 数が分パックの快適域を超え、待ち行列を自管できない。Xcode Cloud が悪いのではなく、「1 日 500 回・キューを握る」目標とずれていました。

Bitrise と自前 Mac Runner のコスト感

Bitrise などのモバイル DevOps SaaS は Agent 運用を省き、並列プランで課金します。18 人・2 App・1 日約 500 Job 規模では、年間は クラウドMac 3 台サブスクを上回ることが多く、Archive 並列もプラン段階に縛られます。Runner を触りたくないスタートアップ向き。2 週間で セルフホスト Mac Runner を組むチームは、多くが 3~6 か月でノード費を回収します。Bitrise 利用中でも L2 だけ release 専用ノードへ移す部分移行は可能です。

Buildkite Mac Agent の長所と短所

Buildkite はキューをクラウドに置き Agent を自前 Mac に載せるため、弾力と Artifact 保持に優れます。反面、編成の学習コストがあり、Mac 3 台だけに導入すると過剰に感じることもあります。当該顧客は PoC でバーストは優秀と判断したものの、最終的に GitHub Actions ネイティブの セルフホスト Runner を選びました——開発が YAML 1 系統に慣れているためです。既に Buildkite がある場合、3 台クラウドMac のラベル戦略は本文の fast / archive トポロジと同じです。

クラウドMac CI とオンプレ Mac mini の違い

オンプレ Mac mini 8 台:CapEx・減価償却・停電・証明書監査。クラウドMac 3 台:OpEx が読みやすく、日割り PoC 可能、Git リポジトリに近いリージョンを選べます。1 台あたり 1 日 14 時間超の単調なコンパイルでスペックが数年固定ならローカル CI もあり得ます。クラウドMac CI は尖峰・委託週・審査シーズンの公証チャネル向きです。当チームは開発用にオフィス Mac 2 台を残し、重い Xcode CI はクラウドへ——8 台 mini の空転を避けました。CircleCI クラウド macOS とクラウドMac Runner の SLO 比較は CircleCI クラウド macOS vs セルフホスト Runner FAQ をどうぞ。

キュー SLO:感覚ではなくデータで 4 台目を決める

監視推奨:queue_wait_seconds(P95)、run_duration を L0/L1/L2 で分桶、cache_hit_ratiol2_concurrent(常に 3 超えないこと)。例:L1 待ち P95 < 8 分、L2 P95 < 25 分。L2 が 3 日連続で基準超え、キャッシュ命中率が既に 60% 超なら、4 台目 release を検討してください。

キャッシュ:63 機時のレバー

DerivedData は branch + Xcodeバージョン をキーに。SPM/CocoaPods のロック変更時に bust。fast ノードは読み取り専用共有、release はローカル NVMe に L2 DerivedData。署名素材は vault 経由でキャッシュに入れません。キーに macOS/Xcode マイナーを含めないと、アップグレード後に「ヒットしたのにリンク失敗」が起きます。キャッシュが 63 機時へ落とす主因です。

GitHub Actions セルフホスト Runner の最小設定

3 台にそれぞれ登録:macos-fast ×2、macos-archive ×1。L2 には concurrency を必ず付け、リリース Job の相互 cancel を防ぎます。

GitHub Actions · Release Mac Runner
concurrency:
                group: ios-archive-${{ github.ref }}
                cancel-in-progress: false
                jobs:
                archive:
                runs-on: [self-hosted, macos-archive]
                steps:
                - uses: actions/checkout@v4
                - run: xcodebuild archive -scheme App -archivePath build/App.xcarchive

release ノードは専用 macOS ユーザー + Match。再起動後は Keychain 解除スクリプトを先に実行してから夜間キューを開きます。無人運用の詳細は xcodebuild 公式ドキュメント と Fastlane を参照してください。

リリース日:500 回が 650 回になったら

順序:(1) 非クリティカル L0 を停止。(2) ホスト Runner は L1 のみ補充。(3) 48 時間だけ 4 台目のクラウドMac を日割り追加。L2 を各台 2 並列に恒久化しない——公証のランダム失敗で後から払います。

4 台目が本当に要る条件

  • L2 待ち P95 が 1 週間連続 40 分超で、キャッシュは既に最適化済み。
  • Monorepo に App が 5 本超え、1 台 release では夜間窓が足りない。
  • 全 PR でフル Archive——まず Job 設計を直し、機械を増やす前に。

アンチパターン

全 PR で Archive、Mac Runner をラベル分けしない、16GB M4 で Archive 2 本同時、キャッシュキーにブランチなし、失敗率だけ見て queue_wait を無視——いずれも「3 台では足りない」と誤診し、8 台調達に進みます。

FAQ:検索で拾われやすい短答

1 日 500 回の iOS ビルドに Mac は何台?

Job を PR / 単体テスト / Archive に分け、DerivedData キャッシュが効いていれば、多くのチームは Apple Silicon クラウドMac 3 台で足ります:Fast Mac Runner 2 + Release Mac Runner 1。500 回の半数以上がフル Archive なら、まずパイプラインを直すか、4 台目 release を計画してください。

GitHub Actions Mac Runner は同時に何 Job?

16GB M4 の目安:軽 Job 2 本(PR Build、Unit Test)、または Archive 1 本。「Archive 2 本」を常態化しないでください。

Archive を高並列にできない理由は?

メモリ競合で swap、ディスクキュー遅延、Keychain ロックcodesign の衝突が重なり、再現しにくいタイムアウトになります。安定したコンパイルエラーではありません。

クラウドMac は Xcode Cloud より安い?

長期・高頻度の iOS CI/CDで、専用 セルフホスト Runner と秘密の常駐が要るチームでは、クラウドMac 3 台の基線プールの方が分課金より読みやすいことが多いです。低頻度・運用ゼロ優先なら Xcode Cloud を先に試してください。

Bitrise とクラウドMac 3 台はどう選ぶ?

Bitrise は運用を省き、素早い立ち上げ向き。1 日約 500 Job でキューとキャッシュを握るなら、セルフホスト Mac Runner + クラウドMac の年間コストが下がることが多く、L2 並列は自分で定義できます。

VPSSpark:Fast 2 + Release 1 のクラウドMac 基線プール

「1 日 500 回に Mac は何台?」と計算しているなら、まず 2 週間のログで図 1 の内訳を描き、本文の 63 機时モデルが成り立つか検証してください。VPSSpark クラウドMac mini M4 / M4 Pro は日割り PoC と長期サブスクに対応し、GitHub Actions セルフホスト Mac Runner で PR と Archive を別キューに載せられます。

クラウド Mac プラン、または VPSSpark トップ でリージョンを選び、実 workflow で分桶してから 3 台で足りるか判断してください——いきなり 8 台ではありません。

期間限定

1日500ジョブのiOS CIはクラウドMac 3台から

GitHub Actions Mac Runner · セルフホスト · fast/archive

ホームへ
期間限定 プランを見る