GitLab CI 多个 iOS 签名任务冲突怎么办?2026 排障指南

这篇指南帮助企业发布与平台团队区分调度锁、Runner 并发、工作目录冲突和签名凭证问题。你将按故障边界完成排查、测试与放行,并据此决定继续共享 Mac、限制并发还是隔离签名节点。

GitLab CI 多个 iOS 签名任务冲突怎么办?2026 排障指南

目录

同一台 Mac 上,签名任务互相等待、归档结果被覆盖,或相同提交重跑却得到不同结果。
最快的处理方式:先用 resource_group 串行化共享签名资源上的关键 Job,再查 Runner 并发、工作目录和 Keychain 凭证边界;资源组不是主机锁,也不提供操作系统级隔离。

谁该看这篇:
企业 iOS 发布负责人:多个流水线同时签名或归档,需要避免发布结果相互覆盖。
平台工程师:多个 Job 共用 Mac Runner,需要确认调度与工作目录边界。
IT 与安全负责人:需要核验共享 Mac 上的签名凭证访问范围和生产放行条件。

GitLab CI iOS 签名任务冲突,先区分故障边界

签名任务失败,不一定意味着证书有问题。等待过久、产物对应错误提交、归档突然消失,以及提示私钥不可用,可能分别指向调度顺序、共享目录、Keychain 状态或凭证授权;在没有团队流水线记录前,不应把其中某一种说成普遍故障。

先收集同一时间段内的流水线与 Job 状态、Runner 执行日志,以及签名命令前后的阶段记录。确认故障是否只在并发时出现,再比较失败 Job 与成功 Job 的提交、执行账号、检出路径和证书身份。日志中不要打印私钥、密码、完整令牌或可重用的秘密值。

现象 优先检查 不要先下的结论
签名 Job 排队,执行顺序与提交顺序不符 resource_group、process mode、队列中等待的流水线 资源组失效
不同 Job 出现同名文件、归档或检出内容变化 builds_dir、GIT_CLONE_PATH、缓存与输出路径 证书损坏
私钥找不到、无法解锁或访问被拒绝 执行账号、Keychain 状态、私钥访问控制 并发是唯一原因
不同项目同时签名同一发布资源 跨项目协调方式与 Runner 路由 同名资源组天然互斥

调度锁只管配置进组的 Job

GitLab CI/CD Job 默认可以并发运行;对需要保护的签名或发布 Job,可指定 resource_group,让使用同一资源组的任务不会同时执行。关键是锁必须覆盖真正会争用的阶段:如果归档、导出或上传仍在资源组之外,单独锁住某个签名命令并不能保证整个发布过程互斥。具体作用与 resource_group 配置说明一致。

例如,若签名和导出会共同更新同一发布状态,可以将它们封装在同一个受锁 Job 中:

sign_and_export:
  stage: release
  resource_group: ios-release-signing
  script:
    - ./ci/sign-and-export.sh

资源组还涉及等待顺序。默认 unordered 不保证 Job 按流水线先后执行;若旧流水线必须先发布,检查 oldest_first 是否符合发布规则。若希望新流水线优先,则不能只看队列顺序:还要确认旧任务不会在新版本之后覆盖发布结果,并验证任务是否满足幂等要求。GitLab 列出的不同 process mode 具有不同排序语义,process mode 可通过资源组 API 文档管理。

边界评分:项目内互斥——资源组适用;跨项目主机锁——不能单独依赖资源组;Keychain 隔离——资源组不负责。

Runner 并发与工作目录是两类问题

设置了资源组,不代表整台 Mac 只会运行一个 Job。普通构建 Job 仍可能与签名 Job 并行,Runner 自身的全局 concurrent 和单 Runner limit 也会影响同时运行的任务数量。对照 Runner 高级配置文档核对实际限制,而不是只看 GitLab 项目里的 Job 数量。

目录问题要单独查。Runner 文档说明,多个 Job 共用 builds_dir 时,并发执行可能导致它们落入相同目录;共享缓存、固定归档文件名或写死的临时目录也可能让产物彼此覆盖。检查 Runner 工作目录配置说明中的路径规则,确认自定义 GIT_CLONE_PATH 是否为每个并发执行实例保留独立位置。

选择 并发与隔离效果 更适合的条件 主要代价
共享 Runner,限制并发 可降低同机争用;不自动隔离用户目录与凭证 共享任务属于同一信任域,且实测目录无串扰 等待增加,其他 Job 也可能受影响
共享 Runner,使用唯一工作路径 目录隔离更清楚;Keychain 仍需另外核验 可并行构建,但每个 Job 有独立检出和输出路径 需检查脚本、缓存键与临时文件路径
专用签名 Runner 或节点 可把签名任务与普通任务分开调度 跨项目共享凭证,或安全边界要求不同 维护与节点成本增加,仍需验证访问控制

这张表是架构选择框架,不是性能承诺。降低 Runner 并发只能减少同时运行的 Job;它不自动清理旧文件,也不能证明凭证不会被其他任务访问。

Keychain 与 CI 变量需要独立验收

iOS 代码签名依赖证书、对应私钥及其访问权限。macOS Keychain 对受保护项目设有访问控制列表,使用私钥执行签名时,系统会根据 ACL 判断调用方是否有相应权限;可对照 Apple 的 Keychain 访问控制说明检查。也就是说,看到签名命令成功,只能说明该次操作成功,不能据此推断其他 Job 被隔离。

按以下顺序核查凭证边界:

  1. 确认签名 Job 的执行账号,以及它实际访问的 Keychain 名称和状态。
  2. 核对证书与私钥是否成对,并查明哪些调用方获准使用私钥;Apple 的签名与验证文档说明签名操作使用私钥。
  3. 检查 CI 变量或密钥是否只向所需项目、受保护分支和发布 Job 提供。
  4. 用不会输出秘密值的日志记录账号、任务标识、Keychain 目标及签名结果。
  5. 使用不含生产凭证的测试身份做负向验证:未授权 Job 应无法读取或使用该凭证。

不要把变量设置为“已掩码”就视为安全隔离。官方 CI/CD 变量安全说明提醒,掩码不是抵御恶意脚本访问变量值的保证;流水线安全指南也建议对敏感数据评估密钥管理方案,并按需要限制可用范围。

跨项目冲突不能靠同名资源组解决

资源组的配置与项目关联,不能推定不同项目中写了同一个组名,就会争用同一把全局锁。若多个项目将 Job 路由到同一台 Mac,先从各项目的流水线配置和执行记录确认是否出现实际并发;再决定是通过统一协调入口排队、将签名 Job 汇聚到受控项目,还是按信任域拆分 Runner。

多项目 Runner 的作用范围也要核查。项目、群组和实例 Runner 的可见范围不同;Runner 管理范围说明可用于确认哪些项目能向同一 Runner 提交任务。共享 Runner 让资源复用更方便,但项目配置、脚本和凭证权限的治理也会更复杂;如果不同团队可以修改签名脚本,却共享同一 Keychain,调度锁并不能补上这条安全边界。

生产放行前按故障模式做验收

不要只用一次成功发布作为通过标准。在非生产环境分别触发签名任务并发、普通构建并行、失败重试和跨项目调用,观察四件事:Job 是否按预定规则等待,检出与输出目录是否唯一,产物是否对应正确的提交,以及未授权任务是否被拒绝使用测试凭证。

每个用例都留存配置版本、流水线与 Job 标识、Runner 标识、工作路径、产物归属和脱敏后的凭证访问结果。归档与日志还应能追溯到具体流水线;GitLab 的Job artifacts 文档说明,产物可在流水线 Job 间传递,但应按用途与保留策略管理,而不是把工作目录当作永久发布存储。

验收结果 处理建议 放行条件
同项目签名顺序正确,普通构建路径独立,凭证负向测试通过 继续共享,并保留回归用例 后续配置变更仍执行同一组验证
目录冲突可复现,但凭证边界清晰 限制 Runner 并发,或修正唯一工作路径后复测 并发用例不再覆盖检出文件与归档
跨项目任务仍同时访问签名资源 增加统一协调,或拆分签名节点 跨项目触发测试证明不会并发争用
未授权 Job 能访问测试凭证,或访问记录无法解释 暂停生产签名放行,先收紧权限并复测 未授权访问被拒绝,审计证据不含秘密值

如果当前做法是让多项目共享一台 Mac、用固定目录写归档,再靠人工观察队列,缺点是冲突难复现、发布顺序不透明、凭证边界也难证明。对短期试运行、临时扩容或验证专用签名节点的团队,可以评估 VPSMAC 的远程 Mac 承载方式;先用非生产任务验证资源组、目录和凭证边界,再决定是否租用独立节点。若需要长期稳定的重负载、物理接口或明确的本地控制,自购设备可能更合适;若需要比较团队 Mac CI 节点规划,可先查看VPSMAC 的远程 Mac 服务,再从 VPSMAC 的 Mac 节点选项了解方案,并结合自身验收结果决定是否租赁。