痛点场景
典型链路:同事给工单打上 build-ios,状态来回拖动触发 Automation 双发,两条 Webhook 在人工察觉前就已入队——随后 DerivedData、模拟器或 exportArchive 与签名身份撞车。标签泛滥会让路由表失真:大量装饰性标签仍落到同一节点,值班看不出哪条车道饱和。
若交互调试与 Automation 共用同一 Unix 登录,SSH 手测与规则脚本会在文件与环境变量上互踩;VNC 下调试叠上 xcodebuild,CPU 打满常被误读成「网络卡」。若 GitHub 已在调度 Runner,Jira 侧脚本必须与 自托管标签路由 对齐,否则会出现「Jira 绿、Mac 红」的双调度争磁盘预算。
先把串行化做实:路由表、共享目录上的互斥、保守的队列深度;再视 SLO 考虑横向加节点。下列为 SSH / 协作 / CI 的简明取舍(与上文入口类文章互补,不重复展开端口故事)。
| 决策 | 适用 | 注意 |
|---|---|---|
| SSH 脚本执行器(每车道独立 Unix 用户) | 无头构建、对共享树做 flock、PATH 可控 |
密钥蔓延:每车道一把部署密钥 + 审计日志 |
| Runner / 队列 worker | 已标准化在 GitHub 或小守护进程上 | Jira 与 CI 双调度需非重叠标签与磁盘根 |
| 桌面 / VNC 车道 | 偶发模拟器或手工复现步骤 | 禁止与个人登录混用 Automation;拆节点或时间窗 |
标签→节点、锁实现、flock/队列上限与超时/退避
用机器拥有的标签(如 ci/pr、ci/release、hotfix)映射到构建等级,避免按人名路由。下表以双节点为基线,可按核数、SSD 与 Xcode 固定版本上调/下调。锁与队列细则还可对照 flock FAQ 与 队列锁 FAQ。
| Jira 标签 | 目标节点 / 车道 | 锁实现方式 | flock / 队列上限 | 超时 / 退避 |
|---|---|---|---|---|
ci/pr |
节点 A — PR 构建 | 仓库 worktree 上 flock;工作流 concurrency 组如 pr-${{issue.key}} |
并发 shell ≤2;每节点待处理深度 ≤8 | HTTP 连接 30s / 读 120s;shell 45m;退避 15s + 抖动,最多 3 次 |
ci/release |
节点 B — 发布机 | 归档全局互斥文件;公证/装订单独 flock |
发布并发 1;队列深度 ≤3(深度 2 起预警) | shell 120m;公证子步骤 45m;退避 60s 指数上限 10m |
hotfix |
节点 B — 池策略允许时可抢占 PR | 网关优先级队列 + 对 PR 任务的协作取消令牌 | 热修突发 1;启动前将 PR 队列泄至 ≤1 | HTTP 60s;shell 90m;5xx 单次重试不加抖动 |
design-qa(可选) |
节点 A — 低优先级车道 | nice 或 CPU cgroup + 独立工作目录;不经签名 |
并发 1;与同卷 ci/release 禁止并行 |
shell 20m;HTTP 45s;线性退避 30s,最多 5 次 |
权限隔离
不要用个人登录跑 Automation。每车道使用服务账户:独立家目录与 SSH 密钥、最小 sudo(理想为零)、只读共享缓存、跨账户产物目录慎用 setgid;钥匙串在 CI 与交互签名之间分离。
Webhook 先落在小型网关(HMAC 或静态头校验),再经 VPN/网格转发到 Mac——形态可与 已验签 Webhook → 队列 类比。Unix 与 VNC 边界实践见 SSH/VNC 权限隔离实战;仅在签名 flock 内短时解锁构建钥匙串。
Jira 触发字段与幂等
收窄触发优于宽触发:例如「进入 Ready for build」流转 + 添加标签 + 第二个动作翻转自定义字段「请求构建」,可减少拖列导致的抖动。网关注册 webhookEvent、issue.id、issue.key、transition.transitionId 以及标签集合哈希。
幂等: dedupe_key = sha256(issue.id + transitionId + ruleId + sorted(labels));15~60 分钟内丢弃重复。仅在持久化入队成功之后再回 200;饱和时用 429 + Retry-After。对 Jira REST 评论做节流(「已排队」与「运行中」分模板)。
建议工单字段:构建关联 ID、最近 Automation 运行时间、由网关更新的 锁持有者(持 flock 时写入)。
冲突案例 FAQ
Q:两条工单几乎同时带 ci/release,谁赢?
以网关持久化入队先后为准;后者应拿到排队位次,绝不在同一签名互斥上双开。仅允许在获取锁之前按数值优先级重排,禁止归档中途改序。
Q:flock 瞬间成功但产物仍坏?
锁路径与真实写入树不一致,或子进程逃出加锁 shell。保持单一包装、显式 LOCKFILE;worktree 并行见 worktree 与锁文件决策矩阵。
Q:Jira 显示已触发,Mac 却空转?
规则可能对探测 URL 回了 200。要求 JSON 形如 {"accepted":true,"task_id":"..."},并与 Jira 审计时间对齐排查。
Q:能否让队列深度跨时区一直堆?
不建议——每车道设硬顶,用 429 + Retry-After 泄压;应先加租 Mac 再放宽表内上限。
总结购买
上线前自检:标签表是否映射到节点;锁是否覆盖源码树、签名、模拟器数据;超时是否贴合真实 xcodebuild 分位。扩容信号应是周级队列饱和或锁等待超 SLO,而不是图表「看起来忙」。在引入更重编排之前,优先把 PR 与发布拆到不同硬件。