某个任务明明显示 Runner 在线,前一个仓库却把宿主机改坏,导致后一个 iOS 构建失败。
最快的处理方式是:使用按信任级别划分的专用 Mac 节点,给任务绑定明确标签,并在工作区清理、签名隔离、重启恢复和队列压力测试全部通过后,再分批放量。Bitbucket Pipelines macOS Runner 可以用于生产,但不能把可写入宿主机的任务直接混跑在共享节点上。
这篇文章适合准备接入 iOS 构建与发布流程的企业 IT 负责人,也适合验收自托管 Mac 节点的平台工程团队。
如果你正在评估固定 Mac、远程 Mac 或 Apple Silicon 构建容量,下面的标准可以直接改造成内部上线表。
01 先确定生产准入线:在线不代表可用
macOS Runner 不是每个步骤都自动获得全新的干净环境。根据 Atlassian 官方 macOS Runner 说明,任务通过 Bash 直接运行在 Mac 宿主机上;脚本可以访问宿主机上的应用、文件和进程。
这会带来至少 4 类隐性风险:
- 目录污染:脚本写入构建目录之外的路径,后续任务可能读取到旧配置或旧产物。
- 软件污染:任务安装全局工具、修改 PATH,或替换系统可见的依赖版本。
- 进程污染:构建脚本启动数据库、模拟器辅助服务或后台进程,任务结束后仍然运行。
- 资源污染:不同任务共享 CPU、内存、磁盘和 swap,失败结果可能随节点当时的负载变化。
平台会尝试在步骤结束后清空构建目录,但这不等同于宿主机级别的隔离。尤其是外部贡献代码、不受信任仓库和发布脚本,不能只依赖一段 cleanup.sh 来解决权限边界。
因此,企业准入结论应按下面的决策条件执行:
- 若任务只处理可信仓库、无签名凭证、只写入受控工作目录,可进入普通测试 Runner 池。
- 若任务需要安装工具、访问特殊 SDK 或保留较大的依赖缓存,应进入项目专用 Runner 池。
- 若任务能够读取 Apple 签名材料、发布密钥或生产环境变量,必须进入独立生产签名节点。
- 若仓库包含外部贡献代码或执行来源不确定的脚本,不得与生产签名节点混用。
- 若清理、恢复或错误路由测试有一项失败,回退到隔离测试,不得直接放量。
⚠️ 经验边界:Runner 页面显示在线,只能证明连接状态正常,不能证明任务环境干净、凭证不可见、构建结果可重复。
02 第一项指标:宿主机隔离与清理必须可验证
你需要把“清理完成”拆成可观察的证据,而不是在流水线最后写一句删除命令。
建议把以下项目加入验收记录:
| 检查对象 | 验收动作 | 通过门槛 | 失败处置 |
|---|---|---|---|
| 工作目录 | 任务写入源码、派生数据和临时文件后退出 | 下一条任务读取不到前序产物 | 清空目录并重新执行 |
| 工作区之外的路径 | 尝试写入用户目录、共享缓存和工具目录 | 脚本无权修改,或修改后可被审计并恢复 | 改为专用节点 |
| 残留进程 | 启动后台服务后取消任务 | 任务结束后不存在非必要进程 | 杀进程并检查端口 |
| 依赖与缓存 | 修改全局工具或依赖缓存 | 下一条任务使用固定版本或独立缓存 | 重建节点环境 |
| 签名文件 | 导入临时证书和描述文件 | 任务结束后无法从 Keychain、临时目录和日志恢复 | 立即撤销凭证并隔离节点 |
官方文档还提醒,在同一台机器上部署多个 Runner,可能产生资源共享和使用冲突。macOS Runner 的部署要求与限制也明确指出,宿主机至少需要 8 GB RAM,并且 Apple Silicon 与 Intel 架构应在注册时明确选择。
这两个信息对采购的意义很直接:不要把“多注册几个 Runner”当成自然扩容,也不要只按开发者人数购买硬件。多个 Runner 可能把队列问题转化为资源争抢问题。
03 第二项指标:Runner 范围决定授权半径
Repository Runner 和 Workspace Runner 的差异,不只是管理入口不同。
Repository Runner 主要服务于指定仓库,适合生产签名、发布归档或包含敏感配置的任务。Workspace Runner 可以服务同一 Workspace 下的多个仓库,适合统一工具链、共享测试节点或低风险构建,但它的授权半径也更大。
Bitbucket Runner 的注册说明显示,Runner 默认带有 self.hosted 和平台标签;自定义标签只能使用小写字母、数字和点号,并且每个 Runner 最多可配置 10 个自定义标签。标签不是装饰信息,而是你的任务路由和权限边界。
推荐的标签结构可以保持短而明确:
runs-on:
- self.hosted
- macos
- ios-test
- apple-silicon
不要把 macos 单独当成生产路由标签。它只能说明操作系统类型,不能说明节点是否安装了指定 Xcode、是否允许签名、是否属于生产环境。
| 节点池 | 适合任务 | 不应承载的任务 | 推荐范围 |
|---|---|---|---|
| 测试池 | 单元测试、依赖验证、非生产归档 | 生产证书、发布密钥 | Repository 或受控 Workspace |
| 项目构建池 | 可信仓库的 iOS 构建、缓存型任务 | 外部贡献代码、生产发布 | 项目级 Workspace |
| 签名发布池 | App Store 或企业发布流程 | 任意不可信脚本 | Repository Runner 优先 |
| 故障备用池 | 主节点故障时的受控切换 | 长期混跑所有项目 | 与生产池同等安全标准 |
在 Bitbucket Pipelines 的 YAML 路由文档中,任务会等待同时满足所有标签的可用 Runner。如果没有匹配标签的在线节点,任务会失败;如果匹配节点繁忙,任务会进入队列。
上线前至少要做 3 次错误路由测试:
- 删除一个必需标签,确认任务不会落到普通 Linux 或普通 macOS 节点。
- 让所有匹配节点处于繁忙状态,确认队列行为和告警可见。
- 创建一个同平台但权限较低的节点,确认生产标签任务不会误投过去。
04 第三项指标:Xcode 环境要能重复,而不是“当前能跑”
iOS CI/CD 的稳定性通常不是由单个芯片型号决定,而是由整个工具链组合决定。你需要记录 macOS、Xcode、SDK、依赖管理器、运行账号、证书存储位置和缓存策略。
Xcode 的命令行工具包括 xcodebuild、simctl、devicectl 和 xcresulttool。Apple 的 Xcode 命令行工具参考要求先安装 Xcode,并将目标版本设置为当前开发目录,然后才能在终端中调用这些工具。
验收动作不要只执行一次构建。建议使用同一提交进行以下测试:
- 第一次执行:记录依赖解析、编译、测试和归档结果。
- 第二次执行:检查缓存命中是否改变产物或隐藏环境依赖。
- 清理后执行:删除派生数据和临时缓存,再次构建。
- 节点重启后执行:确认 Xcode 路径、登录账号和必要服务能够恢复。
- 不同标签执行:确认相同提交不会因为误入不同节点池而产生无法解释的差异。
如果构建依赖 Xcode 图形界面状态、登录钥匙串或人工确认,不能把它包装成完全无人值守的 CI。你应当把这类步骤拆出生产流水线,或改造为明确的命令行流程。
05 第四项指标:签名凭证必须与代码访问权限分开
iOS 发布任务通常同时涉及源码访问、依赖下载、签名证书、描述文件、发布权限和归档产物。把这些信息都放进通用脚本,是最容易扩大事故范围的做法。
Bitbucket 变量与密钥文档说明,Workspace 变量可以被该 Workspace 下多个仓库访问,Repository 变量则面向单个仓库;安全变量会在日志中进行隐藏,但这不等于宿主机上的 Keychain、临时文件和进程环境自动隔离。
建议按下面的边界拆分:
- Runner 注册凭证:只用于 Runner 本身,不写入仓库。
- 代码访问凭证:只允许读取必要仓库,不与 Apple 发布权限共用。
- Apple 签名材料:只导入生产签名节点的临时 Keychain。
- 发布凭证:只在发布步骤短时加载,任务结束后清除。
- 归档产物:只允许指定任务上传,禁止普通测试任务读取生产归档。
Apple 的 代码签名证书技术说明 TN3161指出,codesign 会从 Keychain 中查找匹配的签名身份;如果存在多个匹配身份,还可能出现选择歧义。你应当固定签名身份、限制 Keychain 搜索范围,并记录导入、使用、清除、轮换和撤销证据。
同时,不要用 sudo 随意切换签名用户。Apple 的代码签名文档明确提醒,签名过程依赖用户账户信息,切换账户可能导致访问签名信息失败或行为不一致。
06 第五项指标:恢复能力要通过无人值守测试
生产节点至少要覆盖以下故障:
- Runner 进程退出;
- Mac 主机重启;
- 网络短时中断;
- 任务被取消;
- 构建期间出现异常退出;
- 节点重新上线后接收新任务。
每次测试都要记录 4 项证据:故障发生时间、Runner 状态变化、恢复后的首个任务、恢复后是否残留前序状态。
“恢复成功”不能只定义为网页上重新出现在线。合格标准应包括:
- 节点能够无人值守启动;
- Runner 能重新连接 Bitbucket Cloud;
- 只有匹配标签的任务可以接入;
- 前序任务的进程、Keychain 和临时文件已清除;
- 新任务不需要人工打开 Xcode 或远程桌面确认。
如果恢复动作依赖人工登录,那么它更适合作为备用开发节点,而不是唯一的生产发布节点。对于需要持续在线的 Mac 构建节点,你还应在内部 runbook 中写清楚远程重启权限、责任人、故障升级路径和凭证撤销动作。
07 第六项指标:容量按队列证据采购
Bitbucket Cloud 对自托管 Runner 使用 Workspace 级步骤队列。官方并发与队列说明显示,队列最多容纳 1,000 个步骤,同时运行数量还受 Runner 并发限制影响。
因此,容量模型至少要收集:
- 真实构建时长;
- 单个节点在峰值时段的有效产能;
- 测试、归档、签名任务的比例;
- 队列等待时间;
- 节点故障时剩余产能;
- 缓存命中和清理后的构建时长。
不要用“团队有多少开发者”直接推导 Runner 数量,也不要用芯片宣传参数代替流水线实测。开发者人数不等于并发任务数,CPU 核心数也不等于可用归档吞吐。
最终可以使用下面的放量条件:
- 若隔离、清理、签名和恢复证据全部完整,且错误标签测试通过,选择限量生产。
- 若节点只服务可信仓库,队列稳定但没有故障冗余,选择试点,不承担唯一发布职责。
- 若构建结果依赖人工登录、GUI 状态或节点残留,回退到非生产测试池。
- 若峰值队列持续增长且备用节点不足,先增加专用容量,再扩大生产任务范围。
- 若 Workspace Runner 无法证明跨仓库隔离,改用 Repository Runner 承载敏感任务。
08 把验收记录变成上线门槛
你可以将每个指标设置为“通过、整改、阻断”三种状态,而不是用平均分掩盖高风险项。
建议阻断条件包括:
- 生产标签任务能够落入普通构建节点;
- 前序任务留下的凭证可被后续任务读取;
- 清理脚本失败后没有告警;
- Mac 重启后必须人工登录才能接单;
- 归档产物被非发布任务读取;
- 节点离线时没有备用路由或人工升级流程。
这套标准也适用于你评估固定 Mac 与远程 Mac 构建容量的场景。若需要临时增加隔离的 Mac 节点,可以先查看 CALMVPS 的 Mac 方案,再根据项目周期核对 CALMVPS 的套餐与计费信息。但无论节点来自本地机房还是远程环境,生产准入证据都不能省略。
09 常见问题
Bitbucket macOS Runner 怎样配置到指定任务?
先注册 Runner,再为节点添加平台、环境和权限标签。YAML 中使用 self.hosted、macos 及至少一个业务标签,让测试、归档和发布任务分别进入对应节点池。配置结束后必须验证繁忙、离线和无匹配标签场景,确认失败不会静默落到权限更高的节点。
macOS Runner 可以承担 iOS 发布吗?
可以,但只适合通过生产准入的专用节点。发布节点应与外部贡献代码、普通测试任务和共享缓存分开,并提供签名材料导入、使用、清理、轮换和撤销记录。若任务结束后仍能访问生产 Keychain 或归档文件,应立即停止放量。
Workspace Runner 的隔离边界怎样确认?
重点检查 4 个对象:任务标签、Workspace 变量、缓存目录和宿主机 Keychain。Workspace Runner 可以被多个仓库调用,因此不能仅凭仓库权限判断安全性。生产任务应使用更精确的标签和更小的授权范围,必要时改用 Repository Runner。
构建完成后只清空工作目录够不够?
不够。macOS Runner 会尝试清空构建目录,但工作目录之外的修改、常驻进程、全局工具、临时 Keychain、环境变量和归档产物仍可能残留。你需要用下一条独立任务验证清理结果,而不是只检查清理命令是否返回成功。
Runner 离线后怎样判断已经恢复?
检查的不只是在线状态,还包括无人值守启动、重新连接、正确标签接单和状态清零。恢复后的首个任务应验证源码、缓存、进程和凭证均不来自前序任务。若必须人工登录、打开 Xcode 或删除残留文件,节点仍未达到生产恢复标准。
如果你现在的方案是把一台共享 Mac 直接挂到 Bitbucket Pipelines 上,常见缺点是权限半径过大、宿主机容易被任务污染、节点故障时缺少备用容量,而且采购团队往往没有连续的恢复和清理证据。相比之下,按项目增加隔离的远程 Mac 节点,可以让你在不一次性购置整套硬件的情况下,先验证标签路由、签名安全和队列压力,再决定是否扩大固定容量。对短期项目、迁移期和突发构建峰值,租赁 CALMVPS 的 Mac 往往比直接把生产发布压在共享节点上更容易控制风险。
最稳妥的下一步,是先在隔离的 Mac 节点运行一条非生产流水线。等共享环境、凭证清除、重启恢复和容量证据都能审计,再按项目增加固定节点或远程 Mac 容量,而不是直接把生产发布任务全部迁入。