VPSSpark Cloud Mac CI シリーズ #9。Flutter チームから何度も聞かれる問いに答えます:メンバー 8~15 名規模で、Cloud Mac 2 台で CI を安定運用できるか? 結論は可能——ただし Android と iOS を分けて設計することが前提です。Android ビルドは GitHub ホストの ubuntu-latest に残し、macOS 側は 2 台のセルフホスト Runner で macos-fast と macos-archive の 2 プールに分け、「PR では Archive を回さない」を徹底します。以下に負荷モデル、Job 階層、workflow 断片、導入チェックリストを示します。キュー診断と Runner 登録の詳細は、シリーズ #2 と 并网 FAQ を参照してください。
1 · 直感に反する結論:Flutter CI のボトルネックは Dart ではない
多くの Flutter チームで CI が赤になる原因は、flutter test の遅さではなく、iOS 側が macOS Runner スロットを占有していること——PR で誤って flutter build ipa を回しているケースが典型です。
Flutter のクロスプラットフォーム性から「1 本の workflow で全部」に陥りがちですが、現場では flutter analyze や単体テストは Linux でも実行可能です。macOS が必須なのは CocoaPods 解決、Xcode コンパイル、署名、IPA エクスポートです。PR パスに Archive Job が混ざると、1 台の Cloud Mac が 25~40 分の iOS パッケージに占有され、他 PR はすべて Queued になります——ネイティブ iOS チームと同じ罠で、診断式は wait time >> run time のままです(macOS runner queued 診断 参照)。
したがって「Cloud Mac 2 台」は万能機 2 台ではなく、意図的なデュアルプール構成です。1 台は速報専用、1 台はリリース重タスク専用。Android APK/AAB はホスト Linux に残し、貴重な macOS スロットを Gradle に使いません。
2 · 負荷モデル:8~15 人チームに必要な macOS 並列度
中程度の活発さを基準に試算します(1 人あたり 1 日 1~2 PR、main の nightly + 週次リリース):
Fast プール(macos-fast)の 1 Job 目標:analyze + 単体テスト + iOS Simulator ビルドを wall time 8~14 分(pub/DerivedData キャッシュあり)。Archive プール(macos-archive)の Release IPA + 公証は 1 回 20~35 分 ですが頻度は低く、main マージ・tag・nightly のみ。2 台を混在させると Archive が fast を詰まらせます。分離後、PR の P95 待ち時間は通常 10 分以内に収まり、Flutter チームのレビューペースとして十分です。
15 人超、または monorepo で matrix が複数 flavor に膨らむ場合は、3 台目の Archive 冗長や弾力スケールを検討します——それは 大規模 iOS CI 容量設計 の領域で、本稿の「2 台スタート」の範囲外です。
| プラットフォーム | Runner | 典型 Job | PR で実行 |
|---|---|---|---|
| Android | ubuntu-latest(ホスト) |
flutter build apk/appbundle、Android 単体テスト |
可 |
| iOS 速報 | Cloud Mac #1 · macos-fast |
analyze、単体テスト、build ios --simulator |
可 |
| iOS リリース | Cloud Mac #2 · macos-archive |
build ipa、公証、TestFlight アップロード |
不可 |
3 · Flutter Job 三層:CI Hard Rules へのマッピング
Flutter パイプラインをシリーズ共通の L0/L1/L2 に対応づけ、オンコール手順と揃えます:
| 層 | Flutter 内容 | プール | PR トリガー |
|---|---|---|---|
| L0 | dart format --set-exit-if-changed、flutter analyze、軽量単体テスト |
Linux または macos-fast |
可 |
| L1 | 統合テスト、flutter build ios --simulator、Widget テスト |
macos-fast |
可 · Archive 禁止 |
| L2 | flutter build ipa、App Store Connect アップロード、公証 |
macos-archive |
禁止 · main/tag/schedule のみ |
3 つの鉄則はネイティブ iOS と同じです:Rule 1 PR で L2 を回さない;Rule 2 L2 は隔離プール;Rule 3 fast プールを Archive でブロックしない。Flutter 特有の点:L0 の多くは Linux で足ります——macos-fast に載せると macOS 並列を無駄に消費します。PR では Linux Job と macOS Job を並列にし、macOS は「mac 必須」のステップだけに絞るのが定石です。
4 · デュアルマシン構成:Cloud Mac #1 と #2 の役割分担
図 1 · Cloud Mac 2 台で Flutter チーム CI を支える典型トポロジ
macos-fastL0/L1 · 8~14 min/Jobmacos-archiveL2 · main/tag 専用Cloud Mac #1(Fast プール):ラベル [self-hosted, macOS, macos-fast, flutter]。Flutter SDK(fvm またはイメージ pin)、Xcode、CocoaPods を固定版でプリインストール。Runner は常駐サービスで起動時自動起動。Job が短く回転が速いため、pub cache と DerivedData を積極的に温存できます。
Cloud Mac #2(Archive プール):ラベル [self-hosted, macOS, macos-archive, flutter-release]。Fast プールと物理分離——同一 Mac に 2 プールの Runner を載せないこと(Rule 2 が形骸化します)。Distribution 証明書と App Store Connect API Key を Keychain + 最小権限トークンで保管(登録手順は 30~60 分并网チェックリスト)。リリース前に flutter doctor -v と dry-run Archive をこちらで実行し、Fast 環境を汚しません。
2 台は同一リージョン・同一 Xcode メジャー版を推奨し、「Fast は緑、Archive は赤」のバージョンドリフトを防ぎます。Cloud Mac はオフィス Mac と比べ、イメージ固定・GitHub への安定回線・UPS/物理セキュリティ不要で、7×24 CI ノードに向きます。セルフホスト Runner のセキュリティ境界について、Archive 機の権限は単一 repo または単一 org に絞ってください。
5 · Workflow 例:PR と main の分岐
以下はコア分岐ロジックの断片です(完全 pipeline にはキャッシュ、シークレット、artifact ステップが別途必要)。Flutter 公式継続デリバリーに iOS 署名の詳細があります。ここでは証明書が Archive 機 Keychain にインポート済みと仮定します。
jobs: android-pr: if: github.event_name == 'pull_request' runs-on: ubuntu-latest steps: - run: flutter analyze && flutter test - run: flutter build appbundle --release ios-pr-fast: if: github.event_name == 'pull_request' runs-on: [self-hosted, macOS, macos-fast, flutter] steps: - run: flutter analyze - run: flutter test - run: flutter build ios --simulator --no-codesign ios-release: if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') runs-on: [self-hosted, macOS, macos-archive, flutter-release] steps: - run: flutter build ipa --export-options-plist=ExportOptions.plist # 上传 TestFlight / 公证 — L2 only
ios-pr-fast にbuild ipa は含まれていません。PR で実機パッケージ検証が必要なら、workflow_dispatch 手動トリガーにし、それでも Archive プール経由——PR イベントのデフォルトにしないこと。GitHub Actions のworkflow 構文で concurrency グループにより同一 PR の古い run をキャンセルすると、無駄な待ち行列をさらに約 20% 削減できます。
6 · キャッシュチェックリスト:Flutter チームが persist すべき 4 ディレクトリ
Cloud Mac 2 台を安定させるには、CPU 型番よりキャッシュが重要です。Runner ユーザーディレクトリに永続化(または nightly スナップショット)を推奨:
PUB_CACHE/~/.pub-cache— Dart 依存。pubspec.lock変更時は増分更新- Flutter SDK ディレクトリ —
fvmで版固定。flutter upgradeによるドリフト回避 ios/Pods+ CocoaPods ダウンロードキャッシュ —pod installは iOS 側の長尾要因- Xcode DerivedData — 同一
ios/Podfile.lockならヒット率が高く効果大
Fast と Archive プールでDerivedData を共有しないこと——Debug/Simulator と Release/Archive の成果物混在は不可解なリンクエラーの原因になります。Android の Gradle cache は ubuntu-latest 上で Actions Cache を使えば足り、Cloud Mac ディスクを消費しません。
pod repo update を Archive 機で先に検証し、通過後 Fast プールへ同期。日常 PR はキャッシュ再利用のみ。flutter pub upgrade を CI デフォルトに書かない。
7 · 導入チェックリスト:ゼロからデュアルマシン并网まで
順番に実行すれば、1~2 営業日で PoC 完了可能です:
- 同一スペックの Cloud Mac 2 台を開通(M4 + 16GB 以上推奨。iOS コンパイルのメモリピークは 12GB 超が珍しくない)
- #1 に Flutter/Xcode/CocoaPods を入れ
macos-fastRunner 登録。#2 に署名素材を追加しmacos-archive登録 - workflow 分割:Android は
ubuntu-latest。PR macOS は L1 のみ。main/tag のみ L2 - pub/DerivedData/Pods を永続化。3 日間 P95 wait time が 10 分未満か観測
- wait >> run が続くなら、まず L2 が PR に入っていないか確認してから台数追加
PoC 段階では Cloud Mac 1 台で Fast プールのみ、Archive は GitHub ホスト macos-latest(キュー許容)でも可——分池ロジック検証後に 2 台目を追加。これは「2 台スタート」と矛盾しません。2 台目はリリース SLA の保険であり、PR フィードバックの必須条件ではありません。
8 · FAQ
Flutter 単体テストを全部 Linux に置き、Cloud Mac 1 台で足りる?
PR で macOS コンパイル検証が不要なら理論上可能です——ただし多くのチームは PR で build ios --simulator を回し、ネイティブプラグイン問題を早期検出します。1 台で L1 は回せますが、Archive は速報と分離必須。リリース時に PR が一斉 Queued になります。堅実には 2 台です。
Cloud Mac 2 台とオフィス Mac mini 2 台の違いは?
トポロジは同じ。差は運用:Cloud Mac はハード調達不要、イメージスナップショット、GitHub 回線が安定しやすい。オフィス Mac は 7×24 と機房条件が揃う長期運用向き。短周期・分散チームは Cloud Mac を選ぶことが多いです。詳細は 大規模 CI ノード設計 を参照。
Codemagic / GitHub ホスト macOS は残すべき?
Archive プールのバックアップや PR overflow として有効。自托管を主経路にした後、ホスト macOS は「月 4 回未満のリリース」小規模チーム向き。活発な Flutter チームは長期コストで自托管が有利なことが多いです。
monorepo 複数 app の Job 数は?
flavor/matrix で乗算。PR matrix が macOS Job 4 超なら Fast プール 2 台では不足——matrix を絞るか 3 台目を追加し、Archive を PR に入れないこと。
Cloud Mac 2 台で Flutter デュアル CI を役割分担
Flutter チームのデュアルパイプラインでは、Android は Linux に残せますが、iOS コンパイル・署名・IPA エクスポートに macOS は不可欠です。Cloud Mac mini M4 を 2 台、macos-fast と macos-archive に分ければ、PR フィードバックとリリース重タスクがスロットを奪い合いません——Archive を PR workflow に混ぜて全員 Queued 待ちするより遥かに合理的です。Apple Silicon の Unified Memory は flutter build ios と Xcode リンク段階をスムーズにし、待機約 4W の消費電力は 7×24 CI ノードの常時稼働に適しています。
オフィス Mac 機房を自前構築するより、Cloud Mac はプラン開通・イメージ複製・GitHub 安定回線で、分散 Flutter チームがデュアルプールを素早く立ち上げられます。macOS ネイティブ Unix 環境は Flutter、CocoaPods、Fastlane がそのまま使え、追加の仮想化層は不要です。
Flutter チームの iOS CI 移行を計画中なら、VPSSpark Cloud Mac mini M4 を 2 台から始めるのが最小可行トポロジです——プラン詳細を見る。まず Fast プール PoC を通し、続けて Archive プールでリリースを担いましょう。