VPSSpark 部落格
← 返回開發日記

如何用 2 台 Cloud Mac 支撐 Flutter 團隊 CI?

機房手記 · Cloud Mac CI #9 · 2026.06.08 · 約 12 分鐘閱讀

常見搜尋:Flutter CI macOS · Flutter iOS 建置流水線 · Cloud Mac 自託管 Runner · Flutter 團隊 CI 架構

Flutter 開發者在 Mac 上除錯行動應用,背景為 CI 流水線示意
Flutter 雙端 CI 的瓶頸通常在 iOS/macOS 側——2 台 Cloud Mac 剛好夠分 fast 與 archive 池。

VPSSpark Cloud Mac CI 系列 #9。本篇回答 Flutter 團隊常被問到的問題:8~15 人的團隊,能不能用 2 台 Cloud Mac 把 CI 跑穩? 答案是可以——前提是把 Android 與 iOS 分開看:Android 構建留在 GitHub 託管的 ubuntu-latest,macOS 側用兩台自託管 Runner 分成 macos-fastmacos-archive 兩個池,並嚴守「PR 不跑 Archive」這條鐵律。下文提供負載模型、Job 分層、workflow 片段與落地清單;排隊診斷與 Runner 註冊細節分別見系列 #2併網 FAQ

1 · 反直覺結論:Flutter CI 的瓶頸不在 Dart

多數 Flutter 團隊 CI 亮紅燈,不是 flutter test 慢,而是 iOS 側占滿 macOS Runner 槽位——PR 裡誤跑了 flutter build ipa

Flutter 跨平台優勢容易讓人誤以為「一條 workflow 打天下」。實務上,flutter analyze 與單元測試在 Linux 也能跑;真正必須 macOS 的,是 CocoaPods 解析、Xcode 編譯、簽章與 IPA 匯出。若 PR 路徑混進 Archive Job,一台 Cloud Mac 會被 25~40 分鐘的 iOS 打包占滿,其餘 PR 全部 Queued——和原生 iOS 團隊踩過的坑一模一樣,診斷公式仍是 wait time >> run time(詳見 macOS runner queued 診斷)。

因此「2 台 Cloud Mac」不是「2 台萬能機」,而是刻意設計的雙池拓撲:一台專職快回饋,一台專職發版重任務。Android APK/AAB 繼續走託管 Linux,不把寶貴的 macOS 槽位浪費在 Gradle 上。

2 · 負載模型:8~15 人團隊需要多少 macOS 並行?

以中等活躍度的 Flutter 團隊為基準(每人每天 1~2 次 PR,主分支 nightly + 每週發版):

12
典型團隊人數
~18
工作日 macOS Job/天
2
Cloud Mac 槽位

快池(macos-fast)單次 Job 目標:analyze + 單測 + iOS 模擬器構建,wall time 控制在 8~14 分鐘(有 pub/DerivedData 快取)。Archive 池(macos-archive)上的 Release IPA + 公證,單次 20~35 分鐘,但頻率低——main 合併、tag、定時 nightly 才觸發。兩台機器若混池,Archive 會把 fast 池堵死;分開後,PR 的 P95 等待時間通常能壓在 10 分鐘以內,對 Flutter 團隊的 code review 節奏已經夠用。

若團隊超過 15 人、或 monorepo 裡 matrix 拆多 flavor,才需要考慮第三台 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 分層,便於和 on-call 手冊對齊:

層級 Flutter 內容 PR 可否觸發
L0 dart format --set-exit-if-changedflutter 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

三條鐵律與原生 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 · 2 台 Cloud Mac 支撐 Flutter 團隊 CI 的典型拓撲

GitHub Actions · PR / main 分流Android → ubuntu-latest
Cloud Mac #1 · macos-fastL0/L1 · 8~14 min/Job
Cloud Mac #2 · macos-archiveL2 · main/tag 專用

Cloud Mac #1(快池):註冊 label [self-hosted, macOS, macos-fast, flutter]。預裝固定版本的 Flutter SDK(用 fvm 或映像內 pin 版本)、Xcode、CocoaPods。Runner 以常駐服務方式執行,開機自啟;這台機器可以較積極地保留 pub cache 與 DerivedData,因為 Job 短、周轉快。

Cloud Mac #2(Archive 池):label [self-hosted, macOS, macos-archive, flutter-release]。與快池實體隔離——不要在同一台 Mac 上掛兩個 pool 的 Runner,否則 Rule 2 形同虛設。Archive 機存放 Distribution 憑證與 App Store Connect API Key(用 Keychain + 最小權限 token,註冊步驟見 30~60 分鐘併網清單)。發版前可在這台機上跑 flutter doctor -v 與一次 dry-run Archive,避免污染快池環境。

兩台機器建議同區域、同 Xcode 大版本,減少「快池綠、Archive 紅」的版本漂移。Cloud Mac 相對辦公室 Mac 的優勢是:映像規格固定、到 GitHub 的網路穩定、無需維護 UPS 與實體安全——適合作為 7×24 的 CI 節點。關於自託管 Runner 的安全邊界,務必把 Archive 機權限收窄到單一 repo 或單一 org。

5 · Workflow 範例:PR 與 main 分流

下面片段展示核心分流邏輯(完整 pipeline 還需快取、密鑰與 artifact 步驟)。Flutter 官方持續交付文件對 iOS 簽章有詳細說明,此處假設憑證已匯入 Archive 機 Keychain。

flutter-ci.yml(核心分流)
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 的四類目錄

2 台 Cloud Mac 要跑穩,快取比 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 命中率高時效益明顯

快池與 Archive 池不要共用同一份 DerivedData 目錄——Debug/Simulator 與 Release/Archive 的編譯產物混寫會導致詭異的連結錯誤。Android 側的 Gradle cache 留在 ubuntu-latest 上用 Actions Cache 即可,無需占用 Cloud Mac 磁碟。

映像基線建議
每月做一次「乾淨映像」升級:Flutter minor bump + Xcode patch + pod repo update,在 Archive 機先驗證通過再同步快池。日常 PR 只複用快取,不要把 flutter pub upgrade 寫進 CI 預設步驟。

7 · 落地清單:從 0 到雙機併網

按順序執行,可在 1~2 個工作天內完成 PoC:

  • 開通 2 台同規格 Cloud Mac(建議 M4 + 16GB 起,iOS 編譯記憶體峰值常見 12GB+)
  • #1 裝 Flutter/Xcode/CocoaPods,註冊 macos-fast Runner;#2 額外匯入簽章材料,註冊 macos-archive
  • 拆分 workflow:Android 留 ubuntu-latest;PR macOS 僅 L1;main/tag 才 L2
  • 設定 pub/DerivedData/Pods 持久化;觀察 3 天 P95 wait time 是否 < 10 min
  • 若 wait >> run 仍成立,先查 workflow 是否 L2 進 PR,再加機器

PoC 階段可以只買一台 Cloud Mac 跑快池,Archive 暫時用 GitHub 託管 macos-latest(接受排隊)——驗證分池邏輯後再補第二台。這與「2 台起步」不矛盾:第二台是發版 SLA 的保險,不是 PR 回饋的必需品。

8 · FAQ

Flutter 單測能不能全部放 Linux,一台 Cloud Mac 夠不夠?

若 PR 不需要 macOS 編譯驗證,理論上可以——但多數團隊會在 PR 上跑 build ios --simulator 抓原生外掛問題。一台 Mac 跑 L1 可以,Archive 必須與快回饋隔離,否則發版時 PR 會全線 Queued。穩妥方案仍是 2 台。

2 台 Cloud Mac 和買 2 台 Mac mini 放辦公室有什麼差別?

拓撲相同;差異在維運: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 產生 4 個以上 macOS Job,2 台 fast 池可能不夠——應收斂 matrix 或加第三台,而不是把 Archive 塞進 PR。

系列導覽(#9 Flutter 雙機 CI)
#1 容量 · #2 排隊 · #3 自託管算帳 · #8 構建提速 · #9 本篇

2 台 Cloud Mac,讓 Flutter 雙端 CI 各就各位

Flutter 團隊的雙端流水線裡,Android 可以留在 Linux,但 iOS 編譯、簽章與 IPA 匯出離不開 macOS。用 2 台 Cloud Mac mini M4 分別承擔 macos-fastmacos-archive,PR 回饋與發版重任務互不搶槽——比把 Archive 塞進 PR workflow、然後全員等 Queued 划算得多。Apple Silicon 統一記憶體架構讓 flutter build ios 與 Xcode 連結階段更順暢;約 4W 待機功耗也適合作為 7×24 CI 節點長期在線。

相比自建辦公室 Mac 機房,Cloud Mac 按方案開通、映像可複製、到 GitHub 的網路更穩定,適合分散式 Flutter 團隊快速落地雙池拓撲。macOS 原生 Unix 環境對 Flutter、CocoaPods、Fastlane 開箱即用,無需額外虛擬化層。

若你正在規劃 Flutter 團隊的 iOS CI 遷移,從 2 台 VPSSpark Cloud Mac mini M4 起步是最小可行拓撲——立即了解方案,先跑通快池 PoC,再補 Archive 池發版。

限時特惠

Flutter CI 卡在 iOS?2 台 Cloud Mac 分池

macos-fast + macos-archive · PR 與發版隔離

返回首頁
限時優惠 點擊查看套餐