官方当前资料列出的 Machine Runner 3 支持 macOS X 11.2 及更高版本,并覆盖 Intel 与 Apple Silicon。这意味着,CircleCI Machine Runner 3 远程 Mac 部署可以落到真实的 Apple Silicon 主机上,但“Runner 在线”只能证明代理进程能通信,不能证明 Xcode、签名、重启恢复和清理流程已经达到生产要求。CircleCI Runner 平台支持说明

本周建议动作:先用无签名、可丢弃的测试项目完成资源类路由,再接入 Xcode 构建;最后执行一次计划内重启和工作区清理验收。标准化、无需固定工具链的任务继续使用托管执行器;只有需要固定 Xcode、访问私有依赖、连接内网或控制签名凭据时,才部署自托管 Runner。

维护 CircleCI iOS 或 macOS 流水线、需要固定工具链的开发者,适合看这篇。
需要接入签名、私有仓库或内网服务的 DevOps 工程师,适合看这篇。
负责远程 Mac 权限隔离、更新与故障恢复的平台维护者,也可以直接按时间线执行。

01

先判断任务是否值得迁移到远程 Mac

CircleCI self-hosted runner 的价值,不是把所有任务换到另一台机器,而是把执行环境的控制权交给你。官方说明中,Machine Runner 会直接安装在虚拟机或物理机上,任务就在该 Runner 所在的环境中执行。CircleCI 自托管 Runner 工作模型

这会带来几个现实限制:

  • 环境不会自动变干净。 上一次任务留下的 DerivedData、依赖目录、临时文件或钥匙串状态,可能影响下一次构建。
  • 权限边界由主机决定。 Runner 执行账户能访问什么目录、能否读取 Keychain、能否连接内网,不是 CircleCI 页面上的“在线”状态可以证明的。
  • 工具链版本需要你维护。 Xcode、Command Line Tools、Homebrew、Ruby、Node.js 和 CocoaPods 的升级,都可能改变构建结果。
  • 并发能力受真实主机限制。 单台 Mac 并不等于可以无限并行。多个任务共享 CPU、内存、磁盘和登录会话时,排队与资源争用会直接反映到流水线时间。
  • 远程管理通道必须独立验证。 SSH 或网页控制台可用,不代表 Runner 的后台服务、Xcode 调用和重启后的自动恢复都正常。

因此,你可以按下面的条件分流:

  • 若任务只需要 Linux 容器、标准依赖和普通单元测试,选托管执行器
  • 若任务必须使用固定 Xcode、真实 macOS SDK、私有网络或本机证书,选远程 Mac 上的 Machine Runner 3
  • 若一个流水线既有普通测试,又有 Xcode 打包,采用双轨流水线:普通任务留在托管环境,macOS 专属任务路由到远程 Mac。
02

第 0 小时:画出权限、目录和恢复边界

在远程 Mac 上不要直接使用日常管理员账户跑构建。先建立专用执行账户,例如:

sudo sysadminctl -addUser <ci-user> -password -
sudo mkdir -p /Users/<ci-user>/ci/{workdir,cache,logs}
sudo chown -R <ci-user>:staff /Users/<ci-user>/ci

<ci-user>、目录名和主机名都只是占位符。生产环境中应使用团队的账户管理流程,不要把真实密码写入脚本、仓库或构建日志。

建议把目录职责拆开:

目录 用途 默认处理方式
/Users/<ci-user>/ci/workdir 当前任务源码与中间文件 任务后清理
/Users/<ci-user>/ci/cache 可重建的依赖缓存 定期淘汰
/Users/<ci-user>/ci/logs Runner 与任务日志 限制权限并轮换
/Users/<ci-user>/ci/secrets 不建议长期存放敏感凭据 尽量改用 Keychain 或临时注入

CircleCI 的 Machine Runner 3 使用配置文件定义认证、节点名称、工作目录和日志行为;官方配置参考还说明,环境变量可能优先于 YAML 配置,因此你需要检查系统服务环境,避免同名变量覆盖文件内容。Machine Runner 3 配置参考

这一阶段的停止条件很明确:如果你还不能回答“哪个账户执行、源码放在哪里、缓存能否删除、签名文件谁能读”,不要继续安装 Runner。

03

第 1 小时:创建 namespace、resource class 和令牌

CircleCI 自托管 Runner 的任务路由依赖两个对象:namespace 和 resource class。一个组织只能声明一个唯一 namespace;resource class 则用于把特定任务匹配到对应的 Runner 池。namespace 与 resource class 规则

你可以通过网页界面创建,也可以使用 CircleCI CLI。命令中的内容全部使用占位符:

circleci namespace create <namespace> --org-id <organization-id>

circleci runner resource-class create \
  <namespace>/<resource-class> \
  "<description>" \
  --generate-token

命名时保持简单。namespace 使用小写字母、数字、下划线或短横线;resource class 必须与仓库配置中的字符串完全一致,包括大小写。比如:

resource_class: <namespace>/<resource-class>

怎样给远程 Mac 设计 resource class?
把硬件、Xcode 版本和用途反映在资源类名称或描述中,例如 <namespace>/macos-xcode-build。如果后续还要增加不同 Xcode 版本或签名节点,可以分别创建不同资源类,避免普通测试任务误入发布节点。resource class 是路由标签,不是 CPU 或内存规格的自动申请器。Resource class 配置说明

令牌只在创建时显示一次。不要写入 .circleci/config.yml,也不要通过 echo 直接打印到日志。建议保存到受控的密码管理系统,并记录以下信息:

  • 所属组织与 resource class;
  • 创建人与创建日期;
  • 当前使用的主机;
  • 轮换责任人;
  • 撤销后如何重新注册 Runner。

若令牌疑似泄露,先撤销或轮换,再重启对应 Runner。不要等到构建日志出现异常后才处理。

04

第 2 小时:在真实 Mac 上安装并启动 Machine Runner 3

CircleCI 的 macOS 安装方式可能随 Runner 包和支持状态调整。安装时应以官方 macOS 安装指南当天显示的下载、校验和安装命令为准,不要长期复制旧博客中的安装包地址。

准备阶段先检查基础工具:

uname -m
sw_vers
which curl
which brew
which tar

然后按官方页面完成 Runner 包下载、校验和安装。校验失败、系统版本不在支持范围内,或下载包的签名和公证状态无法确认时,应停止,不要使用 --no-verify 一类方式绕过检查。

安装后配置以下字段:

api:
  auth_token: <runner-auth-token>

runner:
  name: "<runner-hostname>"
  working_directory: "/Users/<ci-user>/ci/workdir"

logging:
  level: "info"
  format: "text"

上面的字段名用于说明结构,实际路径和启动方式以当前官方 macOS 页面为准。尤其要注意:Machine Runner 3 与旧版 Launch Agent 的文件名、服务管理方式并不应混用。官方已经将 Machine Runner 3 作为替代方案,旧命令只能用于排查历史安装。

macOS 无图形界面运行时,还要验证服务是否能在重启后加载。官方安装说明分别给出了 GUI 和非 GUI 会话的 launchctl bootstrapenablekickstartprint 流程。

首次启动后至少检查三处:

launchctl print user/$(id -u)/com.circleci.runner
ps aux | grep -i circleci
tail -n 100 <runner-log-path>

同时在 CircleCI 控制台的 Runner inventory 中确认节点名称和 resource class。控制台显示在线只是第一项证据;进程、服务状态、日志和 inventory 必须相互吻合。

05

第 3 小时:先完成 Xcode 工具链验收,再接入正式项目

Apple 文档明确区分了完整 Xcode 与 Command Line Tools。xcodebuildxctrace 随完整 Xcode 提供,不属于单独安装的 Command Line Tools 包。Apple 的 Command Line Tools 说明

因此,远程 Mac 上不要只执行:

xcode-select --install

就认为 Xcode CI 已经准备完成。先确认完整工具链:

xcode-select --print-path
xcodebuild -version
xcrun --find xcodebuild
xcrun simctl list devices

如果主机安装了多个 Xcode,明确选择构建版本:

sudo xcode-select --switch /Applications/<Xcode.app>
xcode-select --print-path

也可以只对单次任务使用 DEVELOPER_DIR,避免改变整台主机的默认工具链:

DEVELOPER_DIR="/Applications/<Xcode.app>" \
xcodebuild -version

Apple 文档说明,xcode-select --switch 需要管理员权限,而 DEVELOPER_DIR 可在调用命令时临时覆盖开发者目录。Xcode 命令行工具选择方法

自托管 Runner 调用 Xcode 时要检查什么?
在 job 中声明 machine: true,并填写刚创建的 resource class。最小验证配置如下:

version: 2.1

jobs:
  verify-macos-runner:
    machine: true
    resource_class: <namespace>/<resource-class>
    steps:
      - checkout
      - run:
          name: Verify host and Xcode
          command: |
            whoami
            sw_vers
            xcode-select --print-path
            xcodebuild -version
      - run:
          name: Build disposable project
          command: |
            xcodebuild \
              -project <SampleProject>.xcodeproj \
              -scheme <SampleScheme> \
              -sdk iphonesimulator \
              -configuration Debug \
              build

workflows:
  verify:
    jobs:
      - verify-macos-runner

CircleCI 官方配置参考要求使用 machine: trueresource_class: <namespace>/<resource-class>,任务才会路由到对应的自托管 Runner。

验收证据包括:

  • job 日志中的 whoami
  • sw_vers 和 Xcode 版本;
  • resource class;
  • xcodebuild 退出状态;
  • 测试结果或构建产物;
  • Runner 日志中的领取与回传记录。

如果任务停在 Preparing Environment,先检查 Runner 二进制执行权限、服务日志、出站网络和令牌状态。CircleCI 的排障文档也提示,macOS 首次安装可能遇到启动代理权限问题。Machine Runner 排障文档

06

第 4 小时:把普通构建、签名和发布拆开

签名任务不要和普通编译共用完全相同的权限。至少拆成以下两类:

  • 无签名构建:只访问源码、依赖缓存和测试工具。
  • 签名发布:额外访问临时 Keychain、证书、描述文件或发布所需凭据。

怎样隔离签名证书与构建账户?
最稳妥的做法是让非签名 job 使用低权限账户,把签名任务路由到单独 resource class,或者在同一主机上使用独立的临时 Keychain。不要把真实证书名称、团队账号、令牌或密码写进示例配置。

签名任务中应明确:

security create-keychain -p "<temporary-password>" <temporary-keychain>
security list-keychains -d user -s <temporary-keychain>
security default-keychain -s <temporary-keychain>

这里的密码和名称只能由安全注入机制提供。任务结束后执行清理:

security delete-keychain <temporary-keychain>
rm -rf "$HOME/Library/Developer/Xcode/DerivedData/<project-pattern>"

清理命令具有破坏性。执行前必须确认路径只指向当前任务生成的临时内容,并保留失败任务的日志和退出状态。不要为了“清理干净”直接删除整个用户目录或全局 Keychain。

若需要 UI 测试,还要单独处理 macOS 的辅助功能与应用控制权限。无头 CI 环境无法人工点击授权弹窗,因此应在上线前确认测试账户和系统权限已经按团队流程预配置。

07

第 5 小时:执行重启、恢复和污染测试

重启远程 Mac 后,怎样确认 Runner 能继续工作?
不能只执行重启命令后看控制台状态。你需要验证服务是否自动加载、Runner 是否重新领取任务、Xcode 是否可调用,以及 SSH 或网页管理通道是否恢复。

建议按下面顺序操作:

  1. 先确认当前没有正在运行的生产发布任务。
  2. 保存 Runner 日志、inventory 状态和主机当前工具链信息。
  3. 执行计划内重启。
  4. 通过 SSH 或网页控制台重新连接。
  5. 检查 launchctl print、Runner 进程和最近日志。
  6. 重新提交无签名测试任务。
  7. 检查任务是否落到预期 resource class。
  8. 删除测试工作区,确认下一次任务不会读取残留文件。
  9. 运行一次 Xcode 构建和测试。
  10. 记录失败任务后的恢复步骤。

CircleCI 自托管 Runner 需要访问其 Runner 服务端点。官方支持资料要求确认到 runner.circleci.com 的出站连接,并使用 443 端口;网络策略、代理或 DNS 变化都可能让节点看似在线却无法领取任务。自托管 Runner 连接排查

下面这份清单可以作为上线门槛:

  • ✅ inventory 中节点在线,名称与主机对应;
  • ✅ 使用正确的 namespace 与 resource class;
  • ✅ 非交互账户可以调用 xcodebuild
  • ✅ Xcode 版本和项目要求一致;
  • ✅ 任务失败后工作区、临时 Keychain 和敏感文件会清理;
  • ✅ 重启后服务自动恢复;
  • ✅ 令牌轮换后可以重新启动 Runner;
  • ✅ SSH、日志和必要的远程管理通道可用;
  • ❌ 只凭控制台在线状态直接迁移生产发布;
  • ❌ 把签名证书和普通构建放在同一长期可读目录;
  • ❌ 让构建脚本依赖某个工程师的图形化登录会话。
08

远程 Mac 部署的阶段对照与停止条件

阶段 你要完成的动作 可观察证据 未通过时的处理
任务选型 确认必须使用 macOS、Xcode、内网或签名 流水线分工文档 普通任务回退托管执行器
资源注册 创建 namespace、resource class 和令牌 inventory、配置字符串 停止安装,先修正命名或权限
节点安装 安装 Runner 3 并建立服务 进程、日志、launchctl 检查官方安装步骤与网络
首次构建 路由到远程 Mac 并执行 Xcode job 日志、产物、退出码 不接入真实签名项目
发布接入 分离账户、Keychain 和发布资源 权限审计、清理日志 保持无签名构建
重启验收 重启后自动恢复并再次领取任务 恢复任务、Runner 日志 不允许生产上线
09

三种执行方案怎么选

方案 适合的任务 优点 主要代价
CircleCI 托管执行器 标准化测试、普通构建、无需内网访问的任务 少维护主机,环境边界更清晰 工具链、网络和签名控制较少
远程 Mac 自托管节点 固定 Xcode、私有依赖、内网服务、签名发布 可控制真实 Mac 环境、账户和网络 需要维护更新、清理、重启与权限
双轨流水线 同时存在普通任务和 macOS 专属任务的团队 把维护成本限制在必要范围 路由、缓存和产物管理更复杂

如果你还没有可持续运行的真实 Mac 节点,可以先查看 Mac mini M4 租赁方案,再根据项目周期对照 Mac mini M4 租赁价格。选择节点时,优先确认是否满足你的 Xcode、网络、远程管理和持续运行要求,而不是只比较名义上的 CPU 参数。

10

最后的上线判断

如果当前方案是本地 Mac 临时充当 Runner,常见缺点是:工程师关机或退出登录后任务中断;多人共用一台机器造成缓存与证书污染;硬件升级和故障恢复需要人工处理。如果改用普通 Linux 云主机,又无法直接提供完整 macOS、Xcode 和 Apple SDK 环境,跨系统绕行通常会增加维护脚本和排错成本。

因此,固定重负载、必须接物理设备或需要完全掌控硬件的团队,仍应考虑自购 Mac 并自行维护。若你的需求是按项目周期获得一台持续在线的真实 Mac,先部署无签名任务、完成重启恢复和工作区清理,再把生产流水线迁移到 VpsMesh 的远程 Mac,会比一开始就把签名发布任务直接放上去更稳妥。