Mac 已恢复网络连接,但 GitLab Runner 始终离线。
最快解法:先确认专用 CI 账号是否已登录,再检查用户级 LaunchAgent、FileVault 解锁和 Keychain;不要为了“开机自动运行”直接改成 LaunchDaemon。
本周建议动作:先在一台非生产 Mac 上完成一次计划重启、一次异常断电恢复和一次真实 Xcode 构建验收。只有磁盘解锁、用户会话、Runner 接单、签名和模拟器都通过,才允许生产节点恢复发布流量。
谁该看这篇:
企业 IT 负责人:需要让异地 Mac 在重启后恢复在线,而不是等待人员现场登录。
研发效能负责人:需要判断离线来自 Runner 服务、任务路由还是 macOS 用户会话。
安全与发布负责人:需要在自动恢复、FileVault 和签名凭证隔离之间建立可审计边界。
先区分四种“已经开机”
GitLab Runner macOS 重启后离线,最容易误判的地方,是把“主机可访问”当成“构建环境已经恢复”。
一台 Mac 重新启动后,至少要分别确认:
- 网络接口已经起来,SSH 或 VNC 可以连接;
- FileVault 加密卷已经解锁;
- 专用 CI 账号已经建立图形用户会话;
- 该账号的 LaunchAgent 已加载,并且 Runner 能向 GitLab 请求任务。
这四个状态并不会自动同步。Mac 可以响应 SSH,但磁盘仍停在解锁界面;也可以已经解锁,但 CI 账号没有登录;还可能用户已经登录,Runner 却使用了错误账号下的配置文件。
GitLab 官方的 macOS 安装方式把服务文件放在用户目录下的 ~/Library/LaunchAgents/。Apple 对 launchd 的说明也明确区分了系统级 LaunchDaemon 和用户级 LaunchAgent:前者运行在系统上下文,后者运行在当前登录用户上下文。GitLab macOS 安装文档 与 Apple 的 launchd 服务说明 应作为排查起点。
第一轮不要重装。先记录:
whoami
id
gitlab-runner status
launchctl print "gui/$(id -u)"
ls -l ~/Library/LaunchAgents/gitlab-runner.plist
如果执行 gitlab-runner status 的账号不是安装 Runner 的账号,结果可能只代表当前账号,而不是生产 CI 账号。普通用户模式和 sudo 后的系统模式会读取不同的配置路径,因此必须先固定检查账号。
用户会话与 LaunchAgent
macOS 上的 Runner 为什么经常要等用户会话建立后才恢复?
对于官方支持的 macOS 服务模式,Runner 以用户级 LaunchAgent 运行。只有对应账号登录后,用户级 launchd 才会加载该账号目录中的服务配置。
这也是 GitLab Runner macOS 重启后离线的高频根因。主机开机不等于 CI 账号已登录,SSH 登录也不等于已经建立适合图形工具、Keychain 和模拟器的完整用户上下文。
GitLab 官方排障文档还记录了一个边界:通过 SSH 管理用户级服务时,可能遇到找不到对应用户域的问题。安装和启动操作应尽量在 Mac 的本地图形终端中完成。GitLab Runner macOS 安装与排障文档
正确的账号核查顺序
先确认 Runner 是由哪个账号安装:
ps aux | grep '[g]itlab-runner'
stat -f '%Su %N' ~/Library/LaunchAgents/gitlab-runner.plist
gitlab-runner verify
然后核对以下项目:
config.toml是否位于专用 CI 账号的用户目录;gitlab-runner.plist是否位于该账号的~/Library/LaunchAgents/;- 标准输出和错误日志目录是否存在且可写;
- Xcode、模拟器和签名工具是否由同一个账号执行;
- 是否有人曾经用
sudo重复安装,生成了另一份系统模式配置。
Apple 的启动服务说明显示,用户登录时会启动该用户的 launchd 实例,并加载用户级 LaunchAgent。这个上下文不是把 plist 文件复制到 /Library/LaunchDaemons/ 就能等价替代的。Apple 用户级 Agent 与系统级 Daemon 说明
是否应该把 Runner 改成 LaunchDaemon?
不建议把官方的用户级服务直接改成 LaunchDaemon。这样做会改变运行上下文,可能绕过登录用户能力,但同时失去 Keychain、图形会话和模拟器所依赖的用户环境。
如果企业确实需要在没有用户登录时运行某个系统级守护进程,应把它设计成独立的系统服务,再由用户会话中的 Agent 负责需要用户上下文的工作。不要简单修改 plist 的目录和权限后,把它当作等价方案。
⚠️ 经验:如果修复方案的第一步是“把 LaunchAgent 移到 LaunchDaemons”,先暂停变更。这个动作可能让页面状态短暂恢复,却把签名、模拟器和用户凭证问题推迟到真正发布时才暴露。
FileVault 与远程恢复
开启 FileVault 后,自动登录和无人值守恢复不能同时默认成立。Apple 官方说明指出,FileVault 开启时,自动登录选项不可用,系统需要在启动阶段进行手动认证。Apple 自动登录说明
因此,企业需要在两种目标之间做明确选择:
- 优先自动恢复:降低磁盘保护边界,使用受控的 CI 账号和严格网络访问控制;
- 优先静态数据保护:保留 FileVault,并设计远程解锁、人工恢复或备用节点流程。
对 Apple Silicon Mac 而言,Apple 的安全文档写明:在 macOS 26 或更高版本、已开启 Remote Login 且网络可达的条件下,重启后可以通过 SSH 解锁 FileVault。Apple FileVault 管理说明
这不是所有设备都自动具备的能力。企业还需要核对:
- Mac 是否为 Apple Silicon;
- macOS 是否满足对应版本条件;
- Remote Login 是否在重启后仍启用;
- 网络是否在磁盘解锁前可达;
- 远程解锁账号是否受控;
- 解锁动作是否被记录并纳入审计。
Apple 还提醒,开启 Remote Login 会扩大远程访问面。应限制允许登录的用户,而不是默认开放给所有账号。Apple Remote Login 配置说明
FileVault 开启后,远程恢复应如何设计?
先把“磁盘解锁”和“Runner 启动”分成两个操作阶段。远程运维人员需要先证明设备已经完成解锁,再确认专用 CI 账号建立了用户会话,最后才检查 LaunchAgent 和 Runner 状态。
如果远程解锁条件不满足,就不要把任务继续投递到该节点。企业应保留受控恢复账号、审批记录和操作日志,并为无法远程解锁的情况准备备用 Mac 或人工恢复流程。
在线状态与生产能力
Runner 页面显示在线,只能证明它已经向 GitLab 建立通信。它不能证明以下任务都能成功:
- 普通 Shell 脚本;
- Xcode 构建;
- 模拟器启动;
- 代码签名;
- 发布证书访问;
- 构建产物上传。
建议把验收拆成最小测试,不要直接触发完整发布流水线。测试顺序如下:
第一步,验证身份和路径:
whoami
pwd
xcode-select -p
gitlab-runner verify
第二步,验证普通脚本能否由 Runner 执行。检查工作目录、环境变量和文件权限。
第三步,验证 Xcode 构建。确认 xcodebuild 使用的是预期 Xcode 版本,而不是登录用户默认选择的另一套工具链。
第四步,验证模拟器。启动一个明确型号的模拟器,执行一个最小测试目标,记录是否出现图形会话、设备服务或权限错误。
第五步,验证签名。不要只检查证书文件存在,还要确认 CI 账号能访问对应的登录 Keychain、签名身份和 provisioning profile。
Runner 页面在线,但签名凭证无法访问时,排查顺序是什么?
先区分三种对象:登录 Keychain、系统 Keychain 和 CI 服务账号。运维人员在自己的账号中看到签名身份,并不代表 Runner 账号也能使用它。
重点检查:
- 证书是否导入 CI 账号的登录 Keychain;
- Keychain 是否在当前会话中解锁;
security命令是否以 CI 账号执行;- 签名身份是否受访问控制列表限制;
- CI 脚本是否依赖交互式密码输入;
- Xcode 是否首次启动过并完成许可确认。
如果任务不是失败,而是长时间没有输出,还要检查 Git 凭证助手和 Keychain 是否发生等待。某些凭证配置会让 git fetch 卡住,使表面上的“在线 Runner”无法完成实际构建。
残留服务与任务路由
有些“离线”并不是服务没启动,而是任务根本没有被路由到这台 Mac。
先检查是否存在重复配置:
find "$HOME" -name 'config.toml' -o -name 'gitlab-runner.plist'
launchctl list | grep -i gitlab
常见问题包括:
- 运维人员先用个人账号安装,后来又用 CI 账号安装;
- 旧 Runner 没有注销,新 Runner 使用了相似名称;
- 多个配置文件使用不同的
system_id; - plist 指向已经不存在的日志目录;
- Runner 标签与
.gitlab-ci.yml中的标签不匹配; - Runner 被限制在其他项目或群组范围;
- 受保护分支任务被普通 Runner 接收条件拦截。
Runner 标签决定哪些任务可以由该节点执行。项目、群组和实例 Runner 的作用范围也不同。检查页面状态时,必须同时核对 Runner 标签、项目范围、保护规则和任务定义。GitLab Runner 配置与标签说明
对于签名和发布节点,建议使用专用标签,例如 macos-signing,并限制到必要的项目范围。发布凭证不应由所有普通构建任务共享。
调试日志只能在受控窗口短时启用。Debug 日志可能包含变量和敏感信息,不应长期打开,也不应未经处理地发送到第三方日志系统。GitLab Runner 故障排查文档
重启验收清单
下面的清单用于生产准入。每次计划重启、系统更新、账号撤权或基础设施迁移后,都应重新执行。
- [ ] 记录重启前的 Runner 名称、标签、项目范围和当前状态。
- [ ] 记录专用 CI 账号名称、
config.toml路径和 plist 路径。 - [ ] 确认 FileVault 策略、恢复账号和远程解锁方式均有书面记录。
- [ ] 执行计划重启,确认网络是否恢复。
- [ ] 从外部网络测试 SSH 或其他受控远程管理入口。
- [ ] 确认磁盘已解锁,而不是只确认主机有响应。
- [ ] 确认专用 CI 账号已经建立用户会话。
- [ ] 确认 LaunchAgent 已加载,且日志目录可写。
- [ ] 执行
gitlab-runner verify,确认 Runner 身份没有变化。 - [ ] 提交一个最小 Shell 任务。
- [ ] 执行一个最小 Xcode 构建。
- [ ] 启动模拟器并完成一个最小测试。
- [ ] 执行代码签名测试,确认 Keychain 和发布身份可用。
- [ ] 检查任务是否命中正确标签,而不是被其他 Runner 接收。
- [ ] 在 GitLab 页面显示在线后,再观察一次真实构建任务。
- [ ] 记录失败证据、恢复动作和是否需要人工登录。
- [ ] 对异常断电、账号撤权和旧 Runner 残留分别重复验证。
准入标准不应是“页面显示绿色”。更可靠的标准是:远程恢复后,指定项目能够在受控账号下完成从接单到签名的最小生产路径。
如果单节点恢复仍需要人员到现场输入密码,企业就不能把它定义为无人值守构建节点。此时应把人工解锁时间、备用节点切换和发布窗口风险写进运维记录,而不是隐藏在“偶尔需要登录一次”的口头约定中。
现有 Mac 与备用节点
如果当前方案是办公室内一台长期运行的 Mac,常见缺点通常不在 CPU 性能,而在恢复链路:
- FileVault 解锁依赖现场人员;
- CI 账号和个人开发账号混用;
- 重启后没有自动执行真实 Xcode 验收;
- 单台设备故障时,没有隔离的备用节点;
- 签名 Keychain、模拟器和 Runner 配置缺少独立审计。
完成上述验收后,企业可以先保留自有 Mac,适合长期稳定负载、需要本地物理接口或必须完全控制硬件的团队。若问题集中在异地访问、临时扩容和备用构建能力,使用 MESHLAUNCH 的远程 Mac 作为隔离备用节点,会比继续堆叠个人账号、残留服务和现场解锁流程更容易形成可重复的恢复路径。
需要临时构建容量或验证备用节点时,可以先查看 MESHLAUNCH 的远程 Mac 方案;如果团队正在评估 Mac mini 作为长期或备用构建机,也可以对照 Mac mini 远程租赁与部署选项,再决定采用自购、租赁还是混合架构。