本週先做什麼:先建立主備雙軌,再談高可用
Mac 打包伺服器高可用不應從「把唯一節點保持在線」開始,而應從主用節點加可驗證備用節點開始。本週建議先挑一條真實發布流水線,停止主節點接單,讓備用節點完成一次編譯、測試、簽名與產物驗證;未完成這次演練前,不要把單台 Mac 宣稱為具備業務連續性。
這套方法適合管理單台或少量 Mac 構建節點、需要確保 iOS CI/CD 可用性的企業 IT 與研發效能團隊,也適合正在審核災備預算、SLA 和 Mac 算力採購方案的技術總監或 CTO。
單機在線 vs 主備可切換:先定義真正的故障邊界
發布窗口中,唯一 Mac 節點突然離線。流水線仍然存在,工作也仍然排隊,但沒有可接工作的構建資源。這說明「節點在線」不等於「發布流程可恢復」。
我們會先分開三個變數:
- 恢復時間目標:從停止主節點接單,到備用節點能產出可驗證構建物,允許經過多久。
- 可接受的構建狀態損失:故障前已完成的工作、快取或中間產物,哪些可以重跑,哪些不能遺失。
- 發布阻斷範圍:只影響單一專案、整個團隊,還是所有需要 iOS 簽名的生產流程。
單節點適合低風險、可人工重跑的工作。冷備適合成本敏感、可以接受較多人工介入的團隊。溫備則要求備用機已完成 Runner、工具鏈和憑證恢復測試。雙活能分散隊列,但也會增加環境漂移、簽名治理和容量管理的複雜度。
多數企業不需要一開始就做雙活。先把「主用線穩定運行、備用線可真實接管」做成可重複演練的雙軌方案,通常更容易驗收。
| 架構選項 | 適用情境 | 主要風險 | 驗收重點 |
|---|---|---|---|
| 單節點 | 非生產構建、可完全重跑 | 主機故障即停止發布 | 是否有人工重建流程 |
| 冷備 | 低頻發布、預算受限 | 備用環境容易過時 | 臨時啟動後能否完成全流程 |
| 溫備 | 生產發布、需要可預測切換 | 需要持續維護版本基線 | Runner、Xcode、簽名和依賴均可用 |
| 雙活 | 高並發、跨團隊共享 | 環境差異與路由更複雜 | 任務分流、產物一致性和憑證隔離 |
發布中斷:備用節點不是「有一台空機」就夠
Mac 打包伺服器宕機後,切換順序應固定,不能由值班人員臨場猜測。
第一步:停止主節點接單
先在 CI 平台暫停或隔離主節點,避免新任務繼續送入故障主機。已經執行中的工作要標記為可重試、需人工判定或不可重試,不能把所有失敗都直接重跑。
以 GitHub Actions 為例,自託管 Runner 會根據 runs-on 的標籤和群組選擇符合條件的在線節點;找不到在線且閒置的匹配節點時,工作會維持排隊,超過官方規則的保留時間後可能失敗。(docs.github.com)
使用 GitLab 時,則要檢查 Runner 標籤、保護分支和工作是否仍處於 pending。工作只有在找到匹配標籤的 Runner 後才會執行;標籤配置不一致,備用機在線也可能接不到任務。(docs.gitlab.com)
第二步:確認備用節點的可接管狀態
至少核對以下項目:
- Runner 服務已啟動,且出現在 CI 平台的在線清單。
- 備用節點具備正確的 Apple Silicon 架構標籤或平台標籤。
- Xcode、SDK、命令列工具和專案建置腳本已安裝。
- 私有套件、私有 Git 儲存庫和快取來源可連線。
- Keychain、Provisioning Profile 和簽名私鑰能被流水線使用。
- 構建產物能上傳到原本的儲存位置。
第三步:只用真實專案驗證
不要以 xcodebuild -version 或 Runner 心跳作為完整驗收。這些只能證明工具存在,不能證明專案能簽名、依賴能取得或產物能發布。
我們會以一條生產相同的分支或發布候選版本執行:
- 取得指定 commit。
- 使用鎖定的 Swift Package 依賴。
- 執行測試與 Archive。
- 驗證簽名與 entitlements。
- 上傳構建產物。
- 保存完整日誌和產物雜湊。
維護升級:穩定主線 vs 真實驗證備線
系統更新、Xcode 升級和依賴調整不應只靠安排維護時段解決。升級後即使能成功編譯,也可能在簽名、測試裝置、私有套件或發布上傳階段失敗。
我們建議使用雙軌方法:
- 主用線:維持目前已通過發布驗證的 macOS、Xcode、SDK 和依賴版本。
- 備用線:先安裝候選版本,使用真實專案完成編譯、測試、Archive 和發布前驗證。
- 切換條件:只有當備用線通過基線檢查,才允許它接收生產發布任務。
- 回退條件:任何簽名、私有依賴或產物上傳異常,都回退到主用線,不在發布窗口臨時修環境。
Xcode 會把 Swift Package 的確切版本寫入 Package.resolved。Apple 官方建議將該檔案提交到 Git 儲存庫,並在直接使用 xcodebuild 時考慮停用自動解析,避免備用節點自行取得不同依賴版本。私有套件則需要配置 SSH 憑證及 known_hosts。(developer.apple.com)
環境基線不應只寫在 Wiki。建議放入儲存庫或可審計的配置目錄:
| 基線項目 | 主用線 | 備用線 | 驗證證據 |
|---|---|---|---|
| macOS | 已核准版本 | 候選或同版本 | 系統版本輸出 |
| Xcode | 生產版本 | 已完成實測的版本 | xcodebuild -version 與構建日誌 |
| SDK | 專案要求 | 與主用線一致或已核准 | Archive 日誌 |
| Swift Package | Package.resolved |
同一提交內容 | 依賴解析紀錄 |
| 建置設定 | .xcconfig、腳本 |
同一版本 | Git commit 或版本標籤 |
| Apple Silicon | 已確認架構 | 相同或明確標記差異 | Runner 標籤與構建輸出 |
| 簽名資產 | 憑證、私鑰、Profile | 受控恢復 | Archive、簽名和 entitlements 證據 |
Xcode 的 .xcconfig 可將建置設定以純文字管理,適合把 Debug、Release、架構和平台差異放入版本控制,而不是依賴某台 Mac 的圖形介面狀態。(developer.apple.com)
如需進一步管理兩套 Xcode 工具鏈,可參考這份Xcode 雙版本構建環境管理方案,重點不是安裝兩個版本,而是明確記錄每條流水線允許使用哪一套工具鏈。
主機無回應:遠程重啟不等於恢復完成
掉電、系統無回應、磁碟加密等待解鎖和遠程連線中斷,處置方式不同。
Apple 支援透過 SSH 對遠程 Mac 執行重啟命令,也支援設定電源故障後自動開機;但這只解決「主機重新啟動」問題,不能直接證明 Runner、Keychain 和 CI 工作已恢復。(support.apple.com)
我們會在備用節點預先完成以下測試:
- 透過 SSH 或既定遠程管理通道登入。
- 執行重啟,確認主機可回到可管理狀態。
- 確認 Runner 服務在啟動後自動恢復。
- 確認磁碟加密的解鎖路徑由責任人掌握。
- 確認重啟後 Keychain 可在受控流程中使用。
- 執行一個不涉及生產發布的測試構建。
- 記錄需要人工輸入、人工批准或人工確認的步驟。
FileVault 使磁碟解鎖成為災備流程的一部分。Apple 文件指出,Apple Silicon Mac 的 FileVault 金鑰處理與 Secure Enclave 有關;在受管理環境中,組織可使用個人復原金鑰並交由裝置管理服務託管。Apple 亦說明,在 Apple Silicon Mac 搭配 macOS 26 或更新版本的條件下,若已啟用遠程存取且網路可用,存在透過 SSH 解鎖 FileVault 的恢復能力,但企業仍應逐台核對版本與管理條件,不能把它當作所有 Mac 的通用保證。(support.apple.com)
驗收證據至少包括:
| 故障動作 | 必須保存的證據 | 不合格訊號 |
|---|---|---|
| 停止主節點接單 | CI 平台狀態、時間戳記 | 新工作仍送入主節點 |
| 啟用備用節點 | Runner 狀態、標籤、服務日誌 | 節點在線但無法接任務 |
| 重啟主機 | SSH、系統和 Runner 日誌 | 必須現場登入才能恢復 |
| 測試構建 | 測試結果、Archive 和簽名輸出 | 只顯示成功編譯 |
| 恢復發布 | 產物、雜湊、上傳紀錄 | 產物留在本機,沒有可追溯紀錄 |
| 演練結束 | 人工介入點、失敗原因 | 沒有人知道下一次如何重做 |
發布高峰:隊列變長不一定代表要升級硬體
容量規劃不能靠一次跑分。應觀察一段時間內的:
- 隊列等待時間趨勢。
- 同時執行的任務數。
- 每類工作實際構建時間。
- 測試、Archive、簽名和上傳各階段耗時。
- 重試率及因資源不足造成的失敗。
- 發布窗口與日常開發工作的重疊程度。
如果只有隊列等待增加,而單一任務耗時穩定,問題可能是節點數量不足。如果單一任務本身持續變慢,才需要檢查磁碟空間、記憶體壓力、依賴下載、測試並發或工具鏈變更。
固定基礎負載可以使用長期保留的 Mac 節點。短期發布高峰、Xcode 升級窗口或臨時專案,則可考慮按周或按月增加遠程 Mac 容量。成本模型只需要先列出:
總成本 = 基礎容量成本 + 峰值容量成本 + 運維工時成本 + 故障造成的重跑與延誤成本
不要先填入未核實的價格或節省比例。先把每種方案的使用週期、峰值頻率、需要維護的節點數和可接受人工介入次數列出,再由實際演練結果決定冷備、溫備或按需擴容。
若團隊正在處理隊列等待和節點數量問題,可先閱讀企業 iOS CI/CD 容量規劃方法,把需求拆成基礎容量與峰值容量,而不是按開發者人數直接購買 Mac。
不同 CI 平台的任務路由不能互相套用。GitHub Actions 依賴 Runner 標籤和群組;GitLab 則以 Runner 標籤、工作設定及保護條件匹配。GitLab Runner 的 concurrent、limit 等設定還會影響同一 Runner 可處理的工作數,因此容量測試必須連同平台路由設定一起記錄。(docs.github.com)
簽名與依賴異常:能編譯不代表災備成功
最容易被忽略的情況,是備用節點能完成編譯,卻不能完成發布。
常見原因包括:
- 憑證只有公鑰,私鑰沒有進入正確 Keychain。
- Provisioning Profile 已過期或不匹配 App ID。
- Apple 帳戶權限未恢復。
- 私有 Git 儲存庫需要 SSH 憑證。
known_hosts不存在,依賴取得被拒絕。- CI 使用的使用者與手動測試使用者不同。
- Keychain 在非互動工作階段中沒有解鎖。
- 依賴快取只存在主節點本地磁碟。
Apple 官方文件指出,使用外部建置系統時,需要自行管理程式碼簽名身份;簽名設定中的憑證、Team ID 和 Provisioning Profile 必須在構建環境中有效,缺少或無效的簽名憑證會導致構建錯誤。(developer.apple.com)
我們會建立一份責任矩陣:
- 憑證與私鑰:安全或 Apple 開發者帳戶管理人。
- Provisioning Profile:發布工程師或指定 CI 管理人。
- Apple 帳戶權限:最小權限原則下的帳戶管理人。
- SSH 憑證:儲存庫管理人或平台工程團隊。
- Keychain:Mac 節點管理人。
- 撤銷與輪換:安全負責人批准,值班工程師執行。
不要用複製整個使用者目錄的方式同步主備。這會把瀏覽器資料、快取、歷史憑證和不必要的個人資料一起帶入備用機,也很難審計。應只分發流水線需要的資產,記錄版本、接收者、用途、失效時間和撤銷路徑。
第一次演練:用一條真實發布線完成七個驗收動作
我們建議將演練安排在非發布窗口,但使用與生產相同的程式碼、簽名模式和構建腳本。
第一步:建立演練基線
記錄主用節點與備用節點的 macOS、Xcode、SDK、架構、依賴提交版本、Runner 標籤和簽名資產狀態。
第二步:封鎖主節點新任務
暫停主節點接單,保留現有工作紀錄。確認 CI 平台沒有把新任務錯誤地送到主節點。
第三步:啟用備用節點
確認 Runner 在線、標籤匹配、服務已啟動,並檢查工作目錄與快取策略。
第四步:執行依賴恢復
使用專案鎖定的依賴版本。驗證私有套件、SSH、代理和憑證存取。
第五步:執行測試與 Archive
不要只跑最短的單元測試。至少選擇一條接近生產發布的測試、Archive 和簽名流程。
第六步:核對構建物
保存 .xcarchive、IPA 或其他實際產物,以及簽名檢查、版本號、commit 和雜湊紀錄。
第七步:回切並整理證據
恢復主節點接單,停止備用節點的生產路由,記錄每一步耗時、失敗原因和人工介入。下一次演練應能按照這份紀錄重做,而不是依賴某位工程師記憶。
准入前清單:
- [ ] 主節點停止接單後,沒有新工作遺漏。
- [ ] 備用節點能被 CI 平台正確匹配。
- [ ] Xcode 和 SDK 與發布基線一致。
- [ ]
Package.resolved或等效依賴鎖定檔已提交。 - [ ] 私有套件能在非互動工作階段取得。
- [ ] 簽名憑證、私鑰和 Profile 均能使用。
- [ ] FileVault 和重啟後恢復路徑已測試。
- [ ] 真實專案能產出可驗證構建物。
- [ ] 所有人工介入點有責任人。
- [ ] 演練紀錄可供審計及下次重做。
企業 FAQ:把長尾問題變成准入條件
備用節點需要準備幾台?
沒有固定台數。生產發布不能接受單點故障時,至少要有一台主用節點和一台已驗證可接管的備用節點;當隊列和並發持續增加,再按工作負載增加容量。
兩台 Mac 怎樣保持環境一致?
把 Xcode、SDK、建置設定、腳本和 Swift Package 鎖定檔納入版本控制。不要只依靠磁碟複製或人工記憶;備用節點必須定期執行真實專案驗證。
簽名憑證應由誰保管?
由指定安全或平台負責人管理。憑證、私鑰、Profile、Apple 權限和撤銷流程要分開記錄,並限制能接觸簽名資產的帳戶與工作。
遠程重啟成功後,是否代表節點已恢復?
不是。重啟只代表作業系統重新啟動。還要檢查遠程連線、Runner、Keychain、依賴取得、測試、Archive 和產物上傳。
SLA 應該寫什麼?
不要只寫「節點在線」。SLA 應區分可用性、切換責任、支援範圍、人工介入和產物恢復條件,並以演練結果核對,而不是先假設某個恢復時間。
高峰期應增加常駐節點還是臨時容量?
先看高峰發生頻率與持續時間。固定、持續的基礎負載適合長期節點;短期發布高峰或升級窗口,按需增加遠程 Mac 通常更容易控制容量與運維負擔。
最後的採購判斷:先演練,再決定買多少台
如果現有方案是單台機房 Mac,真正的缺點通常不是「算力一定不夠」,而是故障時沒有第二條發布路徑、升級時只能停機等待,而且簽名與依賴狀態容易綁定在某一台主機上。若再加上硬體採購週期、閒置容量和現場維護,企業很難只靠增加單機規格解決業務連續性。
對於無法長期保留備用 Mac 的團隊,MESHLAUNCH 的遠程 Mac 可作為按周或按月交付的臨時災備節點。更穩妥的做法不是先承諾它一定適合,而是用一條真實 iOS CI/CD 流水線驗證環境交付、Runner 接管、簽名恢復、重啟後服務和峰值構建;驗證結果合格,再決定長期採購、溫備保留或按需擴容。相關節點資訊可先從遠程 Mac 企業方案開始核對。