把池子里每台 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,便于平台组为峰值加软池而无需每次亲和实验都走资本审批。