TeamCity 2026.1 已经要求服务器和 Agent 使用 Java 21 才能启动,但 Agent 显示“已连接”并不代表它能稳定执行生产 Xcode 流水线。(TeamCity 官方快速设置指南)

症状: Agent 在线,却注册未授权、任务长期排队、Xcode 找不到,或重启后不再接单。
最快解法: 先锁定 Java 21 与非 root 运行账号,再验证 serverUrl、授权令牌、Xcode 路由、工作区和签名隔离,最后用真实归档与签名任务完成生产准入。

这篇文章适合 3 类人:正在为 TeamCity 增加 iOS 或 macOS 构建能力的平台工程负责人;准备把本地 Mac mini 改造成共享构建节点的企业 IT 负责人;以及需要采购或租用多台远程 Mac,并要求签名隔离、容量证据和故障恢复能力的技术决策者。

注意: 本文讨论的是 TeamCity On-Premises 2026.1。TeamCity Cloud 2026.2 的 Agent 安装和 launchd 文档只能用来理解自托管 Agent 的启动机制,不能直接推导 On-Premises 的功能、授权或版本行为。(TeamCity Cloud 官方集成文档)

01

运行时基线:先解决 Agent 为什么启动不了

TeamCity 2026.1 的首个判断边界是 Java,而不是 Xcode。官方文档明确说明,从 2026.1 起,TeamCity 服务器和 Agent 不能再使用低于 Java 21 的版本启动;这只约束 Agent 自身运行时,不限制项目使用其它 JDK 编译、测试或部署。

因此,你需要把两个概念分开:

  • Agent 运行 JDK: 用于启动 TeamCity Agent 进程,生产基线应明确为 Java 21。
  • 项目编译 JDK: 由 Maven、Gradle、脚本或项目工具链决定,可以通过 JAVA_HOME、Agent 参数或构建参数选择。
  • Xcode 工具链: 由 macOS、Xcode.app、xcode-select 和构建步骤共同决定,不等同于 Java 环境。

在 Apple Silicon 主机上,建议把以下资产记录放进节点登记表:

  • macOS 版本与补丁状态;
  • teamcity.agent.jvm.os.arch 返回值;
  • Xcode 完整安装路径;
  • 当前命令行开发者目录;
  • Agent 运行账号;
  • Agent Home、Work、Temp 和 System 目录;
  • JAVA_HOME 实际指向的位置;
  • 是否安装多个 JDK、多个 Xcode 或额外模拟器运行时。

最小核验命令可以保留为:

java -version
echo "$JAVA_HOME"
uname -m
xcode-select -p
xcodebuild -version

如果 java -version 不是 Java 21,或者 JAVA_HOME 在交互式终端中正确、在 launchd 环境中为空,先不要注册生产 Agent。macOS 的服务启动环境通常不会完整继承你的终端配置,很多“手工启动正常、重启后失效”的问题都从这里开始。

TeamCity 官方系统要求还要求 Agent 进程能够读写 Agent Home、Work、Temp 和 System 目录,并能向 serverUrl 发起出站 HTTP 连接。Agent 本身大约还需要 500 MB 内存,真正的 CPU、磁盘和内存压力则主要来自构建过程。(TeamCity 官方系统要求)

非 root 账号与管理员动作边界

生产 Agent 不应以 root 身份运行。更合理的做法是创建一个专用的构建账号,例如 teamcity-agent,并让它拥有:

  • Agent 安装目录;
  • workDirtempDirsystemDir
  • 构建缓存中确实需要访问的目录;
  • 临时 Keychain;
  • 需要由流水线生成的归档和测试结果目录。

管理员只在初始化阶段执行系统级动作,例如安装 Xcode、安装 Java、创建账号、配置目录权限、加载 launchd,以及必要的网络或证书策略。之后的 Agent 进程、自动升级和构建脚本都交给专用账号完成。

这样做不是为了形式上的“最小权限”,而是为了避免构建脚本继承过大的文件访问能力。特别是当同一台 Mac 还接收外部贡献者的 PR 或第三方依赖构建时,root Agent 会把工作区污染、凭证读取和系统文件修改的影响面放大。

02

连接授权:在线状态为什么不能当作上线证据

Agent 配置核心在 buildAgent.properties。至少要核对以下字段:

serverUrl=https://ci.example.invalid/
name=ios-release-arm64-01
authorizationToken=REDACTED
workDir=../work
tempDir=../temp
systemDir=../system

serverUrl 必须是 Agent 能够访问的 TeamCity 地址,并包含协议。官方文档建议使用 HTTPS。配置文件还需要能被 Agent 进程写入,因为首次连接后,服务器端生成的授权令牌需要保存回配置文件。(TeamCity 官方 Agent 配置文档)

你需要同时检查 3 份证据:

  1. 主机证据: buildAgent.properties、文件所有权、DNS、代理和防火墙记录。
  2. Agent 证据: teamcity-agent.log 中的连接、授权、重连和异常信息。
  3. 服务器证据: Agent 是否已授权、是否位于正确的 Agent Pool、是否报告了预期参数。

只看 TeamCity 控制台的绿色在线标识是不够的。连接成功但没有授权、进入错误 Pool、代理缓存了异常响应,或者配置文件无法写入,都可能造成“看起来在线、实际不能接单”的假在线状态。

如果企业网络通过反向代理连接 TeamCity,要特别确认:

  • Agent 到 serverUrl 的出站访问不被限制;
  • 代理不会缓存 TeamCity 服务端响应;
  • HTTPS 证书链在 macOS 上可验证;
  • 代理认证不会依赖某个用户的交互式登录;
  • 代码仓库、制品存储和 TeamCity 服务器的访问路径分别可达。

Agent Pool 与固定名称

一个 Agent 只能属于一个 Agent Pool,但一个项目可以使用多个 Pool。新授权 Agent 默认会进入 Default Pool,因此不要把生产签名节点直接留在默认池中。(TeamCity 官方 Agent Pool 文档)

建议至少分出以下逻辑:

  • ios-sandbox:非可信 PR、依赖验证和失败频率较高的任务;
  • ios-test:普通单元测试、模拟器测试和开发分支构建;
  • ios-release:归档、签名和发布任务;
  • ios-fallback:主节点故障时接管的备用节点。

固定 Agent 名称便于审计和回退,但不要只用名称做全部路由。名称解决“是哪台机器”,自定义 Agent 参数才解决“它具备什么能力”。

例如,你可以在 Agent 配置文件中加入:

mac.role=release
mac.arch=arm64
xcode.16.path=/Applications/Xcode.app
signing.scope=ios-release

然后在 TeamCity 的 Agent Requirements 中按参数筛选。官方文档说明,Agent Requirements 只能使用 Agent 在构建开始前能够报告的参数;多个条件默认按 AND 逻辑组合。(TeamCity 官方 Agent Requirements 文档)

03

Xcode 路由:工具链识别与任务分配

完整 Xcode 与 Command Line Tools 的区别

只安装 Command Line Tools 的 Mac,不能自动视为可执行完整 iOS 构建的节点。Apple 文档指出,xcodebuildsimctldevicectl 等工具属于 Xcode 工具链,并且需要安装 Xcode.app、将其设置为活动开发者目录后才能正常调用。(Apple 官方 Xcode 命令行工具文档)

因此,验收时不要只执行:

xcode-select -p

还要确认路径确实指向完整的 Xcode.app,并检查:

xcodebuild -version
xcrun --find xcodebuild
xcrun simctl list devices

如果 Agent 报告在线,但 xcodebuild -version 失败,或者路径指向 /Library/Developer/CommandLineTools,它不应进入 iOS 发布 Pool。

单版本与多版本 Xcode

只有一个 Xcode 版本时,TeamCity 的 Xcode 构建步骤可以使用该节点默认工具链。安装或升级 Xcode 后需要重启 Agent,确保新的工具和参数被重新采集。

多个 Xcode 版本共存时,不要只依赖全局 xcode-select。更稳妥的路由方式是:

  1. 为每个 Xcode 版本登记一个明确的 Agent 参数;
  2. 在构建步骤中使用 Path to Xcode 指定路径;
  3. 用 Agent Requirements 限制任务可运行的节点;
  4. 在流水线开头打印 xcodebuild -versionxcode-select -p
  5. 将任务要求、节点参数和实际工具链输出进行三方比对。

这 3 个层面分别解决不同问题:

  • Path to Xcode: 解决本次构建使用哪个 Xcode;
  • xcode-select 解决主机默认命令行工具链;
  • Agent Requirements: 解决任务被分配到哪些节点。

TeamCity 的 Xcode Runner 会根据 Agent 上报告的工具链参数形成相关要求,也支持在不同 Xcode 配置之间进行选择。(TeamCity 官方 Xcode 构建文档)

检查层面 应记录的证据 失败时的典型后果 处理动作
任务要求 Xcode、SDK、平台和架构要求 任务进入错误节点或长期排队 补充 Agent Requirements
节点参数 Xcode 路径、架构、角色参数 控制台显示兼容,实际工具链不匹配 修正 buildAgent.properties
实际工具链 xcodebuild -versionxcode-select -p、模拟器列表 编译、测试或归档失败 修复 Xcode 路径并重启 Agent
服务器分组 Agent Pool、授权状态、Agent 名称 发布任务落入测试节点 调整 Pool 绑定和路由条件

TeamCity 支持按 teamcity.agent.jvm.os.arch 区分 Apple Silicon 等架构。企业环境中可以把 Apple Silicon 节点和 Intel 节点分别放进不同 Pool,避免依赖、模拟器或预编译二进制因架构差异产生隐蔽失败。

04

工作区与签名隔离:共享 Mac 的真正风险

同一 Agent 上的构建并不会天然形成安全沙箱。工作目录、缓存、派生数据、临时文件和 Keychain 都可能跨任务留下痕迹。对于正式签名节点,真正需要审计的不是“有没有清理脚本”,而是清理后是否留下可验证的证据。

重点检查以下位置:

  • system.teamcity.build.checkoutDir 对应的 Checkout Directory;
  • system.teamcity.build.workingDir 对应的工作目录;
  • 构建临时目录;
  • DerivedData;
  • CocoaPods、Swift Package Manager 和其它依赖缓存;
  • 临时 Keychain;
  • Provisioning Profile、证书和导出的签名文件;
  • 构建失败后仍然存在的中间文件。

TeamCity 提供了 Checkout Directory、Working Directory 和 Temp Directory 等构建参数;其中构建临时目录会在构建后清理,但企业仍应通过目录差异和构建日志验证实际结果,而不能把默认行为当作完整隔离。(TeamCity 官方预定义构建参数文档)

哪些任务不能共用发布节点

以下任务不建议与正式签名任务共用同一台生产 Mac:

  • 来源不受完全控制的外部 PR;
  • 会执行第三方脚本或动态依赖安装的任务;
  • 需要修改系统级配置的测试;
  • 会导出开发者证书、Provisioning Profile 或签名密钥的任务;
  • 允许开发者任意修改构建脚本的实验性流水线。

更安全的分法是:普通测试使用测试 Pool,非可信 PR 使用隔离节点,归档与签名使用专用 Mac。你还需要限制构建账号对签名材料的可见范围,并在构建结束后删除临时 Keychain、导出包和相关日志中的敏感字段。

如果团队正在设计完整的签名基线,可以把 企业远程 Mac 签名凭证与工作区隔离方案 作为后续设计入口;如果重点是验收证据,则应配合“Mac 构建节点上线验收与故障恢复清单”逐项留档。

05

重启恢复:开机启动不等于无人值守接单

TeamCity 官方 macOS 流程使用 launchd 启动 Agent,并要求相关文件由构建用户拥有。官方文档还描述了一个重要边界:macOS 的 LaunchAgent 流程通常依赖构建用户登录,必要时还要配置自动登录;重启后再检查 Agent 是否重新连接。(TeamCity 官方 Agent 启动文档)

这和企业要求的“无人值守生产节点”不是同一件事。你需要在准入前明确:

  • 节点是否允许自动登录;
  • FileVault 或系统安全策略是否会阻止无人值守启动;
  • launchd 配置属于哪个用户会话;
  • Agent 目录是否由构建用户拥有;
  • 自动升级后 plist 是否仍然有效;
  • Xcode 首次调用是否弹出许可或图形界面提示;
  • Keychain 是否能在无人工操作时解锁;
  • 失败后谁接收告警,备用节点如何接管。

最小检查可以使用:

launchctl list | grep BuildAgent
tail -f ~/buildAgent/logs/teamcity-agent.log

不要把“重启后进程存在”当作成功。完整恢复测试至少应包含:

  1. 记录重启前的 Agent 名称、时间戳和当前任务;
  2. 远程重启 Mac;
  3. 确认构建用户和 launchd 服务恢复;
  4. 检查 Agent 重新连接并保持授权;
  5. 提交一次真实 Xcode 编译;
  6. 再执行模拟器测试或归档;
  7. 验证临时 Keychain 是否可用;
  8. 人为制造一次失败,确认告警和替代节点接管。

如果其中任何一步需要人工点击 Xcode 许可、手动解锁 Keychain 或重新加载 Agent,就不要把该节点直接放进发布 Pool。可以先放到试运行 Pool,直到恢复链路闭环。

06

生产准入:用真实流水线决定节点去留

空项目只能证明 Java、Xcode 和 TeamCity 基本能启动,不能证明节点有生产价值。企业 IT 应使用一条代表性流水线完成验收,至少覆盖:

  • 普通 PR 编译;
  • 模拟器测试;
  • 依赖缓存命中与清理;
  • Archive;
  • 受控签名;
  • 制品上传;
  • 构建失败后的目录清理;
  • 重启后的自动恢复;
  • 主节点不可用时的备用节点接管。

建议把结果记录成以下准入表:

验收域 通过证据 暂缓原因 回退动作
Java 运行时 java -versionJAVA_HOME、Agent 日志 版本不符或服务环境为空 修正 JDK 与启动环境
注册授权 Agent 日志、服务器授权状态、固定名称 Token 无法写入或进入错误 Pool 重建配置并重新授权
Xcode 路由 任务要求、Agent 参数、实际 xcodebuild 输出一致 版本或架构不匹配 调整 Path、参数和 Requirements
工作区清理 构建前后目录差异、缓存边界记录 凭证或中间文件残留 改为专用节点或强制清理
重启恢复 时间戳、launchd 状态、重新接单日志 需要人工登录或解锁 暂不进入发布 Pool
真实流水线 PR、测试、归档、签名均有记录 仅空项目成功 延长试点并补充容量证据
故障接管 备用 Agent 成功接单 无可用替代节点 增加冗余或降低发布并发

容量规划也要依据真实数据,而不是只看 CPU 核心数。你应记录排队时间、构建成功率、节点空闲窗口、归档任务占用、模拟器任务并发以及备用节点接管时间。需要扩容时,再按队列和冗余证据计算单节点、主备节点或弹性远程 Mac 池的组合。

决策条件列表

  • Agent 能使用 Java 21 启动,配置文件可写,且服务器端已授权,进入连接与路由验收;否则先修复运行时或文件权限。
  • 任务要求、Agent 参数和 xcodebuild -version 三者一致,允许进入测试 Pool;否则禁止依赖“Connected”状态放行。
  • 正式签名任务与非可信 PR 使用不同 Agent Pool 或专用节点,继续做凭证清理测试;否则回退到隔离节点方案。
  • 重启后无需人工登录、点击许可或解锁凭证即可重新接单,进入真实流水线验收;否则不得进入发布 Pool。
  • 代表性归档、签名、清理和备用接管均有日志证据,可以按队列和冗余规划容量;否则只保留为试点节点。
  • 你没有固定 Mac 容量,但需要尽快验证 TeamCity 路由和 Xcode 工具链,先建立一台隔离的远程 Mac 试点节点;否则再评估自购 Mac mini 或长期固定节点。
07

当前方案与远程 Mac:什么时候值得先租一台试点节点

如果你现在把 Mac mini 放在办公室里作为共享打包机,常见缺点是硬件位置固定、远程故障恢复依赖现场人员,而且一台机器往往同时承担测试、签名和发布,隔离边界不够清晰。自购多台 Mac 也会带来采购周期、设备折旧、备机闲置和扩容前置投入。

把 Mac 直接放进通用云主机同样不能解决 Apple Silicon、完整 Xcode 和签名环境的问题;虚拟化或临时环境还可能增加工具链兼容性和恢复验证成本。对正在上线 TeamCity 2026.1 的团队,更稳妥的路径通常不是立刻承诺长期采购,而是先用一台独立远程 Mac 验证真实项目、Xcode 路由、签名隔离和重启恢复。

如果试点通过,再根据队列、发布窗口和故障接管记录选择按周、按月或更长期的容量。KVMNODE 的远程 Mac 更适合这种需要临时算力、独立测试环境或快速建立第二构建节点的场景;但如果你的团队长期持续高负载、必须接入特定物理设备,仍应评估自购 Mac 或专用机房部署。你可以先查看 KVMNODE 的远程 Mac 方案,再用生产准入表决定是否扩大节点数量。