先看一個可驗證的資料點: GitHub Actions 的自託管 Runner 只有在在線、符合工作標籤與工作需求時,才會接收工作;因此「Mac 顯示空閒」不等於任務能被該節點執行。GitHub Actions 自託管 Runner 路由規則已說明這項路由邊界。
結論:iOS CI 構建超時怎麼查? 不要看到總時長變長就立即增加 Mac 節點。先按排隊、任務領取、依賴拉取、xcodebuild、模擬器、簽名與上傳階段定位;只有健康節點持續滿載、排隊隨並發增加,而單一任務耗時沒有明顯異常時,才進入 Mac 擴容或彈性租賃決策。
這篇適合以下讀者:
- 企業 IT 負責人:需要判斷新增 Mac 資源是否真的能改善 CI 超時。
- 平台工程負責人:需要建立從 Runner 狀態到建置階段的證據鏈。
- 研發效能或發布負責人:需要降低高峰期排隊與版本交付延遲。
[ SECTION_01 ] 先建立「超時發生在哪裡」的證據鏈
流水線總時長只能告訴團隊結果變差,不能說明原因。一次任務至少應拆成以下階段:
- 工作進入排隊。
- Mac Runner 領取任務。
- Git、Git LFS、Swift Package 或私有制品庫準備。
xcodebuild執行建置或測試。- 模擬器啟動及測試執行。
- Keychain、憑證與簽名。
- 制品上傳或發布服務回應。
每個階段都要保留開始時間、結束時間、退出狀態、重試次數及關聯工作編號。Apple 的 Xcode 命令列工具參考可用來核對 xcodebuild 的實際呼叫方式;Xcode 測試結果說明則可協助區分測試失敗與建置流程停滯。
建議把資料集中在一張「最小狀態表」:
| 欄位 | 要回答的問題 | 缺少時的風險 |
|---|---|---|
| 排隊開始與結束 | 任務等了多久才有機會執行? | 把排隊誤認為建置慢 |
| Runner 接單時間 | 哪一台 Mac 實際領取? | 無法確認路由是否生效 |
| 階段時間線 | 哪個步驟長時間沒有進度? | 只追查整機 CPU 或記憶體 |
| 錯誤與重試 | 是否是網路、憑證或服務回應問題? | 用增加節點掩蓋根因 |
[ SECTION_02 ] 先排除路由、權限與並發造成的假性容量不足
Mac Runner 空閒但工作仍然排隊,常見原因不是硬體不足,而是任務根本沒有被允許送到該節點。需要逐項核對:
- 工作流程中的標籤是否與 Runner 標籤完全匹配。
- Runner 是否屬於工作流程可使用的 Runner Group。
- 任務要求的作業系統、架構或工具是否存在。
- 工作流程是否被前置 Job、環境審批或人工閘門卡住。
- 並發設定是否讓相同分支或相同環境互相等待。
GitHub 的 自託管 Runner 標籤文件說明標籤如何參與路由;工作流程中的 Runner 使用方式則可用於核對工作需求與節點選擇。若流水線使用並發群組,也應參照 GitHub Actions 並發機制,確認等待是資源排隊,還是同一組工作被刻意序列化。
此階段的判斷重點是:只有在標籤正確、權限正常、健康節點確實接單後,隊列長度才具有容量判斷價值。
[ SECTION_03 ] 把依賴、快取與網路延遲從建置時間中分離
依賴準備經常看起來像 Mac 建置變慢。實際上,Git 伺服器、Git LFS、Swift Package、私有制品庫、代理或 Apple 服務的回應延遲,都可能使工作流程停在建置前。
排查時應把以下情況分開記錄:
- 首次拉取與快取命中後的差異。
- 依賴版本變動導致的重新解析。
- 私有制品庫回應慢、連線中斷或需要重試。
- Runner 到內部服務之間的網路路由與頻寬。
- 快取還原成功,但憑證或依賴版本仍然不正確。
快取只適合保存可以重建的資料。它不能修復私網不可達、權限失效、憑證錯誤或版本漂移。若依賴階段佔用主要時間,優先調整依賴拓撲、代理、快取鍵與重試策略;不要因為看到 Mac CPU 閒置,就直接增加 Mac 節點。
提醒: 新增 Mac 後仍然超時,並不矛盾。若所有節點都在等待同一個制品庫、簽名服務或發布端點,新增節點只會同時製造更多外部請求。
[ SECTION_04 ] 用 Xcode、模擬器與簽名日誌定位真正的建置瓶頸
當任務已經被 Mac Runner 領取,才進入本機資源與工具鏈診斷。建議依照故障表逐項驗證:
| 故障域 | 可驗證的證據 | 不應直接下的結論 |
|---|---|---|
xcodebuild |
命令列、Scheme、DerivedData 路徑及每段日誌 | 不能只看整體 CPU 使用率 |
| 模擬器 | 啟動時間、Runtime 是否存在、測試是否卡住 | 不能把測試失敗當成節點不足 |
| 磁碟 | 可用空間、DerivedData 增長、快取清理記錄 | 不能只用「硬碟快滿」解釋所有超時 |
| 記憶體 | 建置期間壓力、交換活動、同機任務數 | 不能以某個瞬間數值代表整段任務 |
| 簽名 | Keychain 身份、憑證存取、Provisioning Profile | 不能用共享節點擴容解決權限阻塞 |
| 上傳 | 制品大小、重試、發布端點回應 | 不能把上傳等待算成 Xcode 編譯慢 |
Apple 的 Xcode 建置系統文件可用來核對建置系統行為;若測試依賴模擬器,應參考 模擬器與實體裝置執行說明。簽名流程則要對照 Apple 分發簽名程式碼說明核實身份、憑證與簽名步驟。
生產簽名節點與普通 PR 建置節點也應分開評估。PR 任務可以接受較彈性的工具鏈;發布簽名需要更嚴格的 Keychain 權限、隔離及重啟後恢復驗證。簽名阻塞時,增加共享 Mac 通常不能解決根因。
[ SECTION_05 ] 用條件分支決定優化、加節點或採用彈性 Mac
可把每次診斷結果轉成以下決策工具。這是一套證據條件模型,不是以總時長猜測容量。
- 若排隊很長,但 Runner 沒有接單:先修正標籤、Runner Group、權限或並發設定,暫不增加 Mac。
- 若接單後依賴階段異常:先查 Git、Swift Package、私有制品庫、代理與快取,暫不把問題歸因於 Mac。
- 若單一
xcodebuild或測試任務在低並發下也變慢:先查專案、Xcode、DerivedData、模擬器與記憶體壓力,優先做建置優化。 - 若簽名階段固定阻塞:建立專用簽名節點或隔離簽名工作流,不以共享池盲目擴容。
- 若健康節點持續忙碌、排隊隨並發增加,且單任務時間穩定:進入固定 Mac 節點、共享建置池或彈性 Mac 的容量評估。
- 若工作量只在發布窗口或短期試點集中:先用按需遠端 Mac 驗證真實流水線,再決定是否長期採購。
容量模型只需要保留變數:
峰值並發 × 平均執行時長 → 所需有效產能
再加入可接受排隊時間、固定節點數、備用容量及故障恢復需求。沒有企業歷史資料時,不應填入虛構的並發、價格、性能或恢復時間。
[ SECTION_06 ] 擴容後用端到端驗收,而不是只看節點上線
新增固定 Mac 或接入遠端 Mac 後,至少要完成以下驗收步驟:
- 建立與正式工作流相同的 Runner 標籤及權限。
- 用一條真實 PR 或發布工作確認任務能被正確接單。
- 核對 Git、Swift Package、私有制品庫及快取行為一致。
- 分別驗證
xcodebuild、模擬器測試與 DerivedData。 - 使用非生產測試憑證驗證簽名流程,再進入正式簽名准入。
- 檢查制品上傳、失敗重試及日誌關聯。
- 重啟節點後重新執行任務,確認 Runner 可恢復且錯誤仍可追蹤。
驗收評分可以採用本文自訂的三級記錄:0 分代表沒有證據,1 分代表可執行但不穩定,2 分代表已用真實任務驗證。路由、依賴、建置、簽名與恢復五個領域都達到 2 分後,才適合把節點納入生產容量。
[ SECTION_07 ] 固定 Mac、共享池與彈性遠端 Mac的適用邊界
| 方案 | 適合的證據條件 | 主要限制 | 建議驗證項目 |
|---|---|---|---|
| 固定 Mac 建置節點 | 負載穩定、工具鏈固定、長期使用 | 閒置時仍承擔硬體與維運成本 | 利用率、重啟恢復、工具鏈一致性 |
| 共享建置池 | 多個團隊有錯峰需求,任務可標準化 | 標籤、快取與權限隔離較複雜 | 路由規則、並發、快取污染 |
| 專用簽名節點 | 發布任務需要固定 Keychain 與憑證邊界 | 不能完全取代普通 PR 節點 | 簽名權限、審計、憑證輪換 |
| 彈性遠端 Mac | 發布高峰、短期專案或容量試點 | 需驗證網路、依賴接入與交付流程 | 真實工作流、上傳、重啟與日誌 |
若需要先做短期驗證,可參考 NOVAKVM 的遠端 Mac 方案,但不應把產品可用性直接當成企業 CI 性能結論。企業應以自己的 Runner、依賴、簽名及發布流程完成驗收。
[ SECTION_08 ] 把排障結果轉成容量採購決策
目前的固定 Mac 方案,常見缺點是容量按峰值預留、低峰期產生閒置,且硬體故障、工具鏈升級與重啟恢復由企業自行承擔。若改用未經驗證的公有雲 Mac,也可能遇到網路接入、憑證隔離、快取持久性與節點路由不一致等問題。
更穩妥的做法,是先用一條真實流水線測試遠端 Mac:確認任務能接單、依賴能取得、Xcode 能完成建置、簽名邊界正確,並驗證節點重啟後仍能恢復。對只在發布窗口出現的短期高峰,NOVAKVM 的按需遠端 Mac 可作為較低承諾的 PoC 路徑;若是長期穩定且高負載的工作,則應把固定節點、專用簽名環境與正式維運責任一併納入 TCO 評估。需要時,可從 香港遠端 Mac 入口開始測試接入與流水線相容性。