iOS CI 构建超时怎么查?2026 Mac 容量排障指南

GitHub Actions 自托管 Runner 在 60 秒内没有接收任务时,任务会重新排队;排队超过 24 小时仍未被处理则失败。 这说明“流水线超时”不能直接等同于 Mac 性能不足。处理 iOS CI 构建超时排查 时,应先按排队、任务领取、依赖、xcodebuild、模拟器、签名和上传阶段定位;只有健康节点持续满载、排队随并发增长,且单任务耗时没有明显异常,才进入 Mac 扩容或弹性租赁决策。
参考:GitHub Actions 自托管 Runner 路由规则

这篇文章适合企业 IT 负责人:需要判断新增 Mac 资源是否真的能解决 CI 超时。
也适合平台工程负责人:需要建立从 Runner 状态到构建阶段的证据链。研发效能或发布负责人,则可以用它降低高峰期排队与版本交付延迟。

一条 CI 流水线从触发到完成,至少可以拆成以下状态:

阶段 要确认的事实 常见误判
排队 任务何时进入队列,是否等待前置任务 把依赖工作流等待当成 Mac 不够
任务领取 哪个 Mac Runner 接单,接单时间是什么 看到有空闲 Mac 就认为一定能路由
源码与依赖 Git、Git LFS、Swift Package、私有制品库是否完成 把首次下载时间归因于 CPU
构建与测试 xcodebuild 停在哪个 target、scheme 或 test plan 只看整机 CPU 占用
模拟器 模拟器是否启动,目标运行时是否可用 把模拟器启动等待当成编译慢
签名与上传 Keychain、证书、Provisioning Profile、上传接口是否正常 把签名阻塞误判为节点容量不足

Apple 的文档明确说明,xcodebuild 属于 Xcode 提供的命令行工具,可用于构建项目和工作区;测试过程还可以产出 .xcresult 结果包,其中包含测试结果与相关日志。因此,排障时应保留命令输出和结果包,而不是只保存流水线最终的失败状态。
参考:Apple Xcode 命令行工具参考Apple 测试结果说明

建议先建立一张最小证据表:

记录项 必填内容 结论用途
任务时间线 触发、入队、领取、开始构建、签名、上传、结束 区分排队和执行
Runner 状态 在线、离线、忙、空闲、标签、所属组 判断是否能被正确路由
构建日志 xcodebuild 开始与结束、失败 target、测试阶段 定位单任务异常
依赖日志 Git、LFS、Swift Package、制品库请求与重试 判断网络或缓存问题
资源状态 CPU、内存压力、磁盘可用空间、并发任务数 佐证节点争抢
发布日志 签名、导出、上传、服务端响应 区分签名与上传阻塞

这张表的重点不是收集越多数据越好,而是让每一次超时都能回答三个问题:任务有没有进入 Mac?进入后有没有开始执行?哪一个阶段持续没有进展?

在使用自托管 Mac Runner 的环境中,任务路由通常同时受到标签、Runner Group、仓库访问策略和并发规则影响。以 GitHub Actions 为例,任务只有在标签和组都匹配时,才具备被对应 Runner 接收的资格;标签是累积匹配,少一个条件也可能无法执行。
参考:使用标签路由自托管 Runner在工作流中使用自托管 Runner

排查时不要只看控制台里的“空闲”字段,应逐项核对:

  1. 工作流要求的 runs-on 标签,是否与节点实际标签一致。
  2. 任务是否被发送到正确的 Runner Group。
  3. 仓库是否被允许访问该 Runner Group。
  4. 节点架构、系统版本和工具链是否满足任务条件。
  5. 是否存在工作流级或 Job 级并发限制。
  6. 前置 Job 是否尚未完成,导致当前 Job 根本没有进入 Runner 阶段。

并发策略尤其容易制造假性超时。默认允许多个工作流和 Job 同时运行,但配置了 concurrency 后,同一并发组可能只允许一个任务执行,其余任务处于等待状态。此时增加 Mac 节点不一定有效,因为限制可能发生在调度层,而不是机器层。
参考:GitHub Actions 并发机制

判断标准:只有当“匹配条件正确的健康节点”持续处理任务,且排队长度随并发任务增加而增长时,队列数据才具有容量判断价值。一个显示空闲、但标签不匹配或没有仓库访问权限的 Mac,不应计入可用容量。

不少所谓的“Mac 构建慢”,实际发生在构建之前。典型顺序是:Runner 接单后拉取源码,下载 Git LFS 文件,解析 Swift Package,访问私有制品库,再准备 Xcode 所需的依赖。任何一个远程服务响应变慢,都可能让整条流水线触发超时。

建议将依赖阶段拆成四类证据:

  • 首次拉取:新节点没有缓存,首次下载明显更慢。
  • 缓存未命中:缓存键变化、分支变化或工具链变化,导致重新准备依赖。
  • 网络重试:代理、私网、DNS 或制品库访问不稳定。
  • 版本漂移:依赖解析结果变化,造成下载量和编译范围改变。

缓存只能加速可重建数据,不能修复凭证失效、私网不可达或依赖版本漂移。若同一任务在缓存命中时稳定,而缓存失效后卡在依赖准备阶段,应优先优化缓存键、依赖拓扑和网络接入;若缓存命中后仍卡在 xcodebuild,再进入构建资源排查。

企业团队可以在日志中增加以下字段:

dependency_start
dependency_end
cache_hit
git_lfs_retry_count
swift_package_resolve_start
private_registry_error
xcodebuild_start

这些字段不需要绑定某个 CI 平台。只要每次任务都使用相同定义,就能比较“排队变长”和“任务本身变慢”是否同时发生。

Xcode 构建不是一个单一动作。Apple 将构建设置、编译、链接、资源处理和打包等行为放在可配置的构建系统中;xcodebuild 传入的设置还具有较高优先级。因此,同一项目在不同节点上出现差异时,应先比较实际生效的构建设置,而不是只比较 Mac 型号。
参考:Apple Xcode 构建系统

按故障表现处理:

单任务变慢

如果排队几乎没有变化,但某个任务的编译、链接或测试阶段变长,应检查:

  • 使用的 Xcode 路径是否正确。
  • Scheme、SDK 和构建配置是否发生变化。
  • DerivedData 是否被多个任务共用。
  • 是否出现更多目标重编译。
  • 磁盘空间是否不足,导致缓存反复失效。
  • 内存压力是否造成并发编译效率下降。

多任务互相争抢

如果单任务运行正常,但并行任务一增加,所有任务都变慢,应检查:

  • 同一 Mac 是否同时承载多个大型构建。
  • DerivedData、模拟器目录和临时目录是否隔离。
  • 测试任务是否共享同一个模拟器状态。
  • CI 配置是否把发布、PR 和夜间任务放入同一资源池。
  • 是否存在固定并发组,导致任务等待调度而非等待硬件。

模拟器阶段停滞

模拟器并不等同于物理设备。Apple 明确提醒,模拟器无法完整复制真实设备的性能和硬件特性;涉及硬件特性的验证仍需要物理设备。若任务在模拟器启动或测试阶段停滞,应记录目标设备、运行时、启动日志和测试数量,不要只根据编译阶段的 CPU 曲线判断。
参考:Apple 模拟器与物理设备运行说明

签名阶段阻塞

签名问题通常具有权限和身份上下文特征。Keychain 未解锁、证书不匹配、Provisioning Profile 不适配、CI 用户权限不足,都会让任务停在导出或签名阶段。Apple 的签名文档也说明,分发签名涉及证书、配置文件和导出流程,不能把它简单视为普通编译任务。
参考:Apple 分发签名代码说明

因此,生产签名节点最好与普通 PR 构建节点分开评估。签名凭证需要更严格的访问控制和更清晰的恢复流程。若根因是 Keychain 或证书,新增普通 Mac 节点不会自动解决问题。

容量决策不应从“买几台 Mac”开始,而应先填写变量:

  • 峰值并发:C_peak
  • 单任务平均执行时长:T_avg
  • 可接受排队时间:Q_target
  • 固定节点数量:N_fixed
  • 备用节点数量:N_spare
  • 发布窗口集中程度:W_peak
  • 节点恢复要求:R_recovery

这些变量可以来自企业自己的流水线记录。没有连续数据时,不要把某个网上配置或单次测试结果直接当成容量结论。

决策条件列表

  • 任务领取前就长时间等待,且存在标签、Runner Group 或并发限制,则先修复路由和调度;否则回退到下一项
  • 任务领取后主要耗时在 Git、Git LFS、Swift Package 或私有制品库,则先优化依赖与网络;否则回退到下一项
  • 只有单个项目、单个 Scheme 或单个 test plan 变慢,则先检查 Xcode 配置、DerivedData 和项目依赖;否则回退到下一项
  • 签名或上传阶段阻塞,则隔离签名节点并修复凭证、Keychain 或发布接口;不要直接增加普通构建节点
  • 健康节点持续满载,单任务阶段耗时相对稳定,排队又随着并发增长,则增加固定 Mac 构建容量
  • 高负载只出现在发布窗口、短期试点或临时项目,则优先使用弹性远程 Mac 做容量验证,再决定是否长期采购
  • 任务必须接入特定物理设备、外设或内部网络,则先确认远程 Mac 的物理接口和网络边界;不满足时回退到自有专用节点

固定 Mac、共享构建池、专用签名节点和弹性远程 Mac 的适用范围不同:

方案 更适合的场景 主要风险 决策评分
固定 Mac 节点 长期稳定负载、工具链固定、需要持续缓存 闲置时仍承担折旧、维护和占用成本 稳定性:★★★★★
共享构建池 多项目共用、任务类型相近、可统一标签 资源争抢、缓存污染、权限边界复杂 利用率:★★★★☆
专用签名节点 发布任务、证书和 Keychain 需要隔离 容量不能简单与普通构建混用 安全性:★★★★★
弹性远程 Mac 发布高峰、短期试点、临时扩容 需要验证网络、凭证、缓存和恢复链路 灵活性:★★★★☆

NOVAKVM 的 Mac 远程资源入口可以作为短期容量验证的起点,但不应替代企业自己的验收数据。需要比较不同节点交付方式时,应先把一条真实流水线迁移过去,记录排队、依赖、构建、签名、上传和重启恢复结果,再决定是否进入长期部署。

增加节点或引入弹性 Mac 后,不能只验证“节点在线”。建议按以下步骤完成一次端到端验收:

  1. 验证注册:确认新节点在线,Runner 标签、分组和权限符合工作流要求。
  2. 验证路由:提交一条真实任务,确认它被目标节点接收,而不是继续停留在队列。
  3. 验证依赖:检查 Git、Git LFS、Swift Package 和私有制品库的访问与缓存行为。
  4. 验证构建:执行真实的 xcodebuild 构建和测试,保存日志与 .xcresult
  5. 验证模拟器:覆盖实际使用的运行时和测试目标,记录启动与测试阶段状态。
  6. 验证签名:使用受控凭证完成归档、导出或发布,不把证书复制到不必要的节点。
  7. 验证上传:确认上传接口、网络出口和失败重试行为正常。
  8. 验证恢复:重启节点或重启 Runner 服务,确认能自动恢复并重新接收任务。
  9. 验证追踪:故意制造一次可控失败,确认任务、Runner、构建和发布日志仍能关联。

其中,恢复验证不能被省略。企业真正需要的不是“某次构建成功”,而是节点异常后能否恢复、失败后能否定位,以及新节点是否会把问题从一个资源池复制到另一个资源池。

排查 iOS CI 超时应从哪个证据开始?

先查任务时间线,而不是先查 Mac 型号。确认任务入队时间、Runner 接单时间、依赖开始时间、xcodebuild 开始时间,以及签名和上传是否已经启动。这样可以先把排队故障、路由故障和单任务故障分开。

Mac Runner 显示空闲但任务仍在等待,通常缺什么证据?

通常缺少“任务要求”和“节点能力”的对应关系。需要同时查看标签、Runner Group、仓库访问策略、系统与架构条件,以及工作流并发设置。空闲节点只有在满足全部路由条件时,才算真正可用容量。

怎样区分构建阶段变慢与 Mac 资源不足?

将任务分成排队时间和执行时间。排队变长、执行阶段稳定,通常指向容量或调度;执行阶段本身变长,则更像 Xcode、依赖、模拟器、磁盘、内存或签名问题。若两者同时变差,还要排查节点争抢和外部服务变慢。

Xcode 阶段长时间无进展,是否应直接增加节点?

不一定。若阻塞发生在 xcodebuild 的单个 target、依赖解析、DerivedData、模拟器或签名阶段,应先修复对应环节。只有健康节点长期满载,且任务本身没有明显异常,增加节点才可能缩短排队。

证明企业打包机容量不足,需要保留哪些记录?

有效证据包括:符合条件的健康节点持续忙碌、排队随并发增长、单任务执行时间相对稳定、任务领取时间延后,以及扩容后排队明显改善。单独看到 CPU 高、内存高或某一次任务失败,都不足以证明容量不足。

完成上述分类后,企业可以把排障结果整理成一份“Mac 构建容量采集表”,至少记录峰值并发、阶段耗时、节点状态、失败原因和恢复结果。对于发布高峰或短期项目,先用 NOVAKVM 的远程 Mac 做一条真实流水线的 PoC,验证排队、构建、签名和恢复链路,再决定是采购固定 Mac、建设专用签名节点,还是保留弹性租赁。

与当前固定但数量不足的方案相比,单纯等待现有节点会把排队压力集中到发布窗口;与直接采购多台 Mac 相比,短期高峰又容易产生闲置、维护和工具链迁移成本。远程 Mac 租赁的价值不在于替代所有固定节点,而在于让企业先用真实任务验证容量缺口,再决定长期基础设施规模。需要开始评估时,可先查看 NOVAKVM 的 Mac 配置与交付选项,并把验收结果作为采购或扩容依据。

为 iOS CI 准备稳定的 NOVAKVM Mac 构建节点

当排队、磁盘容量或构建高峰拖慢发布时,使用 NOVAKVM Mac 租赁快速补充弹性算力。

基于 M4 Mac 的远程节点,为 Xcode 构建、测试与签名任务提供更充足的性能和存储空间。

查看定价 →