Mac 已经恢复 SSH,但 GitLab 页面仍显示 Runner 离线。
最快解法:先确认故障位于 Mac 主机、图形会话、用户级 LaunchAgent、Runner 注册状态还是网络层,不要直接重装;涉及代码签名或 Simulator 时,恢复正确的用户登录会话,而不是擅自改成系统级 LaunchDaemon。
这篇文章适合维护单台远程 Mac CI 节点、重启后需要手动恢复 Runner 的独立开发者,也适合负责多项目构建、代码签名和模拟器任务的 DevOps、发布工程师。研发平台团队还可以把文末清单改造成远程 Mac 节点的重启验收标准。
先判断:到底是哪一层离线
“GitLab Runner Mac 重启后离线”不一定代表 Runner 服务没有启动。远程排障时,至少要区分以下 4 种状态:
- 主机不可达:SSH、VNC 或网页控制台都无法连接。先处理 Mac 启动、网络、电源或远程访问通道。
- 主机已恢复,但图形用户未登录:SSH 可以登录,运行 Runner 的用户却没有进入桌面。普通命令行任务可能可以手动执行,但 Keychain、代码签名和 iOS Simulator 通常不能据此判定可用。
- Runner 进程未运行:用户已经进入图形会话,但服务状态异常,或者 LaunchAgent 没有加载。
- Runner 在线但任务排队:Runner 能连接 GitLab,却没有匹配任务标签、项目范围或执行条件。
GitLab 官方确认,macOS 上的 Runner 采用用户级 LaunchAgent 运行。它属于当前登录用户,不是系统级 LaunchDaemon。因此,重启后即使 Mac 已经联网,用户会话没有恢复,Runner 也可能不会自动上线。查看 GitLab 官方的 macOS 安装与服务模式说明
先记录失败现场
假设目标账户为 <runner-user>,主机名为 <mac-host>,项目为 <project>:
- 你通过 SSH 登录
<mac-host>,系统命令和磁盘检查都正常。 - GitLab 项目页面仍显示 Runner 离线。
- 执行
gitlab-runner start,返回Could not find domain for。 - 你使用
sudo重新安装服务,进程看似启动,但签名任务随后失败。
这个现场包含两个常见误判。SSH 可达不等于图形用户会话已经建立;使用 sudo 也可能把配置切换到 root 的路径,导致原有用户配置、Keychain 和开发工具环境不再一致。
先保存以下信息:
who
id
hostname
date
gitlab-runner status
如果 SSH 登录的是管理员账户,而 Runner 属于 <runner-user>,后续排查必须切换到正确用户。不要把管理员账户的 config.toml、环境变量和日志路径当成 Runner 的实际配置。查看 GitLab Runner 命令与配置路径
第一步:确认远程 Mac 和用户会话
先测试 SSH:
ssh <runner-user>@<mac-host>
如果 SSH 失败,不要立即判断 GitLab Runner 有问题。Mac 可能停在磁盘解锁界面、用户登录界面,或者网络服务尚未恢复。
如果 SSH 正常,继续打开 VNC 或网页控制台。你要确认的是 <runner-user> 已经进入桌面,而不是只看到了登录窗口。
macOS 的 LaunchAgent 运行在已登录用户的上下文中。它可以访问该用户的环境、权限和部分用户级服务;LaunchDaemon 则面向系统级后台任务,不等价于一个拥有桌面会话的构建账户。查看 Apple 对 Service Management 的说明
这一步会直接影响以下任务:
- 读取登录用户 Keychain 的代码签名任务;
- 使用 iOS Simulator 的测试任务;
- 需要图形会话或开发者工具授权的 Xcode 流程;
- 依赖用户级 Homebrew、Ruby、Node.js 或环境变量的脚本。
如果只是运行不需要签名和 Simulator 的命令行构建,缺少图形会话可能暂时不暴露。但这不能证明生产节点已经恢复。
第二步:在图形终端检查 LaunchAgent
如果日志出现 Could not find domain for,优先判断服务是否曾经通过纯 SSH 会话安装或启动。
通过 VNC 或网页控制台进入 <runner-user> 的桌面,打开终端后执行:
whoami
gitlab-runner status
ls -l ~/Library/LaunchAgents/gitlab-runner.plist
相关操作应在目标用户的图形会话中执行。纯 SSH 会话可能没有对应的 GUI bootstrap domain,因此 launchctl 找不到管理用户级 LaunchAgent 所需的域。
正确的处理顺序是:
- 进入目标用户的图形会话;
- 确认
whoami返回<runner-user>; - 检查 plist 是否存在;
- 查看
gitlab-runner status; - 只有在确认服务文件缺失时,才执行安装或重新加载。
不要直接把 Runner 改成 LaunchDaemon。即使进程能够以 root 启动,也可能失去用户 Keychain、桌面会话和 Simulator 访问能力。对于 Xcode 发布任务,这种“服务在线、任务失败”的状态比完全离线更难发现。
第三步:检查 plist、日志和账户权限
先备份现有配置,不要在没有证据时删除注册信息:
mkdir -p ~/.gitlab-runner-backup-<date>
cp ~/.gitlab-runner/config.toml \
~/.gitlab-runner-backup-<date>/config.toml
cp ~/Library/LaunchAgents/gitlab-runner.plist \
~/.gitlab-runner-backup-<date>/gitlab-runner.plist
再检查关键路径:
ls -l /usr/local/bin/gitlab-runner
ls -ld ~/.gitlab-runner
ls -l ~/.gitlab-runner/config.toml
grep -n "StandardOutPath\|StandardErrorPath\|ProgramArguments" \
~/Library/LaunchAgents/gitlab-runner.plist
不同错误对应不同方向:
killed: 9:检查 plist 中的日志目录是否存在,以及 Runner 用户是否有写入权限。exit status 134:先确认是否已有 Runner 进程,避免重复启动;再检查服务安装状态和日志。Load failed: 5: Input/output error:检查 plist、二进制路径、日志目录和账户权限。Operation timed out:转向代理、防火墙、路由、证书和 GitLab 实例可达性,不要继续盲目重装。
如果日志路径指向不存在的目录,可以先创建目录:
mkdir -p ~/gitlab-runner-log
chmod u+rwx ~/gitlab-runner-log
然后确认 plist 中的输出路径确实位于该目录。路径中的用户名、目录和文件名都必须替换成实际值,不能直接照抄其他节点。
重装只适合作为末级动作。只有在确认 plist 损坏、二进制路径失效或安装过程未完成时,才考虑:
gitlab-runner uninstall
gitlab-runner install
gitlab-runner start
重装前必须保留 config.toml、plist 备份和注册信息。重装只能修复服务文件问题,不能自动修复 FileVault、代理、标签、证书或代码签名权限。
第四步:把 Runner 在线和任务可执行分开验证
先在正确用户下执行:
gitlab-runner status
gitlab-runner list
gitlab-runner verify
status 用于观察本地服务进程,verify 用于验证已注册 Runner 与平台的连接。两者都正常,也不代表当前任务一定能够被领取。
继续检查 GitLab 项目设置和流水线配置:
- Runner 是否属于正确的项目或组;
- Runner 是否被暂停;
- Runner 标签是否包含任务要求的
macos、ios或自定义标签; - 任务是否允许无标签 Runner;
- 是否已有其他任务占用节点;
- 受保护分支和受保护 Runner 是否匹配。
例如,任务声明:
build_ios:
tags:
- macos
那么 Runner 必须拥有对应的 macos 标签,否则任务可能持续排队。Runner 页面显示在线,只能说明它具备连接平台的能力,不能说明标签路由正确。查看 GitLab 官方的 Runner 标签匹配规则
如果 Runner 进程在线但连接异常,可以采集调试输出:
gitlab-runner --debug verify
同时检查代理和系统时间:
env | grep -i proxy
scutil --proxy
date
浏览器能够打开 GitLab,不代表 Runner 使用的进程环境也能正常连接。Runner 可能继承不同的代理变量、证书链或用户级配置。
配置文件修改后,通常不需要马上重新注册 Runner。GitLab Runner 会定期检查配置变化,文档给出的默认检查间隔是 3 秒。但如果修改了服务路径、监听设置或 LaunchAgent 内容,仍应根据具体变更决定是否重启。查看 GitLab Runner 高级配置说明
第五步:处理 FileVault 和自动登录限制
FileVault 开启后,重启流程可能包含两个不同阶段:
- 解锁启动磁盘;
- 登录运行 Runner 的用户。
只完成第一步,不代表 LaunchAgent 已经恢复。你还需要通过图形控制台进入 <runner-user>,再验证服务、Keychain 和开发工具状态。
如果组织允许自动登录,可以检查系统设置。但不要把关闭 FileVault 当成默认修复方案。Apple 说明,FileVault、设备管理策略和账户安全设置都可能限制自动登录能力。查看 Apple 关于 Mac 自动登录限制的说明
更稳妥的远程 Mac 交付方式是保留双通道:
- SSH:处理日志、配置、网络和命令行任务;
- VNC 或网页控制台:处理磁盘解锁、用户登录、Keychain、Simulator 和图形授权。
如果只有 SSH,没有图形控制台,节点即使能够偶尔完成命令行构建,也不适合承载依赖签名和模拟器的生产流水线。
FAQ:几个容易混淆的恢复场景
为什么 Mac 重启后 Runner 没有自动上线?
因为 macOS Runner 运行在当前用户的 LaunchAgent 中。Mac 重新通电或恢复网络,只能说明主机可能已经可达;如果运行 Runner 的账户还停留在登录界面,LaunchAgent 就没有进入目标用户上下文。涉及 Keychain、签名或 Simulator 的任务,还必须验证图形会话和用户权限。
SSH 启动 Runner 时为什么找不到 launchctl domain?
纯 SSH 会话可能没有对应用户的 GUI bootstrap domain。此时 launchctl 无法正确管理用户级 LaunchAgent。你应通过 VNC、网页控制台或本机图形终端进入运行 Runner 的账户,再执行状态检查和启动操作,不要使用 sudo 强行改成系统级服务。
Runner 显示在线,为什么 macOS CI 任务仍然排队?
Runner 在线只代表它能和 GitLab 通信。任务是否执行,还取决于标签、项目范围、分支保护、并发限制和无标签任务设置。先比较 .gitlab-ci.yml 中的 tags 与 Runner 实际标签,再检查 Runner 是否被暂停或已被其他任务占用。
开启 FileVault 后,远程 Mac Runner 怎样恢复?
保留图形控制台,先完成磁盘解锁,再登录运行 Runner 的用户。随后检查 LaunchAgent、Keychain、签名和 Simulator。不要为了追求无人值守而默认关闭 FileVault;如果组织策略不允许自动登录,就应把人工解锁步骤纳入节点交付和故障恢复标准。
用真实重启完成验收
修复后至少执行一次真实重启。只运行一次 gitlab-runner start,不能证明重启恢复链路有效。
- [ ] 记录
<runner-user>、<mac-host>、项目名、Runner 标签和配置文件路径。 - [ ] 先完成一个普通 CI 任务,确认日志、产物和缓存正常。
- [ ] 通过 SSH 记录主机可达性、当前用户和系统时间。
- [ ] 通过 VNC 或网页控制台确认磁盘状态和图形登录状态。
- [ ] 在图形会话中执行
gitlab-runner status。 - [ ] 执行
gitlab-runner verify,确认注册信息仍能连接平台。 - [ ] 检查
~/Library/LaunchAgents/gitlab-runner.plist是否存在。 - [ ] 检查标准输出和错误日志目录是否存在且可写。
- [ ] 重启 Mac,不使用手动启动作为唯一恢复动作。
- [ ] 重启后重新确认 SSH、图形会话和 Runner 服务状态。
- [ ] 提交一个不依赖图形能力的 Shell 构建任务。
- [ ] 再提交一个依赖 Keychain、代码签名或 Simulator 的 Xcode 任务。
- [ ] 记录任务开始时间、Runner 上线时间、日志片段和最终结果。
- [ ] 如果必须人工登录后才能恢复,明确标记为“需人工恢复”节点。
- [ ] 如果任务标签不匹配,修正 CI 配置或 Runner 标签后再次测试。
Shell executor 会直接以 Runner 用户权限执行脚本。GitLab 官方提醒,这种执行方式只适合可信项目;共享节点可能暴露其他项目的代码、凭据和任务令牌。查看 GitLab Shell executor 安全说明
因此,多项目节点不能只验收“能不能跑”。还要确认项目边界、工作目录清理、凭据隔离和访问权限。若多个互不信任的项目共用静态 Shell executor,安全风险可能比一次重启故障更严重。
什么时候该更换远程 Mac 交付方案
如果你现在使用办公室里的 Mac mini,重启后常见的实际缺点包括:没有稳定的远程图形控制台、FileVault 解锁必须等人在现场、家庭或办公室网络存在端口与路由问题,以及单台设备故障时没有快速替换节点。
如果改用普通 Linux 云主机,又会遇到 Xcode、代码签名、Keychain 和 iOS Simulator 无法完整复现的问题。虚拟 macOS 环境也可能在硬件能力、图形会话或开发工具兼容性上增加新的变量。
更稳妥的做法,是先用一台同时支持 SSH 与图形控制台的远程 Mac 复现完整重启流程。你可以通过 MACCOME 的远程 Mac 方案 验证用户登录、LaunchAgent、签名和 Simulator 的恢复链路,再决定是否迁移生产任务或增加备用节点。若你还在评估 Mac mini 类型的远程算力,可进一步查看 Mac mini 云算力方案。
稳定的 GitLab Runner,不是页面显示在线就算完成,而是重启后能在预期用户会话中恢复,能领取正确标签的任务,并且签名与 Simulator 流水线也能通过复测。如果当前节点只能依赖现场登录、人工解锁或临时改权限,MACCOME 的远程 Mac 更适合作为短期测试环境、备用构建节点或迁移前的验证节点。