Runner 自动注销,不等于 Mac 已经恢复干净。最快的判断是:公共仓库、不可信代码或任务间必须强隔离时,优先使用临时 Runner;需要稳定签名环境、外设或高命中缓存时,使用权限受限的常驻 Runner。多数生产团队应采用“临时执行池+少量受控常驻节点”的双轨方案。
这篇文章适合三类人:需要为拉取请求和普通测试任务提供干净环境的平台团队;维护代码签名、真机或固定网络依赖的移动研发团队;正在计算 Mac Mini M6 Runner 数量、队列容量和扩缩容方式的技术负责人。
先按任务风险划分 Runner,而不是先按设备数量采购
GitHub Actions 临时 Runner 和常驻 Runner 的核心差异,不只是“任务结束后是否注销”。真正需要比较的是:工作目录、派生进程、钥匙串、缓存、网络配置和本地凭据,能否在任务完成后恢复到可验证状态。
GitHub 官方建议,自动扩缩容场景优先采用临时自托管 Runner,不建议用常驻 Runner承担自动扩缩容。临时 Runner只保证 GitHub 服务端向该 Runner 分配一个任务,任务完成后自动注销;擦除磁盘、删除用户、重置系统和重新注册设备,仍然需要你自己的自动化负责。(GitHub 官方自托管 Runner 文档)
这会带来至少四个容易被低估的限制:
- 隔离限制:注销 Runner 不会自动终止所有由构建脚本启动的子进程,也不会自动清除用户目录、系统钥匙串或外接磁盘中的文件。
- 缓存代价:常驻节点可以保留 Swift Package、CocoaPods、DerivedData 和模拟器运行时,但缓存越多,任务串扰和环境漂移的排查成本越高。
- 凭据风险:签名证书、临时密钥、发布账号和
GITHUB_TOKEN可能被构建脚本、第三方依赖或错误日志间接接触。 - 恢复成本:Mac 是物理设备。它不像 Linux 容器那样天然具备快速销毁和重建能力,系统更新、磁盘重置、重新注册和故障替换都需要平台团队编排。
GitHub 文档还明确提醒,自托管 Runner 不保证每个任务都在干净的临时虚拟机中运行;公共仓库或可接收外部贡献的仓库尤其容易让不可信代码进入 Runner 权限范围。(GitHub Actions 安全使用说明)
注意: 不要把
--ephemeral当作“设备自动擦除”开关。它是 GitHub 任务生命周期设置,不是 Mac 硬件生命周期方案。
个人项目与可信私有仓库:常驻节点通常更容易维护
如果你维护的是个人项目,或者团队内部使用、任务来源相对可控的私有仓库,常驻 Runner 可以减少工具链重复安装和缓存下载。对低并发 iOS 构建来说,固定的 Xcode、模拟器运行时和依赖缓存,往往比每次重新准备环境更容易操作。
但“私有”不等于“所有代码都可信”。拥有读取权限的人可能创建分支或拉取请求,工作流也可能执行来自依赖安装脚本、构建插件和代码生成器的命令。因此,常驻 Mac Mini M6 不应同时承担所有仓库和所有分支的任务。
建议你至少做这几件事:
- 每个任务开始前删除工作区,而不是只执行
git clean。 - 每个任务结束后清除临时密钥、导出的归档包、日志中的敏感变量和用户缓存。
- 将常驻 Runner 放入专用 Runner group,只允许明确的仓库使用。
- 用自定义标签区分普通编译、模拟器测试、真机测试和发布任务。
- 禁止普通拉取请求工作流接触发布证书和生产环境账号。
GitHub 的标签是累积匹配规则,工作流指定的标签必须同时满足;组织级 Runner group 还可以进一步限制哪些仓库有资格使用节点。(GitHub Actions 工作流中的自托管 Runner 配置)
如果你只是想把一个可信私有仓库迁移到 macOS 自托管 Runner,可以先参考 kvmsync 的 GitHub Actions 与 Mac 节点资料,但不要因为节点是租用的就跳过清理和权限设计。
接收外部贡献的开源团队:优先一次任务一次身份
开源团队最不适合把长期保存凭据的常驻 Mac 直接暴露给拉取请求。外部贡献者提交的代码可能修改构建脚本、依赖版本、测试命令甚至工作流调用方式。只要任务能访问 Runner 本地环境,就可能读取残留文件或利用系统权限。
Mac Mini M6 可以承担一次性 macOS Runner,但“能注册”不等于“任务结束后自动干净”。更准确的设计是:
- 为任务创建专用 macOS 用户或一次性系统环境。
- 使用短时注册凭据完成 Runner 注册,不在磁盘脚本中长期保存令牌。
- 为 Runner 设置临时任务属性,使 GitHub 在完成一个任务后自动注销它。
- 任务结束后等待子进程退出,停止构建服务和模拟器。
- 删除工作区、用户缓存、临时钥匙串与构建产物。
- 通过系统快照、磁盘重建或人工验收确认环境达到可重复状态。
- 清理失败时,将设备标记为不可调度,不允许它接收下一个任务。
GitHub 提供 REST API 管理 Runner 的注册、删除和标签,这意味着你可以把“注册—执行—注销—清理—重新注册”串成控制器流程,而不是靠人工登录设备处理。相关接口需要相应的仓库、组织或企业权限。(GitHub REST API 自托管 Runner 接口)
另外,临时 Runner 的日志必须转存到外部存储,否则一旦设备被重置,排查失败任务所需的信息可能一并消失。GitHub 官方也建议在生产部署临时 Runner 前,先确保日志被外部保存。(GitHub 官方自托管 Runner 文档)
高频构建的小型移动团队:把缓存收益与串扰风险分开算
小型移动研发团队通常会遇到一个矛盾:常驻 Runner 的缓存可以缩短构建准备时间,但多项目共用同一节点后,环境漂移和任务互相污染会吞掉这部分收益。
例如,项目 A 更新了 Swift Package,项目 B 随后复用本地缓存;某个测试任务留下模拟器进程,下一次任务启动时出现端口占用;某个脚本修改了系统工具链路径,失败重跑却在另一套环境中通过。这些问题不一定每天发生,却会让 CI 结果变得难以复现。
因此,普通编译和敏感发布任务应分开路由:
| 任务类型 | 推荐 Runner | 主要原因 | 需要保留的资产 |
|---|---|---|---|
| 拉取请求编译 | 临时 Runner 或可验证重置节点 | 降低任务残留影响 | 最少量依赖缓存 |
| 普通单元测试 | 临时 Runner | 便于失败后重新创建环境 | 测试报告与外部日志 |
| 模拟器回归 | 受控常驻或专用临时池 | 运行时和设备状态较复杂 | 指定模拟器运行时 |
| 真机测试 | 受控常驻 Runner | 需要物理设备、线缆和配对状态 | 设备配对信息 |
| App 签名与发布 | 少量受限常驻 Runner | 凭据与审批边界更重要 | 受控钥匙串和证书 |
Apple 已公布的 Mac mini M6 规格包括 12 核 CPU、最高 32GB 统一内存、最高 170GB/s 内存带宽,并提供 2.5Gb Ethernet 与 3 个 Thunderbolt 4 接口;这些参数说明它适合做单一职责的 macOS 构建节点,但不能直接推导出并发 Runner 数量。(Apple Mac mini 官方规格)
截至 2026 年 9 月 7 日,Mac mini M6 已发布,但首批设备预计从 2026 年 9 月 22 日开始送达,因此实际重置耗时、缓存命中收益和并发容量仍应以同一工作流实测为准。(Apple 发布 Mac mini M6 的官方公告)
如何降低不同任务之间的状态残留?
不要只依赖工作区清理。你还需要清理派生进程、用户目录、临时钥匙串、模拟器状态、包管理器缓存和网络代理配置;对无法验证清理结果的节点,下一任务不应继续调度。
需要签名与发布的团队:常驻可以,但必须缩小权限
iOS 签名任务是否放进临时 Runner,取决于凭据注入和销毁能否被审计。对于普通开发签名、短期测试证书或不涉及生产发布的任务,临时 Runner可以降低残留风险;对于生产发布,单纯注册 --ephemeral 并不足够。
更稳妥的做法是保留少量受控常驻发布节点,并同时限制:
- 可使用节点的仓库范围;
- 允许执行发布任务的分支;
- 工作流来源和审批环境;
- 证书导入、使用和销毁的日志;
- 发布账号的最小权限;
- 节点故障后的接管与证书撤销流程。
Apple Silicon 原生构建需要确认工具链和依赖是否支持 arm64;Apple 的开发者文档说明,Xcode 支持将 arm64 纳入通用二进制构建,旧的 x86_64 工具则可能通过 Rosetta 运行。(Apple Silicon 应用迁移文档) 这意味着迁移到 Mac Mini M6 时,不仅要验证签名,还要验证插件、命令行工具、脚本和二进制依赖的架构兼容性。
| 维度 | 临时签名节点 | 受控常驻签名节点 |
|---|---|---|
| 凭据暴露面 | 可缩短凭据存在时间 | 需要严格限制访问范围 |
| 环境复现 | 每次准备成本较高 | 工具链和钥匙串更稳定 |
| 真机与外设 | 不适合频繁重配 | 更适合固定设备 |
| 审计重点 | 注入、使用、销毁 | 使用范围、访问记录、定期重置 |
| 故障接管 | 需要备用镜像或备用设备 | 需要第二台同权限节点 |
如果你的签名流程还不能证明证书和账号能在任务结束后可靠销毁,就不要把它包装成“临时隔离”。先降低常驻节点权限,再逐步补齐重置和审计能力。
大规模平台工程:把扩缩容拆成五个控制面
大规模团队不能只看“当前有几台 Mac”。Mac Runner 的扩缩容至少要持续记录排队时长、任务执行时长、并发峰值、失败重跑次数和节点恢复时间。
GitHub 的 workflow_job Webhook 会提供任务排队、执行中和完成等生命周期事件,你可以据此触发注册、路由和回收流程;但 Webhook 到达延迟会影响扩容决策,不能把它当作绝对实时的容量信号。相关生命周期和临时 Runner要求可参考 GitHub 官方 Runner 管理说明。
建议将控制面拆为五部分:
- 容量判断:按队列长度、P95 等待时间和发布高峰记录决定是否增加节点。
- 注册与路由:新设备上线后自动注册,并写入用途、架构、Xcode 版本和权限标签。
- 日志转存:任务日志、系统日志和清理结果发送到外部存储。
- 环境管理:维护 macOS、Xcode、模拟器运行时和依赖工具的可复现镜像或基线。
- 失败回收:清理失败、更新失败或任务异常退出时,立即摘除节点,等待人工或自动重置。
不要把 Linux 容器的扩缩容经验直接套在 Mac 硬件上。GitHub 文档明确指出,Docker 容器 action 或服务容器要求 Linux Runner;macOS 节点的外设、系统权限和物理重启能力需要单独设计。
Runner 软件本身默认会自动更新;如果你关闭自动更新,就必须自行维护版本,并在新版本发布后 30 天内完成更新,否则新的 GitHub Actions 功能可能无法正常处理。(GitHub Runner 更新要求)
当某类标签没有可用节点时,任务不会立刻失败,而是会持续排队,直到 24 小时超时。因此,容量告警不能只看设备在线状态,还要看“有标签但无空闲节点”的任务数量。
最终落地:用双轨架构替代“全临时”或“全常驻”
你可以按下面的顺序完成一次试点:
- 先挑选一类不接触签名凭据的普通测试工作流。
- 为它创建独立 Runner group 和专用标签,不与发布节点混用。
- 记录任务排队、执行、清理和失败重跑数据。
- 在任务结束后验证工作区、进程、缓存、钥匙串和日志是否完成回收。
- 连续观察多个高峰周期,再决定是否增加临时节点。
- 将签名、真机和固定网络依赖迁移到受控常驻池。
- 为两类节点分别制定更新、清理、故障接管和权限复核标准。
| 决策条件 | 选择方案 |
|---|---|
| 公共仓库、外部拉取请求或依赖来源不可完全控制 | 临时 Runner,任务后必须验证重置 |
| 任务低频、仓库范围小、缓存收益明显 | 受限常驻 Runner |
| 需要生产签名、真机或固定外设 | 少量受控常驻节点 |
| 普通测试和发布任务同时存在 | 双轨架构,按标签与 Runner group 路由 |
| 还没有稳定的自动重置流程 | 暂不扩大临时池,先减少常驻权限 |
| 队列等待集中出现在发布高峰 | 增加短期发布容量,不扩大所有 Runner 权限 |
你还可以在 kvmsync 的帮助中心确认远程 Mac 节点的连接、交付和故障处理边界;如果需要核对短期节点的可用方式,再查看 kvmsync 的方案页面。
如果你当前使用的是一台长期在线的 Mac,常见缺点是缓存和凭据长期共存、任务之间容易残留状态,发布高峰还会被单节点队列拖慢;直接购买多台 Mac,则会增加闲置设备、系统更新和故障替换成本。更实际的做法是先把普通测试工作流迁移到临时 Mac 执行节点,用相同镜像和权限边界验证队列与清理效果,发布高峰再按需增加短期容量。对需要临时算力、短期 CI 扩容或新 Runner 验证的团队,租赁 kvmsync 的 Mac 节点通常比立即建设一整套长期硬件池更容易控制试错范围。
延伸阅读
kvmsync M4 远程节点
用 kvmsync 灵活部署 Mac Runner
按项目需求租用远程 Mac,临时任务用完即停,常驻构建也能稳定运行。