結論から: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 はさらに分割できます。下図参照)。
図 1 · 1 日約 500 回の iOS CI 構成(当該チーム GitHub Actions 2 週間実測)
容量の算術: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 回/日モデル)
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-archive、1 台同時 1 本まで。
GitLab CI、Buildkite、Jenkins の 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)
fast 2 台がスループット、release 1 台が確定的な出荷を担います。リリース日に L2 が溢れたら Mac-A に一時的に macos-archive を足せますが、その間 L0 並列は止めてください。Keychain と DerivedData の競合で署名がランダムに失敗します。3 台の Xcode マイナーは Xcode リリースノート で固定してください。
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_ratio、l2_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 を防ぎます。
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 台ではありません。