GitLab 官方文档明确说明,macOS 上的 GitLab Runner 使用用户态 LaunchAgent,不是系统级 LaunchDaemon;它跟随已登录用户启动和退出,并依赖用户会话访问钥匙串与图形界面。GitLab macOS 安装文档
这直接决定了本周的部署建议:先准备专用普通账户,再安装 Shell executor,随后验证用户会话、Xcode 工具链、签名隔离和整机重启恢复。 Runner 页面显示在线,只能证明它能向 GitLab 报到,不能证明它已经具备生产构建能力。
如果你正在把 iOS 项目从手工打包迁移到 GitLab CI,应重点关注第一条可复现流水线。
如果你负责跨平台 CI 基础设施,应重点关注常驻方式、权限边界和恢复机制。
如果团队没有长期在线的真实 Mac,这篇文章也能帮助你判断远程 Mac 节点的配置与交付条件。
先判断:这个任务是否真的需要远程 Mac
macOS GitLab Runner 部署不适合所有构建任务。普通的后端编译、容器构建和多数脚本任务,可以继续放在 Linux 节点上。只有任务明确依赖以下能力时,Mac 节点才有必要:
- 需要 Xcode、Apple SDK 或
xcodebuild。 - 需要 iOS 模拟器、macOS 图形会话或用户钥匙串。
- 需要证书、描述文件和 Apple 平台签名。
- 需要在 Apple Silicon 环境中验证架构、依赖或运行行为。
实际维护中,最容易被低估的是下面三个限制。
第一,Shell executor 会直接在 Runner 主机上执行项目脚本。GitLab 官方明确提示,Shell executor 对不可信代码存在较高风险,任务可能读取同一用户权限范围内的文件或执行任意命令。Shell executor 安全说明
第二,macOS 的服务模式和 Linux 不同。你不能简单把 Runner 改成 root 守护进程,然后期待它同时访问钥匙串、模拟器和图形会话。GitLab 当前文档把用户态 LaunchAgent 作为 Mac 上支持的服务模式。
第三,Xcode 构建成功不等于发布成功。无签名测试、归档、导出和上传使用的凭据不同。若一开始就把发布证书放进所有分支可触达的 Runner,故障排查和密钥泄露的成本都会上升。
02第一步:划清 Runner 范围和可信代码边界
先决定 Runner 属于项目、群组,还是多个项目共享。
项目级 Runner 适合单一应用,权限和依赖最容易控制。群组级 Runner 适合多个受控仓库共用同一套 Xcode 工具链。共享节点则应非常谨慎,尤其不能把持有发布证书的 Mac Runner 暴露给不熟悉的仓库。
建议在注册前写下四项条件:
- 哪些项目可以调用这个 Runner。
- 哪些分支可以执行签名和发布任务。
- 哪些任务只允许无签名构建。
- Runner 账户可以读取哪些目录和凭据。
标签不要只写 macos。更好的做法是区分任务能力,例如:
build_ios:
tags:
- macos
- xcode
- ios-build
发布任务则使用单独标签,例如 ios-release,并配合受保护分支。这样做的目的不是让配置看起来复杂,而是避免测试任务和发布任务无意中落到同一执行路径。
第二步:用专用账户完成安装和注册
创建一个只承载 CI 作业的普通 macOS 用户。不要直接使用个人开发账户,也不要把 Runner 运行在管理员账户下。这个账户需要能够访问 Xcode、项目依赖、构建目录和专用钥匙串,但不应拥有不必要的系统管理权限。
GitLab 官方安装页提供 Apple Silicon 与 Intel 架构的 Runner 二进制下载方式。安装时必须根据实际处理器选择对应文件,不能把另一种架构的安装命令直接复制过来。GitLab Runner macOS 安装步骤
示例中的路径和令牌只使用占位符:
sudo curl --output /usr/local/bin/gitlab-runner \
"<按官方页面选择的 macOS Runner 下载地址>"
sudo chmod +x /usr/local/bin/gitlab-runner
gitlab-runner register \
--url "<GitLab 实例地址>" \
--token "<RUNNER_AUTH_TOKEN>" \
--executor "shell" \
--description "macos-ios-build" \
--tag-list "macos,xcode,ios-build"
令牌应从 GitLab 控制台当前生成的 Runner 注册流程获取。不要把真实令牌写进仓库、工单、截图或流水线日志。注册范围确定后,再把 Runner 设置为项目或群组专用,而不是为了方便直接扩大可见范围。
Shell executor 是 iOS 和 macOS 本机构建常用的选择,因为它能直接调用主机上的 Xcode、模拟器和钥匙串。但它的隔离能力有限,只适合可信代码、可信仓库和你能控制的主机环境。
04第三步:处理用户会话常驻,别把 SSH 当成桌面登录
这是 macOS GitLab Runner 部署最容易失败的阶段。
GitLab 文档说明,Mac Runner 的 LaunchAgent 属于当前登录用户。用户登录时启动,用户退出时停止;它还需要用户会话来访问钥匙串和 iOS Simulator。
因此,下面这种现象并不矛盾:
- 你通过 SSH 执行安装命令,注册成功。
- 控制台短时间显示 Runner 在线。
- SSH 断开或机器重启后,Runner 变成离线。
- 重新登录图形桌面后,Runner 又恢复。
安装和启动应在 CI 专用账户的 macOS 图形终端中完成。官方命令形式如下:
cd ~
gitlab-runner install
gitlab-runner start
gitlab-runner status
安装过程会创建用户目录下的 LaunchAgent 配置,并交给 launchctl 管理。GitLab 还特别提醒,LaunchAgent 需要图形登录会话才能正常加载。GitLab Runner 服务配置说明
重启前,按这个顺序验证:
- 正常退出 CI 账户,再重新登录。
- 断开 SSH,确认 Runner 是否仍在线。
- 临时中断网络,再观察恢复后的连接状态。
- 执行整机重启,等待网络和用户会话恢复。
- 查看 Runner 状态、服务日志和最近一次任务时间。
自动登录可以改善重启后的可用性,但会降低物理设备被接触时的保护能力。是否采用,应结合数据中心访问控制、磁盘加密、远程恢复方式和项目凭据等级决定。不要把自动登录当成无条件的安全建议。
05第四步:先跑无签名构建,再接入 Xcode 归档
GitLab CI 调用 Xcode 时,第一条流水线不要直接执行发布。先证明 Runner 能正确拉取仓库、解析依赖、调用目标 Xcode,并在无签名条件下完成构建与测试。
Apple 文档指出,Xcode 的构建设置会影响编译、链接、调试信息生成和产品打包;命令行传入的设置还拥有较高优先级。Xcode 构建设置说明
可采用这样的最小结构:
stages:
- verify
- test
- archive
variables:
LANG: "en_US.UTF-8"
verify_xcode:
stage: verify
tags:
- macos
- xcode
script:
- whoami
- xcode-select -p
- xcodebuild -version
- git --version
test_ios:
stage: test
tags:
- macos
- xcode
script:
- bundle exec pod install
- xcodebuild test \
-workspace "<WORKSPACE>.xcworkspace" \
-scheme "<SCHEME>" \
-destination "platform=iOS Simulator,name=<SIMULATOR_NAME>"
archive_ios:
stage: archive
tags:
- macos
- xcode
- ios-release
script:
- xcodebuild archive \
-workspace "<WORKSPACE>.xcworkspace" \
-scheme "<SCHEME>" \
-archivePath "build/<APP>.xcarchive"
artifacts:
when: always
paths:
- build/
项目使用 Swift Package Manager、CocoaPods 或其他依赖管理方式时,依赖解析也要作为验收的一部分。不要只在个人 Mac 上预热缓存,然后把缓存命中误认为 Runner 配置正确。
首次流水线至少保留:
xcode-select -p和xcodebuild -version输出。- 依赖解析日志。
- 测试退出码。
xcresult或等效测试结果。.xcarchive和必要的符号文件。- 失败任务结束后的工作目录状态。
Apple 的构建设置参考也明确列出了 CODE_SIGN_IDENTITY、CODE_SIGN_STYLE、DEVELOPMENT_TEAM 等签名相关设置。它们应在项目配置、受保护变量和专用钥匙串之间形成清晰边界,而不是散落在脚本中。Apple Xcode 构建设置参考
第五步:把签名任务从普通测试任务中隔离
无签名测试可以覆盖更广的分支。发布归档则应只允许可信分支、受保护变量和专用 Runner。
签名资产通常包括:
- 开发或分发证书。
- 私钥。
- Provisioning Profile。
- App Store Connect 相关凭据。
- 专用钥匙串及其解锁权限。
Apple 说明,证书可以通过开发者账户或 Xcode 创建与撤销;开发证书和分发证书的用途、权限范围并不相同。Apple 证书总览
在 CI 中,推荐按以下方式处理:
✅ 为 CI 账户创建专用钥匙串。
✅ 让证书和描述文件只在需要发布的任务中导入。
✅ 使用 GitLab 的受保护变量保存敏感内容。
✅ 限制发布任务只能由受保护分支触发。
✅ 任务结束后清理临时证书、描述文件和导出目录。
❌ 不把 .p12、私钥或明文密码提交到仓库。
❌ 不让分叉仓库或外部贡献者触达发布 Runner。
❌ 不在日志中打印变量内容或完整签名命令。
Apple 的证书文档还说明,证书文件安装后会出现在 Mac 的钥匙串中。因此,“文件已经复制到磁盘”不能证明 Xcode 能够签名;你必须用实际归档任务验证 Runner 账户是否能访问正确钥匙串。Apple 证书安装与分发说明
07上线前的三张决策表
下面的表格用于决定 Runner 是否适合生产,而不是用于比较控制台上的“在线”状态。
| 决策维度 | 继续使用当前 Mac Runner | 暂缓上线或更换方案 |
|---|---|---|
| 构建能力 | 真实项目能完成构建与测试 | 只能运行空项目或示例命令 |
| Xcode 环境 | xcode-select、依赖和 Scheme 均可复现 |
依赖个人账户或人工点击 |
| 用户会话 | 重启后能恢复登录和 Runner 服务 | 重启后必须人工登录才能上线 |
| 签名边界 | 发布任务使用专用账户、钥匙串和受保护变量 | 测试与发布共用个人凭据 |
| 安全范围 | Runner 只承接可信项目 | 外部代码可以修改脚本并触达主机 |
| 失败恢复 | 能清理目录、停止残留进程并重新执行 | 失败后必须人工删除文件或重启 |
再看执行器选择。对 iOS 构建而言,Shell executor 通常最直接,但它并不等于强隔离。
| 执行方式 | 适合场景 | 主要优点 | 主要风险 |
|---|---|---|---|
| Shell executor | 可信 iOS / macOS 项目 | 直接访问 Xcode、模拟器和钥匙串 | 作业与主机权限边界较弱 |
| 容器类执行器 | 可容器化的普通构建 | 环境更容易标准化 | 不适合直接替代完整 macOS 图形与签名环境 |
| 共享 Runner | 无敏感凭据的公共任务 | 配置成本较低 | 节点归属、工具链和数据边界不稳定 |
| 专用 Mac Runner | 受控项目与发布任务 | 工具链、权限和凭据可单独管理 | 需要承担持续维护和恢复责任 |
最后用任务阶段检查交付条件:
| 阶段 | 必须留下的证据 | 未通过时的处理 |
|---|---|---|
| 注册 | Runner 标签、范围和认证配置 | 删除错误注册,重新限定项目或群组 |
| 常驻 | 退出 SSH、重新登录、整机重启后的状态 | 检查 LaunchAgent 和图形登录会话 |
| 构建 | 真实仓库的编译、测试日志和退出码 | 先修复工具链,不接入签名 |
| 归档 | .xcarchive、测试结果和构建产物 |
检查 Scheme、签名设置和路径 |
| 安全 | 凭据不进仓库、不出日志,外部代码不可触达 | 拆分测试 Runner 与发布 Runner |
| 运维 | 失败清理、磁盘回收、证书轮换和故障记录 | 暂缓扩大任务范围 |
上线首周观察真实任务,而不是跑分
上线后的第一周,应使用真实项目观察四类问题。
一是排队。若所有任务都使用同一个标签,测试、归档和发布可能互相等待。二是工作目录增长。失败任务、DerivedData、归档和依赖缓存都可能持续占用磁盘。三是并发冲突。多个任务共享模拟器、钥匙串或固定输出目录时,失败原因往往不是代码本身。四是重启恢复。节点能否在维护窗口后自动回到可用状态,比空项目跑通更重要。
维护责任也要提前划分:
- 项目团队负责
.gitlab-ci.yml、Scheme、依赖和产物规则。 - DevOps 负责 Runner 版本复核、标签、日志、磁盘和恢复流程。
- 发布负责人负责证书、描述文件和 App Store Connect 凭据轮换。
- 节点提供方负责主机可用性、远程访问、系统维护窗口和交付边界。
如果你需要远程 Mac 作为长期构建节点,可以先查看 远程 Mac CI 构建节点配置选择,再根据项目是否需要固定环境、长期在线和独立账户,比较 Mac mini M4 租赁方案 与本地购置方案。涉及地区和链路时,再参考 Mac mini M4 租赁价格。
09远程 Mac 与本地 Mac mini:如何选择 CI 节点
如果你已经有一台可长期在线的 Mac,且团队能够管理账户、证书、磁盘和系统更新,本地设备可以继续承担 CI。它的缺点是采购成本一次性发生,硬件故障、网络中断和远程恢复也由你自己负责。
远程 Mac 的价值不只是“少买一台电脑”。它更适合短期迁移、临时扩展构建容量、验证 Apple Silicon 工具链,或为没有 Mac 实机的团队提供可持续运行节点。它仍然需要你维护 GitLab Runner、Xcode、签名资产和流水线规则,但不需要你先承担硬件闲置、机房网络和物理故障处理。
如果当前方案是 Linux 云主机或虚拟化 macOS,常见问题是无法直接提供完整的 Xcode 工具链、模拟器、钥匙串和真实 Apple Silicon 行为;如果当前方案是个人 Mac,则常见问题是睡眠、用户退出、磁盘被开发任务占满,以及发布凭据和个人账户混在一起。对需要真实 macOS 构建的团队来说,租赁一台可长期在线、允许独立账户和完整工具链配置的 Mac,通常比先购买一台可能长期闲置的硬件更容易完成验证。
当你的项目负载长期稳定、需要物理接口,或必须完全掌控硬件生命周期时,自购 Mac 仍然合理。若需求集中在迁移期、发布周期或临时 CI 扩容,VpsMesh 的远程 Mac 租赁则更适合先按实际任务验证,再决定是否扩大长期投入。