把池子裡每臺 Mac mini M4 都當成可互換節點聽起來很「平權」,直到某個大倉任務總落在同一臺機器上烤乾 SSD,或 UI 測試需要乾淨模擬器卻把整個組織的隊列串在同一臺上。親和(affinity) 是調度器對「任務該跑哪」的偏好;分散(spread) 是反向力量——把並發任務攤到多臺機,降低相關故障與熱點。本文用 2026 矩陣,把 Kubernetes 裡常說的軟/硬約束翻譯到 GitHub Actions 式 runs-on、Runner 組與組織策略上。
標籤治理:標籤命名空間與飢餓防護。公平並發:並發切片與公平性。系統釘版:macOS 與 Xcode 釘扎。價格:定價;幫助:幫助。
軟/硬親和(允許打破什麼)
| 約束 | 無匹配時行為 | 典型 macOS 場景 |
|---|---|---|
| 軟親和 | 排隊或選次優主機 | 偏好 m4-apple-silicon,但允許溢流到 m2 預發 |
| 硬親和 | 直到標籤集完全匹配才調度 | 僅在 nm-org/signing/* Runner 上簽名 |
| 反親和/分散 | 同倉已有任務在跑則拒絕該主機 | 模擬器重負載:每機最多 1 個 UI 任務 |
何時分散、何時釘死
| 信號 | 傾向分散 | 傾向釘死/硬標籤 |
|---|---|---|
| 磁碟 IO 飽和 | 是——錯開重 DerivedData 任務 |
少見——除非每機獨立緩存卷 |
| 許可證/籤名 | 否——保持硬池 | 是——令牌綁定主機 |
| 並行 UI 抖動 | 是——每主機一shard策略 | 可選固定調試機做復現 |
經驗法則: 若同一主機上連續兩次失敗會讓工程師花超過 30 分鐘 才能區分根因,應強制分散或按模劃分 Runner。
八步落地清單
- 盤點工作流 中
runs-on,標記軟、硬或分散。 - 實現分散: 用編排器唯一併發組,或為 shard 使用不同 Runner 標籤。
- 限制硬親和 僅用於籤名、GPU 或合規——其餘默認軟。
- 大盤 按標籤看隊列深度;硬池飢餓而軟池空轉時告警。
- 演練日: 下線一臺機,驗證軟任務遷移、硬任務排隊提示清晰。
- 例外寫入 與 OS/Xcode manifest 同倉。
- 季度復盤 新晶片(M5…)出現時軟池應能承接試驗。
- 成本: 分散可能要多 N 臺 mini;對比租用 burst 與隊列 SLO 下滑。
反模式
每個工作流都寫 runs-on: [self-hosted, 精確主機名]——等於養寵物。只用軟標籤卻不度量公平性——沒有度量則分散無效。忽視筆記本改 Runner 的熱節流;雲側專用 M4 金屬機行為更可預期,這也是團隊租用區域 Mac 而非把 CI 堆在開發桌上的原因。
NodeMac 在香港、日本、韓國、新加坡、美國提供帶 SSH/VNC 的物理 Mac mini M4,便於平臺組為峰值加軟池而無需每次親和實驗都走資本審批。