本週先不要增加 Mac Runner 節點:先把故障分成「沒有符合標籤與組別規則的 Runner」、「符合條件但全部忙碌」和「介面顯示線上卻無法領取任務」三類,再按路由、占用、服務的順序修復;只有修正後仍有穩定隊列積壓,才考慮增加遠端 Mac 容量或拆分任務池。

排查時段 建議動作 可接受的判斷結果
今天第一輪 凍結 YAML、runs-on、Runner 標籤、Runner Group 與倉庫授權快照 能指出第一個可驗證的排隊原因
今天第二輪 執行最小診斷 Job,檢查節點是否真的領取任務 路由、執行、產物回傳三者可分開判定
本週收尾 以近似生產的 Xcode 任務重測,才決定修復、重建或擴容 擴容決定有隊列證據,而不是只看介面狀態

這篇內容適合使用 GitHub Actions 自託管 Mac 執行 Xcode 建置、卻發現任務長時間排隊的行動開發者;也適合負責 Runner 標籤、Runner Group、倉庫存取範圍和節點常駐服務的 DevOps 工程師。若需要判斷現有遠端 Mac 應修復、重新註冊還是擴充,以下流程可作為變更前後的紀錄基準。

01

先把「排隊」還原成可驗證的狀態

Runner 管理頁顯示 Idle,但目標 Job 仍停在 queued,不代表一定是節點數量不足。GitHub Actions 可能仍在等待工作流前置條件、人工核准、並發名額,或等待符合路由條件的自託管 Runner。GitHub 官方的 選擇 Job 執行 Runner 說明 和自託管 Runner 路由參考 都應作為判讀依據。

先開啟該次工作流的執行記錄,記下:

  • Job 的實際 runs-on 內容,而不是只看工作流檔案的預設值。
  • Job 註釋、環境核准、依賴 Job 是否已完成。
  • 倉庫或組織是否設有並發限制;並發控制會影響同一組工作流是否能同時進入執行,詳見 GitHub Actions 並發控制文件。
  • Runner 管理頁的狀態、最後回報時間、標籤和所屬組別。

先把這些內容保存成快照,再修改任何 YAML、標籤、權限或服務。否則排查期間的變更會把原始證據覆蓋,最後很難分辨是路由本來就錯,還是修改造成新問題。

02

第一層:檢查 runs-on 與標籤路由

runs-on 若指定多個標籤,節點必須同時符合全部條件。常見故障包括把 arm64 寫成另一種拼法、重建 Mac 後遺失自訂的 xcode 標籤,或把工作流使用的標籤放在另一個 Runner 上。官方的自託管 Runner 標籤文件說明了標籤如何套用與用於路由。

例如,工作流可能寫成:

jobs:
  build:
    runs-on: [self-hosted, macOS, arm64, xcode]
    steps:
      - run: uname -m
      - run: sw_vers
      - run: xcodebuild -version

這裡不是「有一個標籤相同就可以」,而是 self-hosted、macOS、arm64 和 xcode 都要符合。先在 Runner 管理頁逐項抄錄實際標籤,再與 YAML 逐字比較,包含大小寫、連字符和底線。

診斷時可建立只輸出環境資訊的短工作流:

name: runner-routing-check

on:
  workflow_dispatch:

jobs:
  route-check:
    runs-on: [self-hosted, macOS, arm64]
    steps:
      - name: Show runner context
        run: |
          echo "runner=${RUNNER_NAME}"
          echo "os=${RUNNER_OS}"
          uname -m
          sw_vers

若這個 Job 也排隊,問題仍在路由或服務層;若它能執行,而生產 Job 不能執行,便要繼續比較生產工作流的自訂標籤、環境條件和並發設定。不要把生產任務長期改成只有 self-hosted 的寬泛標籤,因為這會讓不具備 Xcode、簽署憑證或指定架構的節點也可能成為候選。

03

第二層:Runner Group 與倉庫權限要分開驗證

標籤匹配不代表倉庫可以使用該 Runner。組織級或企業級 Runner 通常還受 Runner Group 的倉庫存取範圍限制;GitHub 官方的 Runner Group 存取管理文件把「節點屬於哪個組別」和「哪些倉庫能使用該組別」分成不同管理項目。

建議按以下次序核對:

  1. 確認目標 Mac 所屬的 Runner Group,而不是只確認 Runner 名稱。
  2. 確認該組別是否允許目標倉庫;組織內其他倉庫可用,不代表這個倉庫也可用。
  3. 確認工作流沒有指定不存在、拼寫不同或不在授權範圍內的組別。
  4. 用不包含機密、簽署步驟和發布操作的測試 Job 驗證路由。
  5. 記錄權限修正前後可見的 Runner 差異,再刪除測試工作流或限制其觸發範圍。

測試時不要直接撤銷整個組別權限,也不要刪除 Runner 來「刷新」狀態。若確實需要重新註冊,應先保存工作目錄、服務設定和恢復所需的註冊資訊;官方移除 Runner 說明可用來確認移除的影響範圍。

04

第三層:確認候選節點是真的忙碌

當標籤與 Runner Group 都符合,下一個問題才是容量。先從活動 Job、Runner 管理頁和 Mac 本機進程交叉判斷,不要只因為 Job 顯示排隊就直接增加節點。

需要特別分開觀察的工作類型包括:

  • Xcode 建置是否長時間佔用同一節點。
  • Simulator 測試是否在測試結束後仍留下進程。
  • 簽署與發布步驟是否因憑證、鑰匙圈或人工確認而保持工作執行狀態。
  • 長時間腳本是否使用背景進程,導致工作表面結束但節點仍被工作區或子程序鎖住。

若活動 Job 顯示仍在執行,先確認它是否真的有輸出或進程活動;若 Job 已結束但 Runner 沒有恢復可用,應優先處理異常占用和清理流程。只有清理後仍有多個符合條件的節點持續忙碌,且新的任務穩定進入隊列,才有理由評估拆分建置、測試、簽署任務池或增加遠端 Mac。

05

第四層:線上不等於 macOS 服務能領取任務

SSH 可以登入,只能證明遠端連線和登入路徑可用,不能證明 Runner 服務仍能向 GitHub 保持連線並領取任務。macOS 節點應同步查看 Runner 診斷日誌、網路連線、工作目錄權限和 launchd 服務狀態;可參考 GitHub 的 macOS 自託管 Runner 監控與疑難排解文件。

在節點上先以實際服務標籤替換 <service-label>,保留輸出再進行修改:

launchctl print system/<service-label>
log show --last 30m --predicate 'process CONTAINS[c] "Runner"'

重點不是看到某個程序名稱就判定正常,而是確認:

  • 服務帳戶是否能讀寫 Runner 工作目錄。
  • 重啟 Mac 後服務是否自動恢復,而不是只在 SSH 工作階段中啟動。
  • 診斷日誌是否顯示註冊狀態損壞、連線中斷或反覆重啟。
  • 自動更新後,服務帳戶、工作目錄和常駐設定是否仍一致。

只有在日誌明確指向註冊狀態損壞時,才考慮重裝服務或重新註冊。重新註冊前必須保留原節點識別、工作流快照與回復路徑;刪除 Runner、撤銷令牌或改寫 launchd 設定,都可能令原本可恢復的節點變成需要重新佈署的節點。

06

可勾選的低風險排查清單

  • [ ] 保存工作流 YAML、實際 runs-on、Runner 標籤和 Runner Group 快照。
  • [ ] 在執行記錄中排除前置 Job、環境核准和並發限制造成的等待。
  • [ ] 確認所有組合標籤都逐字匹配,沒有因重建節點而遺失自訂標籤。
  • [ ] 確認 Runner Group 已授權目標倉庫,而非只授權其他倉庫。
  • [ ] 執行不含機密與發布步驟的最小診斷 Job。
  • [ ] 比對活動 Job、卡死進程、工作區鎖定和 Runner 空閒狀態。
  • [ ] 查看診斷日誌、網路連線、工作目錄權限及 launchd 狀態。
  • [ ] 只有取得註冊損壞證據後,才安排服務重裝或重新註冊。
  • [ ] 以真實 Xcode 建置和近似生產的標籤任務完成復測。
  • [ ] 把結果分類為路由故障、節點故障或容量不足,並記錄回退方案。
07

常見情況的獨立判讀

自託管 Runner 在線但沒有任務

這通常不是「Runner 沒開機」這麼簡單。先確認它是否符合工作流的完整標籤集合,再確認 Runner Group 是否允許目標倉庫;兩者都符合後,才查看服務是否能持續領取任務。GitHub 的自託管 Runner 工作流使用文件可用於核對工作流宣告方式。

runs-on 看似匹配仍然 queued

將生產 Job 拆成最小路由測試和最小執行測試。前者只驗證節點是否被選中,後者才執行環境輸出;如果前者成功、後者失敗,問題便從路由轉移到節點服務、工作目錄或工具鏈,不能再用增加節點解決。

Idle 狀態與實際執行不一致

Idle 只應被視為管理頁上的一個觀察點。必須用 Job 領取記錄、Runner 日誌和實際命令輸出交叉驗證;若三者不一致,先處理服務連線或組別權限,而不是重建整個 CI 流程。

08

第五步:用復測矩陣決定修復、重建或擴容

修正後至少安排三類測試,避免把「能跑一個命令」誤當成生產恢復:

  1. 最小命令任務:只輸出 Runner 名稱、作業系統、架構與工具鏈版本,確認路由及基本執行。
  2. 真實 Xcode 建置:使用與生產相同的簽署、依賴和建置入口,確認不是只有空殼工作流能執行。
  3. 生產近似任務:使用目標標籤、Runner Group、測試流程和產物回傳,確認完整路徑恢復。

結果應分類如下:

  • 最小任務無法開始:優先修復標籤、組別或服務。
  • 最小任務能開始,Xcode 任務失敗:檢查工具鏈、憑證、Simulator 或工作區清理,不要歸因於隊列容量。
  • 三類任務都能執行,但符合條件的節點長期全忙:才進入任務分池、錯峰或增加遠端 Mac 的容量評估。
  • 節點反覆重啟、狀態註冊損壞或服務無法可靠恢復:建立隔離節點,保留原節點作為回復對象,再決定是否重新註冊。

若現有本地設備不足以長時間保持構建節點在線,可先參考遠端 Mac 開發與算力方案作為隔離復測環境;需要評估 Mac mini 雲端算力時,也可查看遠端 Mac 方案選擇。這些方案適合用來驗證同一份工作流是否能在乾淨節點穩定領取與完成,不應取代對現有 Runner 根因的記錄。

如果目前方案是共用的 Mac、臨時開機的本地主機或只可 SSH 登入但服務未經驗收的節點,常見缺點是工作流會受人工開關機影響、標籤和權限容易漂移,而且重啟後未必能自動恢復領取任務。對需要備用建置能力或短期隔離驗證的團隊而言,租用 NodeMini 的遠端 Mac 可把節點在線、遠端登入與驗收流程分開管理;但若團隊需要長期固定的高負載建置、特殊實體介面或完全掌控硬體,購買並自行維護 Mac 仍可能更合適。完成本篇排查後,再按工作量和維運責任選擇方案,會比單純增加 Runner 數量更可靠。