掌握Jenkins执行器优化,实现更快的构建

通过掌握Jenkins执行器配置来优化CI/CD性能。本专家指南解释了如何基于CPU和I/O约束计算最佳执行器数量,从而减少构建队列时间并最大化代理吞吐量。学习关键配置策略,包括利用Pipeline并行性、管理静态与动态代理,以及使用队列长度和I/O等待等关键指标识别瓶颈。实施这些可操作步骤,实现更快的构建和更高效的Jenkins环境。

掌握Jenkins执行器优化,实现更快的构建

Jenkins执行器优化实际上是容量规划的细化。执行器是Jenkins在节点上运行工作的一个槽位。槽位太少,作业会在队列中等待;槽位太多,每个构建都会争夺CPU、内存、磁盘、网络,有时甚至争夺相同的依赖缓存。

目标不是“更多执行器”,而是在不使单个构建不可靠的前提下缩短总反馈时间。

将控制器执行器设为零

对于大多数现代Jenkins安装,控制器应负责调度工作、提供UI、存储作业配置和协调代理,而不应编译代码或运行测试套件。

将控制器执行器设为0,除非有必须在其上运行的小型管理作业。控制器上繁忙的构建会拖慢UI、延迟队列调度,并使插件问题更难诊断。如果控制器变得不稳定,每个使用Jenkins的团队都会感受到影响。

将构建工作转移到带有明确标签的代理上:

linux && docker
linux && maven
windows && visualstudio
large-memory

标签是执行器优化的一部分,因为等待linux && docker的作业不会关心你是否有空闲的Windows执行器。

从工作负载类型开始,而非通用公式

对于CPU密集型构建,从接近物理或虚拟CPU核心数开始。一个运行C++编译或大型Java测试套件的8核构建虚拟机可能从6到8个执行器开始,然后根据测量结果调整。

对于I/O密集型构建,由于作业花费时间等待网络下载、工件上传或测试服务,你可以运行比CPU核心更多的执行器。一个8核且依赖项繁重的代理可能能很好地处理10到12个执行器。但如果磁盘速度慢或内存紧张,它也可能在6个执行器时崩溃。

内存通常设定实际限制。如果每个构建使用2 GB RAM,而代理有16 GB,那么八个执行器将没有空间留给操作系统、Docker守护进程、语言运行时、浏览器测试或文件系统缓存。在这种情况下,四或五个执行器可能比八个更快,因为机器避免了交换。

使用类似这样的工作表:

代理RAM:32 GB
为操作系统和守护进程保留:4 GB
可用于构建:28 GB
典型构建峰值:3 GB
起始执行器:8或9,然后在负载下验证

数字不需要完美,但需要足够明确,以便在观察实际构建后进行调整。

关注队列原因

Jenkins构建队列会告诉你工作为何等待。“等待下一个可用执行器”与“没有带有该标签的节点”不同。前者表明容量压力,后者表明标签或代理配置问题。

当开发人员抱怨Jenkins慢时,检查:

  • 按标签的队列长度。
  • 平均队列等待时间。
  • 代理在线/离线切换频率。
  • 因锁定资源而停滞的构建。
  • 并行流水线阶段消耗的执行器超出预期。

一个具有十个并行分支的流水线可能消耗十个执行器槽位。对于测试矩阵来说,这可能正是你想要的。但也可能使小型Jenkins安装上的所有其他作业陷入饥饿。

每个一次性代理使用一个执行器

对于Kubernetes代理和许多云创建的代理,每个Pod或实例一个执行器通常是最清晰的模型。Pod是隔离单元。如果需要更多并发,创建更多Pod,而不是将不相关的构建打包到同一个一次性工作区中。

该模型使资源请求变得重要:

resources:
  requests:
    cpu: "2"
    memory: "4Gi"
  limits:
    memory: "6Gi"

如果请求太低,Kubernetes可能将太多Jenkins代理打包到一个节点上。Jenkins看到可用的执行器,但集群节点已过载。如果限制太紧,构建会因内存错误而失败,看起来像是应用程序问题。

对于静态VM代理,多个执行器可以正常工作。只需确保工作区布局、缓存目录和工具安装设计为支持并发作业。

限制嘈杂的作业

某些作业不应像Jenkins可以调度的那样运行多个副本。数据库集成测试、浏览器测试、负载测试和镜像构建会迅速使共享系统饱和。

使用作业级节流、可锁定资源或流水线控制:

pipeline {
  agent { label 'linux && docker' }
  options {
    disableConcurrentBuilds()
  }
  stages {
    stage('构建镜像') {
      steps {
        sh 'docker build -t app:${BUILD_NUMBER} .'
      }
    }
  }
}

对于共享的临时数据库,使用锁更清晰:

lock('staging-db') {
  sh './run-integration-tests.sh'
}

这可能会使一个作业等待,但可以防止五个作业同时失败并浪费所有执行器时间。

测量代理,而不仅仅是Jenkins

Jenkins可以告诉你队列时间和执行器使用情况。操作系统则告诉你执行器数量是否合理。

在Linux代理上,检查:

uptime
mpstat 1
iostat -xz 1
free -h
df -h
docker system df

高CPU且低I/O等待意味着CPU密集型工作。高I/O等待意味着增加执行器可能会使构建变慢。构建期间的交换使用是减少执行器或增加内存的强烈信号。几乎满的磁盘会使检出、归档和Docker构建变得缓慢。

也要关注单个构建的持续时间。如果队列时间减少一分钟,但每个构建变慢五分钟,那么更高的执行器数量并不是胜利。

小幅度调整

逐步更改执行器数量。将代理从4个增加到6个,观察几个繁忙的日子,然后决定。大幅跳跃会隐藏回归的原因。

保留记录:

2026-05-24:linux-build-03 执行器 4 -> 6
原因:linux && maven 队列平均等待12分钟
监控:CPU、iowait、Maven缓存锁争用、构建持续时间
回滚:如果p95构建持续时间上升超过预期,则设为4

这个简短的记录有助于在两周后有人问为什么代理变慢时提供答案。

实际目标

一个健康的Jenkins设置应具备:大多数代理在高峰期间繁忙、常见标签的队列短、无交换、可预测的构建持续时间以及无控制器构建。还应有足够的标签特定容量,以便缺少Docker代理不会让不相关的Maven作业等待。

执行器优化不是一次性的设置。当你的团队添加并行阶段、迁移到更大的测试套件、采用Docker构建或从静态VM迁移到Kubernetes代理时,它都会发生变化。当队列时间变得明显、代理开始交换或人们建议的最快修复是“只需添加更多执行器”时,请重新审视它。有时这是正确的。但通常,更好的修复是新的代理池、更好的标签或减少对其他人造成伤害的作业的并发副本。

并行阶段需要预算

流水线并行性可以是一个巨大的胜利,但它会快速消耗执行器。一个跨四个Java版本和三个操作系统的矩阵构建可能创建十二个分支。如果每个分支在单独的代理上运行,那么这一个构建在部署阶段开始之前就可能消耗十二个执行器。

当团队为此做了规划时,这是可以接受的。当单个拉取请求使发布构建陷入饥饿时,这是痛苦的。对大型扇出作业设置限制:

options {
  parallelsAlwaysFailFast()
}

使用标签将昂贵的分支发送到正确的池,并考虑在主分支上运行完整矩阵,而在拉取请求上运行较小的矩阵。目标是在关键地方提供快速反馈,而不是在所有地方实现最大并发。

区分交互式和批处理预期

并非每个Jenkins作业都值得相同的队列目标。生产热修复构建、拉取请求验证作业、夜间安全扫描和每周清理作业都可以在Jenkins中运行,但它们不应平等竞争。

你可以通过标签、静默期、节流和计划窗口来分离它们。繁重的夜间作业应在交互式PR流量低时运行。长时间运行的负载测试应有自己的代理。发布作业可能值得保留容量或普通分支作业无法使用的标签。

这与其说是政治问题,不如说是可靠性问题。如果每个工作负载共享一个执行器池,那么最嘈杂的工作负载将决定每个人的体验。

需要记录的内容

对于每个代理池,保留一个小记录:

标签:linux && docker
代理类型:静态VM
每个代理的执行器:4
主要工作负载:Docker镜像构建、集成测试
已知限制:磁盘I/O和Docker层增长
清理:夜间docker prune、构建后工作区清理
负责人:平台团队

该记录使未来的调整更加容易。当队列时间增长时,你可以决定是添加更多相同类型的代理、将工作负载拆分为新池,还是减少引起争用的作业的并发数。