限時優惠

xcodebuild 為什麼比本機慢 2–3 倍?

CI Flutter iOS CI
2026-06-08 約 12 分鐘閱讀

這篇文章用繁體實戰語境整理 Flutter iOS 團隊最常遇到的 CI 速度落差,並提供租用 Mac 與自託管 Runner 的決策線索。

Flutter iOS CI 本機與 CI 時間落差示意圖
本機 Release 看似正常,但在 GitHub Actions 因為無狀態執行環境,xcodebuild 前後置流程常把總耗時拉到 2-3 倍。

先看五個重點

  1. 慢的不只 xcodebuild,本質是快取生命週期與機器生命週期不一致。
  2. GitHub Actions 託管機像每次重灌;自託管 Runner 更像長期維運的工作站。
  3. DerivedData 與 CocoaPods 一旦可持久化,通常就能拿回最大一段時間。
  4. 磁碟與簽章流程常偽裝成「編譯慢」,必須拆階段量測。
  5. Flutter 團隊可先用租用 Mac 做 48 小時壓測,再決定月租自託管 Runner。

本機 Mac vs GitHub Actions:多出來的時間去哪了?

很多人抱怨 CI 慢,其實對比常常不公平。本機開發多半是這樣:

  • 本機:持久 SSD + 熱快取;CI:短生命週期 VM + 冷依賴。
  • 本機:同一使用者簽章環境;CI:每次重建 keychain。
  • 本機:同機反覆建置;CI:分支切換導致快取命中率下降。

CI 流水線的典型做法則是:

  • 每次 checkout 一份乾淨的工作區
  • flutter build ipaxcodebuild archive,走 Release
  • 託管 Runner 快取往往存不住,等於每次從零編。

很多團隊只拿 `xcodebuild archive` 做比較,忽略 checkout、pod install、keychain 建置、DerivedData 失效等隱性成本。這些成本疊在一起,就會形成你感受到的 2-3 倍差距。

xcodebuild 在 CI 變慢的五個原因

以下模式在 Flutter iOS 專案很常見,無論你現在用 GitHub Actions 或準備轉自託管 Runner 都適用。

  1. 1) 無狀態 Runner 每次重置依賴層 GitHub 託管機每次 job 都是新環境,SPM、Pods、模組快取很難真正熱起來。即使有 actions/cache,也常因 key 漂移導致命中不穩。
  2. 2) DerivedData 路徑不固定讓增量編譯失效 若每次都用暫存路徑,xcodebuild 會把可重用的編譯成果視為無效,重編譯比你想像多。
  3. 3) Flutter 外掛連動 Pod 解析造成冷啟成本 外掛版本波動會觸發 pod 解析與下載;若沒鎖定 lockfile 與 deployment 模式,`pod install` 很容易多花數分鐘。
  4. 4) 簽章與 keychain 流程是固定額外開銷 憑證匯入、keychain 建立、解鎖流程在 CI 需要每次重做,本機通常是常駐狀態。
  5. 5) 磁碟與 inode 壓力會讓寫入吞吐變慢 只看 CPU 會漏掉真因。SSD 可用空間、快取碎片、inode 壓力都會拖慢 index 與輸出。

階段耗時表(Flutter iOS 範例)

先用這種表格量測 10 次以上,再決定要不要換平台或重寫 workflow。

階段 本機 Mac GitHub Actions 常見原因
flutter pub get0m25s1m10spub 快取不夠熱
pod install1m30s4m45sSpecs 與 plugin pod 冷解析
xcodebuild compile/archive6m40s12m20sDerivedData 無法重用
codesign/export0m55s2m00s每次重建 keychain
總計9m30s20m15s約慢 2.1 倍

先別急著怪 xcodebuild

不少團隊盯著 xcodebuild,卻忽略前面的 pod install。CocoaPods 在 CI 裡慢,通常就三個原因:

  • 沒提交 Podfile.lock——每次重新算依賴,時間不可控。
  • 沒快取 ios/Pods 目錄——託管 Runner 跑完就刪,下次又從 CDN 拉。
  • 多跑了 pod repo update——CI 裡一般不需要,鎖檔已釘死版本。

改法很具體:CI 用 pod install --deployment,並把 Pods 目錄快取在同一台機器的 SSD 上。自託管 Mac mini 比 GitHub 託管 Runner 省事——快取直接落本機,不必先打包上傳再下載。

DerivedData、Pods 與快取的實戰架構

對 Flutter iOS CI 來說,最有效的改動通常是固定 DerivedData 絕對路徑,並讓 Pods 快取可預測。若使用自託管 Runner,CI 就從一次性機器變成可持續優化的建置節點。

即使暫時留在 GitHub 託管機,也要把路徑與 lockfile 做到可重現,讓快取還原真的發揮作用。若能遷移,自託管 Runner 在 Apple Silicon 上通常可把熱路徑表現拉近本機。

  • 每個 target 使用固定 derivedDataPath,避免臨時目錄。
  • 使用 `pod install --deployment` 控制 lockfile 漂移。
  • 把磁碟可用量與 inode 當成 CI 核心指標。

修復模式:固定路徑 + 可預測依賴還原

下面範例刻意保持簡單,重點放在可維護性,而非華麗 YAML 技巧。

  1. workflow 加 concurrency——同一分支新 push 取消舊構建,少排隊。
  2. 測試與分析放 Linux——flutter test 別佔 Mac 時間。
  3. 持久化 Pods + DerivedData——自託管固定路徑;託管用 actions/cache 並縮小 key 粒度。
  4. CI 只編要發的 flavor——別在流水線裡掃 6 個 flavor 卻只上傳 1 個包。
  5. 固定 Xcode / Flutter 版本——別在 CI 裡 brew upgradeflutter upgrade
# .github/workflows/ios-ci.yml
jobs:
  ios:
    runs-on: [self-hosted, macOS, ARM64, flutter-ios]
    steps:
      - uses: actions/checkout@v4
      - name: Restore caches
        run: |
          mkdir -p "$HOME/Library/Caches/CocoaPods"
          mkdir -p "$HOME/.pub-cache"
      - name: Build with stable DerivedData path
        run: |
          flutter pub get
          cd ios
          xcodebuild             -workspace Runner.xcworkspace             -scheme Runner             -configuration Release             -destination 'generic/platform=iOS'             -derivedDataPath "$HOME/ci-derived/runner"             CODE_SIGNING_ALLOWED=NO
      - name: Keep pod install deterministic
        run: |
          cd ios
          pod install --deployment

這個基線通常可先拿回 30%-50% 時間。規模更大時,再搭配分池自託管 Runner 與分支感知快取 key。

合併前的效能檢查清單

Flutter 或 Xcode 升版後,只要 CI 變慢就跑一次。

  • ☐ 每次 CI 都跑 Release / IPA,本機對比的是 Debug
  • ☐ Job 裡寫過 flutter clean 或刪 DerivedData
  • pod install 日誌裡每次大量 Installing
  • ☐ 使用 GitHub 託管 Runner,沒用本機 SSD 快取
  • ☐ 16GB 機器同時跑多條 Job 或模擬器
  • ☐ 改 README 也觸發了 iOS 全量構建
  • ☐ Xcode 版本與本機不一致

勾了前三條,優先修快取;勾了後兩條,優先修觸發條件與機器規格。磁碟滿了也會越編越慢,可參考 Runner 磁碟清理文。

常見問題

GitHub Actions 一定比本機慢嗎?

不一定,但 Flutter iOS 流水線在快取無法升溫時,確實常出現顯著落差。自託管 Runner 通常能縮小差距。

要不要直接轉自託管 Runner?

先量測。若耗時表顯示固定冷啟成本,再挑一條 workflow 做 A/B 比較,連續 10 次中位數更有說服力。

一定要買高規硬體才有改善嗎?

通常不用。穩定快取的 Apple Silicon 租用 Mac,在 iOS 建置上常比無狀態大機更有效率。

怎麼說服團隊投入這件事?

把每階段省下的分鐘換算成等待成本,再加上發版可預測性提升,決策會更清楚。

若你正在重整 Flutter iOS CI,建議依序閱讀:

想要穩定、可預測的 iOS CI 速度?

kvmboot 提供 Apple Silicon 雲端 Mac,適合 Flutter iOS 流水線。可先租用 Mac 日租壓測,再轉月租自託管 Runner。

若你想把 CI 從「每天碰運氣」變成可控系統, 查看雲端 Mac 方案