Kubernetes 不能把 macOS 主機當成官方支援的原生 Worker Node;本週應先保留 Kubernetes 作為佇列與控制層,將 Xcode 任務路由到獨立 Mac 池,正式簽名再使用隔離的可信 Mac。這適合已有 Kubernetes 平台、需要共用 Apple 建構資源,並正在比較固定採購、按需租用或混合方案的企業。

如果你負責 Kubernetes 平台、iOS/macOS CI/CD 或建構資源預算,本文用場景軸拆開這個決策。你會看到哪些工作應留在 Linux Pod、哪些工作必須交給 Mac,以及如何驗收任務回傳、簽名隔離、重啟恢復和節點回收。

01

先分清 Kubernetes 的控制邊界

「Mac 上可以安裝 kubectl」和「Mac 可以成為生產 Worker」是兩件事。前者只代表命令列工具能連線 Kubernetes API;後者要求主機符合節點元件、容器執行環境、網路、儲存與生命週期管理條件。

Kubernetes 官方 Node 節點元件與管理文件 說明了節點在叢集中的角色與管理方式。官方另有 Windows 節點支援說明,但沒有提供 macOS 原生 Worker Node 的生產支援邊界。因此,不能把某個實驗性 macOS kubelet 專案或社群方案當成企業標準答案。

這裡要拆成三層:

  • 控制面統一:Kubernetes API、佇列、策略與觀測資料可以集中管理。
  • 任務調度統一:控制器可以決定哪個工作交給 Linux Pod,哪個工作交給 Mac Runner。
  • 執行環境統一:不能假設 Linux 容器和 macOS 主機擁有相同的 Apple SDK、Xcode 或 Simulator。

第三層正是容易出錯的地方。Xcode 命令列工具需要在 macOS 上安裝並選定 Xcode 環境;Apple 的 Xcode 命令列工具安裝文件 與 Xcode 自動化測試說明 都沒有把這些工作描述成可直接放入普通 Linux Pod 的工具鏈。

02

Linux Pod 先完成不依賴 Apple 工具鏈的工作

第一個場景是通用 CI。程式碼掃描、格式檢查、後端測試、一般腳本、依賴快取整理,以及不使用 Apple SDK 的共享模組測試,應先在 Kubernetes 叢集內完成。

這樣做有三個直接好處:

  • Linux 工作不會搶佔昂貴的 Mac 建構資源。
  • 通用測試可使用既有的 Pod 彈性與隔離策略。
  • Xcode 工作失敗時,可以區分是程式碼問題、制品問題,還是 Mac 環境問題。

跨環境交接不要只傳一個工作目錄。應建立明確的輸入、輸出和失敗契約:

交接項目 Kubernetes 端責任 Mac 端責任
任務識別 建立唯一工作 ID、提交版本與分支資訊 接收並原樣記錄工作 ID
輸入制品 產生鎖定版本的原始碼、依賴或前置制品 驗證雜湊與工作版本
建構指令 指定 scheme、configuration、測試範圍 在本機執行 Xcode 工具鏈
輸出制品 提供可寫入的位置與保留規則 回傳封裝檔、測試報告和日誌
失敗狀態 保存錯誤分類與重試政策 回報退出原因、環境錯誤與取消結果

你可以把這個交接契約放在 CI 平台的工作描述中,而不是把所有邏輯埋在 SSH 腳本裡。SSH 可以是傳輸通道,但不應成為唯一的狀態系統。

注意: 如果 Runner 斷線後,控制器只能看到「SSH 消失」,卻不知道建構是否已經產生制品,就無法安全重試。重試前必須核對工作 ID、制品完整性和 Mac 上的工作區狀態。

03

Xcode 建構任務路由至外部 Mac 池

當工作需要 xcodebuild、simctl、Apple SDK 或 Simulator,就不應再嘗試把它偽裝成一般 Pod。Kubernetes 可以管理工作請求和期望狀態,但實際執行者應是安裝 Xcode 的 Mac 主機。

企業常見的連線方式有三種。

CI Runner 作為佇列消費者

CI 平台把標記為 Apple 平台的工作放入專用佇列。Mac Runner 主動取工作,在本機執行,再把結果回傳。

優點是落地快,容易沿用既有的重試、日誌和制品機制。缺點是 Mac 池的健康檢查、註冊、排空和回收,仍要由平台團隊自行管理。

自訂佇列服務

Kubernetes 工作先產生一個外部任務,佇列服務再把工作交給可用的 Mac Runner。這適合需要跨多個 CI 平台,或要自行控制優先級、租期與資源配額的企業。

風險是狀態模型較複雜。你至少要處理重複提交、任務逾時、Runner 重啟、取消傳播與制品回收。

Custom Resource 與 Controller

Kubernetes Custom Resource 可以描述一個外部 Mac 工作,例如目標 Runner、Xcode 工具鏈、建構參數和回傳位置。Controller 再根據資源狀態呼叫外部服務。

這不是 Kubernetes 原生提供的 Mac 調度能力。它只是把企業自訂的外部資源控制邏輯接入 Kubernetes API。若採用這條路線,應參考 Operator 模式文件,同時自行負責 Mac 節點註冊、健康狀態與清理流程。

04

正式簽名必須另設可信 Mac

普通建構池可以共用,不代表正式簽名節點也應共用。Developer ID、發佈憑證、程式碼簽名私密金鑰、Keychain 與發佈 API 憑證,應使用更窄的任務授權和更清晰的節點邊界。Apple 的 Mac 發佈簽名文件 可作為簽名流程的官方參考,但不會替你設計企業內部的權限隔離。

建議採用三段式路徑:

  1. Kubernetes 完成程式碼檢查、通用測試與依賴驗證。
  2. 普通 Mac 建構池完成未簽名建構、Simulator 測試與封裝前處理。
  3. 可信 Mac 執行正式簽名、發佈封裝與上架前動作。

每段都要留下可審計資料:

  • 哪個工作 ID 獲得簽名授權。
  • 哪個 Runner 執行過簽名。
  • 憑證或金鑰如何進入 Keychain。
  • 工作完成後工作區和暫存檔是否清除。
  • 失敗或取消後,制品、憑證和待處理任務如何撤銷。
  • 發佈制品的雜湊、版本與審批記錄是否可追溯。

不要讓普通建構 Runner 同時持有長期發佈憑證。也不要把簽名金鑰放進 Kubernetes Secret 後,讓所有可建立 Pod 的角色都能讀取。

05

高峰發布與故障恢復採用彈性 Mac

固定 Mac 池適合長期穩定的基礎負載。短期遠端 Mac 則適合版本發布、高峰回歸、遷移試點或固定節點故障時的替補。容量不能從開發者人數直接推算,因為真正佔用資源的是佇列深度、單台主機的有效產能、環境交付時間和冗餘要求。

你可以按以下方式判斷:

  • 若高峰佇列持續增加,先檢查任務是否被錯誤路由,而不是立即購買更多 Mac。
  • 若普通建構排隊,但簽名任務不多,應擴大普通池,不應複製整個可信簽名池。
  • 若版本發布造成短時間併發,優先評估彈性遠端 Mac,而不是全年維持最高固定容量。
  • 若固定 Mac 故障後沒有替補,至少要驗證外部 Mac 的環境交付、Runner 註冊和工作區清理。

新 Mac 不應直接加入生產佇列。接入前要完成以下五個步驟:

  1. 套用已核准的 macOS、Xcode、命令列工具與系統設定基線。
  2. 註冊 CI Runner,確認標籤、權限、工作目錄和制品路徑。
  3. 執行真實的 Xcode 建構、Simulator 測試與失敗案例。
  4. 模擬 Runner 重啟、網路中斷、任務取消和重複回報。
  5. 完成工作區、暫存檔、快取與憑證清理,再允許接收正式工作。
06

以條件分支決定固定、租用或混合資源

先不要問「企業應該買幾台 Mac」。先問你的工作負載屬於哪個場景。

  • 若團隊沒有 Xcode、Apple SDK 或 Simulator 任務,選擇純 Kubernetes。Mac 資源只會增加管理面,沒有必要為了統一控制面而加入。
  • 若 Xcode 建構量穩定、工具鏈變更少,且需要長時間保留快取,選擇 Kubernetes 加固定 Mac 池。
  • 若多個專案在發布期同時增加回歸和封裝任務,固定池加彈性遠端 Mac 更合理。
  • 若簽名憑證的隔離要求高於普通建構,無論容量大小,都把可信 Mac 從普通池分開。
  • 若你還沒有掌握任務路由和回收流程,先用短週期遠端 Mac 做非生產 PoC;驗證失敗就回到人工佇列或固定 Runner,不要直接接入發布流程。

下面這張表適合放在架構評審會議中,先確認場景,再討論採購方式:

工作場景 Kubernetes 的角色 Mac 資源形態 生產准入重點
程式碼檢查、後端測試、通用腳本 直接執行 Pod 不需要 Mac Pod 隔離、制品與日誌
Xcode 建構與 Simulator 建立任務、保存狀態 普通 Mac 建構池 工具鏈一致、Runner 健康
正式簽名與發佈 審批、授權、追蹤 獨立可信 Mac 憑證隔離、審計、清理
發布高峰與回歸 排程與容量控制 固定池加彈性遠端 Mac 交付、註冊、回收、失敗重試
故障替補 健康狀態與重新派工 預備或短期 Mac 重啟恢復、制品一致性

如果你正在評估實體採購與遠端資源,可先閱讀 Mac mini 雲端訂購方案 和 Mac mini 租用價格資訊。重點不是單看月費,而是把硬體折舊、維修替換、機房位置、遠端管理和高峰閒置一併放進總成本。

第二張表用來決定架構成熟度:

驗收面向 未通過時的表現 通過條件 對資源決策的影響
任務路由 Xcode 工作落到 Linux Pod Apple 工具鏈工作穩定進入 Mac 池 可評估固定池
狀態回傳 只能看到 SSH 中斷 狀態、日誌、制品和錯誤原因可追蹤 才能安全重試
環境一致性 Runner 工具鏈各自漂移 基線、版本與工作區規則一致 才能比較建構結果
簽名隔離 普通 Runner 可讀取發佈憑證 可信 Mac、授權和審計分離 決定是否可接正式發布
重啟恢復 主機重啟後任務遺失 可排空、重派工或明確失敗 影響冗餘與替補容量
節點回收 快取、暫存檔或憑證殘留 回收後能通過清理檢查 決定能否採用彈性遠端 Mac
07

常見決策問題

macOS 作為 Kubernetes 節點的邊界

macOS 不能因為安裝了 kubectl 就成為官方支援的 Kubernetes Worker。你的控制面可以統一,但建構執行環境不必、也不能因此統一。需要 Xcode 的工作應透過 Runner 或外部控制器路由到 Mac,而不是把非官方節點方案直接放入生產。

外部 Mac 的任務執行方式

Kubernetes 應負責佇列、期望狀態和工作識別,外部 Mac 負責執行 Apple 工具鏈。Runner、佇列消費者或 Controller 都可以實現這條連線,但必須回傳結構化狀態、日誌、制品位置和健康結果。單純 SSH 背景執行不適合需要審計和重試的企業流程。

iOS CI 的集群分工

通用測試可留在 Kubernetes,Xcode 建構、Simulator、Apple SDK 和封裝任務則交給 Mac。這種分工能避免 Linux Pod 與 Mac 主機之間的環境誤判,也能讓普通建構和正式簽名使用不同的安全邊界。

Mac Runner 的狀態契約

狀態回傳至少要能區分排隊、已接收、執行中、成功、失敗和取消。任務 ID、提交版本、制品雜湊、錯誤原因與 Runner 狀態應分開保存。遇到斷線時,控制器先查詢工作和制品狀態,再決定重試,不能把連線中斷直接當成建構失敗。

Mac 池容量的計算方式

企業不應按開發者人數直接購買 Mac。應以排隊任務、單節點有效產能、交付時間和冗餘要求建立容量模型。穩定負載使用固定池;發布高峰、遷移試點和災備需求,則可加入短期遠端 Mac,並先完成真實工作驗收。

對大多數已有 Kubernetes 的企業,最穩妥的路徑不是強行尋找 macOS 原生 Worker,而是先讓 Kubernetes 管理控制流,再讓獨立 Mac 處理 Apple 執行流。你目前若只使用 Kubernetes-only 方案,會遇到 Apple 工具鏈無法直接執行、SSH 任務狀態不完整,以及固定容量難以應對發布高峰等問題;若自行採購 Mac,還要承擔閒置、維修、替換和遠端管理成本。

更實際的做法,是先選一條非生產 Xcode 工作流,用短週期的 VpsMesh 遠端 Mac 驗證任務路由、環境交付、狀態回傳、重啟恢復和節點回收。驗證結果穩定後,再決定哪些容量值得固定採購,哪些高峰容量交給租用方案;你可以從 VpsMesh 的 Mac 遠端資源頁面 開始核對適合 PoC 的交付方式。