How-To's

集中的力量:TeamCity 架构如何解决 Jenkins 的扩缩难题

Read this post in other languages:

这篇博文由 draft.dev 的 Aykut Bulgu 撰写。

当 Jenkins 安装开始出现变慢的情况时,最先表现出的问题通常是队列积压。构建等待时间过长,反馈无法及时传达给开发者,CI 系统开始需要平台团队投入远超预期的精力。

这种情况对于早期采用 Jenkins 并持续扩展的团队十分常见。Jenkins 可以扩缩,但在规模较大时,通常需要细心规划控制器规模、管理插件,在很多组织中,还需要使用多个控制器来分散负载。这种方式虽然可行,但也增加了运行开销。

对于 DevOps 工程师和架构师来说,这类开销至关重要。CI/CD 是交付流程的一部分,当平台维护的难度加大时,工程团队很快就会感受到。

在这篇博文中,我们将探讨团队在使用 Jenkins 时经常遇到的扩缩挑战,以及 TeamCity 的服务器–代理架构如何在帮助减少运行负担的同时支持从少量流水线扩展为数百个流水线。

Jenkins 的扩缩挑战

概括来讲,Jenkins 采用控制器–代理模型。中央控制器负责管理配置、调度和协调,代理则负责运行实际的构建。TeamCity 也采用中央服务器与构建代理搭配的模式,因此两者的架构相似。两者的差异体现在两套系统在大规模运行和扩展时的实现方式上。

在 Kubernetes 上运行 Jenkins 可以改进代理的配置,并使突发容量更易于管理,但并不能消除管理控制器负载、插件兼容性和系统治理的需求。

控制器可能成为瓶颈

随着团队、仓库和流水线的增加,Jenkins 控制器承担的工作也越来越多:

  • 管理作业和流水线配置
  • 调度构建和协调代理
  • 提供 UI 和处理 API 请求
  • 维护插件状态和运行时行为

在负载较高的情况下,控制器可能会成为瓶颈。Jenkins 文档和生态系统指导通常会建议规模较大的组织采用多控制器策略来分散负载。这种方式可以有效发挥作用,但会带来治理、版本统一和团队间可见性方面的额外工作。

横向扩缩不仅仅是增加代理的问题

增加更多 Jenkins 代理可以提高执行能力,但并不能解决控制器端的协调和配置挑战。随着团队规模的扩大,他们常常需要处理以下问题:

  • 各个控制器的插件版本不统一
  • 作业定义和约定不一致
  • 管理凭据、共享库和策略强化需要进行重复工作

此时,扩缩 Jenkins 通常意味着运行一组控制器、维护共享库,并构建内部流程以确保实现完全一致。

插件依赖项带来运行风险

Jenkins 的灵活性很大程度上来自于其插件生态系统。这是它的其中一项优势,但在大规模部署时也会带来运行方面的权衡。插件密集的环境可能会:

  • 产生升级链,其中一个插件的更新会影响其他插件
  • 增加控制器的性能或内存开销
  • 故障排查的难度加大,因为行为分布在插件特定的日志和扩展点中

在许多 Jenkins 环境中,平台团队最终需要花费大量时间来验证插件更新、检查兼容性以及排查组件之间的交互问题。

TeamCity 的服务器–代理架构

TeamCity 也采用中央服务器与构建代理的架构,但该平台的设计宗旨是保持配置集中化,同时将执行任务向外扩展。

TeamCity 服务器负责处理编排工作。它可以存储配置、构建历史和工件元数据,管理队列和依赖项,并提供 UI 和 REST API。对于生产用例,TeamCity 支持外部数据库,这是扩缩较大规模安装的重要部分。

图像由 Aykut Bulgu 提供

构建代理负责处理执行。它们会签出源代码、运行构建步骤和测试、发布工件和报告,并将结果传回服务器。

代理是安装在物理机或虚拟机上的独立软件, 它们与服务器保持连接,并接收工作指定,这简化了入站联网受限环境中的部署流程。

这种分离在实际应用中非常重要。代理可以横向增加,包括在云环境中增加,而平台则保留集中配置和可见性。

TeamCity 的内置可扩缩功能

除了核心的服务器–代理模型之外,TeamCity 包含的功能还能帮助团队进行扩缩,无需反复重新设计 CI 系统。

弹性代理和云集成

TeamCity 支持在物理机器云托管机器上运行代理,并且可以通过内置的云集成和官方支持的插件按需启动云代理。这样一来,用户可以更轻松地处理临时需求激增,而无需永久增加容量。

假设某个团队日常运行时使用 10 个本地代理,且正常一周内的构建时间是可以预测的。合并大批量的拉取请求后,队列长度急剧增加。通过配置云配置文件,TeamCity 可以启动临时云代理,在需求激增期间缩短队列长度,然后在需求下降时移除临时容量。

从开发者的角度来看,重要结果是一致性:即使构建量发生变化,仍然可以在合理范围内快速提供反馈。

可视构建链替代复杂拼接的流水线逻辑

TeamCity 的构建链允许您定义通过快照和工件依赖项连接的各个构建的顺序和图形。这样一来,用户可以更轻松地对工作流的相关部分共用统一 VCS 快照的流水线建模。

构建链可以对工作流进行建模,例如构建 → 测试 → 打包 → 部署,在可能的情况下并行运行有依赖关系的构建,并重用工件以避免重复工作。由于构建链是 TeamCity 的核心概念,团队可以对复杂的流程建模,而无需拼接多个扩展程序来实现依赖项的可见性。

Jenkins 流水线确实通过 Jenkinsfile 原生支持多阶段工作流,但在规模较大的安装中,团队通常会将流水线与共享库、控制器特定的约定以及用于编排、可见性或环境处理的额外插件结合使用。TeamCity 的方式更以自我为中心,且更加集中。

假设一个产品由共享库、后端 API 和前端 SPA 组成。在 TeamCity 中,您可以定义一个构建链,其中共享库构建先运行,然后开始运行后端和前端构建,最后运行依赖于前两者的打包或部署构建。

该依赖关系图在 UI 中可见,并作为平台的一部分进行管理,而不是由多个单独部分拼接而成。

智能代理选择

TeamCity 根据要求和容量将构建匹配到代理。这有助于资源利用,并减少因环境专业性的提高而带来的手动调度开销。

例如,组织可能有:

  • 安装了 Docker 和 Java 21 的 Linux 代理,用于处理后端服务
  • 安装了 .NET SDK 的 Windows 代理,用于处理旧版应用程序
  • 安装了 Xcode 的 macOS 代理,用于处理移动构建

每个构建配置均可声明其需求:操作系统、安装的工具链、docker.server.osType = linux 这类自定义参数,或特定的版本要求。

当构建排队时,TeamCity 会将其路由到满足这些要求的代理。这样便使调度规则保留在配置中,而不是作为群体经验或本地约定而存在。

可靠性和可维护性优势

扩缩不仅关乎吞吐量, 还关乎随着项目数量的增加,保持平台稳定需要完成的工作量。

更少的变动部分

TeamCity 为很多常见工作流提供一流支持,因此团队对通过第三方扩展程序来实现核心 CI/CD 行为的依赖性通常比较低。测试报告、并行测试执行支持、不稳定测试检测和可视依赖项管理等功能都是产品的组成部分。这通常会导致升级更具可预测性,由扩展程序交互引起的意外情况会减少。

集中化配置

在具有多个控制器的 Jenkins 环境中,团队通常会在各实例之间重复使用相同的配置模式、凭据管理和作业约定。在 TeamCity 中,项目、模板和构建配置位于单个服务器或数量较少的服务器下,因此可以更轻松地跨团队标准化质量门、权限和可重用设置。

利用这种集中化配置,可以更轻松地统一实现治理。

简化了升级过程,降低了停机风险

插件密集的 Jenkins 环境可能会将升级变为漫长的验证过程。有了 TeamCity,团队需要处理的关键第三方依赖项通常比较少,服务器和代理的升级途径更清晰,并且可以集中进行版本控制。升级仍需要规划,但运行范围通常较小。

为 DevOps 工程师和架构师带来的实际收益

在实践中,这会带来以下收益:

  • 运行开销降低:扩缩通常更多地涉及添加或调试代理、审查队列行为和标准化配置,而不是添加更多控制器和验证大型插件组合。
  • 更好的开发者反馈循环:可视构建链、并行执行和详细报告可以帮助团队更快地理解故障,提高队列时间的可预测性。
  • 增长更易管理:随着组织添加服务、语言和交付目标,TeamCity 为平台团队提供了一种集中化的方式来扩大 CI/CD 容量,无需从头开始重新构建治理。

Jenkins 与 TeamCity 对比

下图提供了 Jenkins 与 TeamCity 在大规模部署时的典型运行方式。

图像由 Aykut Bulgu 提供

下表总结了两种架构在本文所讨论的各个维度上的对比:

方面 Jenkins TeamCity 为何重要
核心架构 控制器–代理模型;控制器负责处理 UI、调度和扩展程序 服务器–代理模型;服务器负责处理编排和状态,代理则负责执行构建 两者均使用中央协调器,但大规模部署时的运行复杂性有差异
扩缩策略 可以扩缩,但大型安装通常使用多个控制器和审慎治理 通常通过添加代理和集中组织项目的方式进行扩缩 运行开销更低,更易于管理增长
插件依赖 生态系统强大;许多安装依赖于插件和共享库实现集成和平台行为 许多核心功能均已内置,减少了集中工作流对第三方扩展程序的依赖 关键依赖项减少通常可以降低升级和故障排查风险
流水线/编排 基于 Jenkinsfile 的流水线是原生的;更大规模的部署通常会在其基础上添加共享库和插件 构建链、快照依赖项和工件依赖项是重要概念,并提供可视化支持 依赖项可视化实现起来更加轻松,可以简化大型交付流
代理管理 动态代理通常通过插件或外部平台工作实现 支持物理代理和云代理,并内置云集成和支持的插件 两者均可扩缩执行,但 TeamCity 将更多体验集中管理
工作负载分配 标签、节点选择和流水线逻辑 由服务器匹配代理要求和容量 分配更合理,可以减少环境不匹配问题
大规模部署时的可维护性 多控制器环境和插件协调增加了管理工作量 采用集中化服务器模型,重要的外部依赖项更少,可以简化管理工作 维护负担更小,平台稳定性逐渐提升

:TeamCity On-Premises 版本最多免费提供三个构建代理;如需扩展,需购买额外的代理许可证,具体价格见 TeamCity On-Premises 定价页面。TeamCity Cloud 采用另一种基于使用量的定价模型,且没有“三个代理”这一限制。

结论

Jenkins 仍然是功能强大且广泛使用的 CI/CD 平台,但在企业规模下,通常需要更多的架构规划和平台团队的日常协调。控制器负载、插件管理和多控制器治理都是可管理的,但会产生实际的运行成本。

TeamCity 通过集中编排、横向可扩缩代理,以及对依赖项建模、测试可见性和环境管理的更多内置支持来解决同样的问题。如果团队希望在扩缩 CI/CD 时无需自行进行大量的平台拼接工作,这一优势十分显著。

如果您当前的 Jenkins 设置已经要求采取控制器变通方法、插件验证周期和自定义治理流程,那么评估更加集中化的平台是否会减轻这种负担可能值得一试。TeamCity 的设计宗旨是支持这种转变,同时随着组织规模的扩大保持一致的开发者体验。

本博文英文原作者:

Olga Bedrina

Olga Bedrina

Discover more