Jenkins Controller、Agent与构建执行全过程
Jenkins不是“网页上点一下,然后服务器执行Shell”。一次构建要经过触发、队列、节点匹配、Executor占用、Workspace准备、代码检出、Step执行、日志回传、制品归档和清理。理解这些对象后,才能解释为什么任务一直排队、为什么同一Job出现 workspace@2、为什么Controller磁盘满会让所有流水线异常。
学习目标
- 区分Controller、Node、Agent进程、Executor、Queue、Workspace、Job和Build。
- 解释Webhook到Build完成的完整链路。
- 说明Queue等待的所有常见原因。
- 解释Executor为什么不是CPU核,也不是线程池大小的直接映射。
- 说明Workspace的创建、复用、并发目录和污染风险。
- 区分Workspace、stash、archive、fingerprint、制品仓库和镜像仓库。
- 解释JENKINS_HOME保存什么,以及为什么Controller备份不能只复制Workspace。
- 区分现代Jenkins运行Java与业务项目构建JDK 7/8/17/21。
- 设计Controller不执行重构建、Agent最小权限和凭据边界。
- 定位Queue卡住、Agent离线、磁盘满、工具缺失和Workspace串扰。
一、核心对象关系
flowchart TD
A["用户、Webhook或定时器"] --> B["Jenkins Controller"]
B --> C["Job配置与Jenkinsfile"]
B --> D["Build Queue"]
D --> E["Node标签与资源匹配"]
E --> F["Agent进程"]
F --> G["Executor槽位"]
G --> H["Workspace"]
H --> I["Git、Maven、测试、Docker等Step"]
I --> J["日志、状态和测试结果"]
I --> K["Jar、镜像或其他制品"]
J --> B二、Controller到底负责什么
Controller负责控制面:
- Web UI和API。
- 用户、权限和凭据元数据。
- Job/Pipeline定义。
- Webhook、定时和手动触发。
- Queue调度。
- Node与Agent管理。
- Pipeline执行状态持久化。
- Build历史、日志索引、测试结果和归档元数据。
- 插件加载与系统配置。
Controller不应长期承担所有Maven编译、Docker构建和生产部署。重任务会竞争Controller的CPU、Heap、磁盘和线程,最终影响所有Job调度与UI。
常见安全起点是Controller内置节点Executor设为0,只通过明确Agent执行构建。是否能完全做到取决于系统任务和插件,需验证。
三、Agent、Node和Executor区别
| 概念 | 含义 |
|---|---|
| Node | Jenkins中可调度计算资源的逻辑定义 |
| Agent | 连接Controller并执行远程任务的进程/运行环境 |
| Executor | Node可同时占用的构建执行槽位 |
一台Linux机器可定义为一个Node,运行一个Agent进程,并配置多个Executor。但Executor不是CPU核,也不自动限制Maven线程、测试子进程或Docker容器资源。
四、Executor为什么不能盲目调大
若Node配置8个Executor,表示最多可让8个需要Executor的构建同时占用该Node。每个构建还可能启动:
- Maven/Gradle并行线程。
- Java测试JVM。
- Node.js构建进程。
- Docker BuildKit。
- 集成测试数据库和中间件。
- 压缩、扫描和上传进程。
因此:
Executor数 × 单Build峰值CPU/内存/磁盘IO才接近容量。Executor过多会导致OOM、磁盘抖动、测试超时和构建整体变慢;过少则Queue等待。
五、Agent如何连接Controller
常见模式:
- Controller通过SSH启动/管理远程Agent。
- Agent主动连接Controller的Inbound模式。
- WebSocket等穿越反向代理的连接方式。
- Kubernetes插件动态创建Agent Pod。
- 云插件按需创建虚拟机。
旧资料常把Inbound Agent统称JNLP Agent,现代界面和协议名称可能变化。实际加密、认证、端口和版本以当前Jenkins文档为准。
Agent连接只解决控制通道,不自动提供Maven、Docker、kubectl和业务网络。工具和权限仍需在Agent环境准备。
六、一次触发如何进入Queue
flowchart TD
A["Webhook/手动/定时触发"] --> B["创建Queue Item"]
B --> C["检查Job是否允许并发"]
C --> D["解析需要的Label/Node"]
D --> E{"是否有在线且匹配的Node"}
E -- "否" --> F["留在Queue并显示原因"]
E -- "是" --> G{"是否有空闲Executor"}
G -- "否" --> F
G -- "是" --> H["分配Executor并创建Run"]
H --> I["准备Workspace并执行"]Queue Item还不是Build进程。只有满足调度条件并获得执行资源后,才进入实际运行。
七、Queue一直等待的常见原因
| 提示方向 | 常见原因 |
|---|---|
| Waiting for next available executor | 匹配Node的Executor都忙 |
| Label不存在/不匹配 | Jenkinsfile要求的label没有Node |
| Node offline | Agent断连、磁盘阈值或人为下线 |
| Waiting for builds to finish | 禁止并发、里程碑或锁 |
| Throttle/Lock等待 | 插件限制并发或共享资源 |
| Flyweight/Controller任务等待 | Pipeline控制步骤或Controller压力 |
| Quiet period | 配置了延迟合并触发 |
排查不能只“重启Jenkins”,应读取Queue原因、Node状态、Executor占用、标签表达式和上一个Build。
八、Label是能力声明,不应只是机器名
可以给Agent标注:
linux && jdk8 && docker
linux && jdk17 && kubectl
windows && node20Pipeline:
agent { label 'linux && jdk8 && docker' }Label表示能力与约束,比 agent-01更可维护。但Label不是事实自动探测:节点被错误打上 jdk8,实际没安装JDK 8,构建仍会失败。应在镜像、配置管理和启动验收中保证标签真实。
九、现代Jenkins与JDK 7/8业务构建边界
现代Jenkins Controller/Agent进程通常要求受支持的现代Java版本,具体基线随Jenkins LTS升级变化,不能假定仍能运行在JDK 8。
但这不妨碍构建JDK 7/8业务:
Controller/Agent运行时:使用Jenkins当前支持的Java
业务编译工具链:选择JDK 7、JDK 8、JDK 17或JDK 21实现方式:
- Jenkins Tool配置选择不同JDK。
- Maven Toolchains。
- 固定构建容器镜像。
- 不同Label的Agent。
JDK 7/8构建还要固定具体更新版本、Maven/Gradle兼容性、TLS仓库访问和编译参数,不能只写“Java 8”。
十、动态Agent生命周期
Kubernetes动态Agent概念流程:
flowchart TD
A["Queue出现k8s-agent需求"] --> B["Jenkins Kubernetes插件请求创建Pod"]
B --> C["调度Pod并启动Agent容器"]
C --> D["Agent连接Controller"]
D --> E["获得Executor并执行Build"]
E --> F["上传日志、测试结果和制品"]
F --> G["Pod按策略删除"]动态Agent减少环境漂移和空闲成本,但要处理镜像拉取、Pod启动延迟、PVC、网络策略、ServiceAccount、缓存和失败现场保留。
十一、Workspace是什么
Workspace是某次Job在某个Node上的工作目录,通常包含:
- 检出的源码。
- 构建中间文件。
- 测试输出。
- 临时脚本。
- 未归档产物。
它不是可靠长期存储,也不应成为部署事实源。Agent删除、Workspace清理、并发构建和Node迁移都可能让文件消失。
十二、为什么出现workspace@2
当同一Job允许并发,并在同一Node上同时执行时,Jenkins需要避免两个Build写同一目录,可能创建:
workspace/job-name
workspace/job-name@2
workspace/job-name@3因此脚本不能硬编码绝对Workspace路径,也不能假定另一次Build会看到当前目录。
并发隔离仍不完整:两个Workspace可能共享同一Maven本地仓库、Docker daemon、固定端口、测试数据库和部署环境,仍会冲突。
十三、Workspace污染如何发生
Build 100生成旧class/配置
→ 未清理
→ Build 101切换分支
→ 工具增量构建复用旧文件
→ 产物混入上一版本内容治理:
- 构建工具执行clean或可靠增量策略。
- 必要时
deleteDir()/Workspace Cleanup。 - 不把凭据文件留在Workspace。
- 动态Agent提供干净环境。
- 缓存目录与源码目录分离。
清理不是越多越好:每次删除所有依赖缓存会显著拖慢构建,应区分可复用依赖缓存和必须隔离的业务输出。
十四、Checkout全过程
flowchart TD
A["Agent准备Workspace"] --> B["凭据访问Git"]
B --> C["获取指定仓库和Ref"]
C --> D["Checkout具体Commit"]
D --> E["记录Commit、分支和变更集"]
E --> F["后续Build只使用该Commit"]正式发布应记录具体Commit SHA,不只记录分支名。分支在构建期间可能继续移动;同一Build必须绑定不可变Commit。
浅克隆、Reference仓库和缓存可以加速,但会影响Tag、历史、Changelog和某些版本插件,需要按流水线需求选择。
十五、Job、Build和Run
- Job:长期任务定义。
- Build/Run:Job的一次具体执行。
- Build Number:Job内递增标识,不等于全局版本。
- Result:SUCCESS、UNSTABLE、FAILURE、ABORTED等。
- Cause:Webhook、用户、上游任务、定时器等触发原因。
UNSTABLE通常表示流水线完成但测试等质量门不完全通过,不应与SUCCESS混用。是否允许部署必须由明确策略决定。
十六、Workspace、stash、archive和制品仓库
| 存储方式 | 用途 | 生命周期/边界 |
|---|---|---|
| Workspace | 当前Node构建工作目录 | 可清理、不可长期依赖 |
| stash/unstash | 同一Pipeline不同Node/Stage传小型文件 | Controller/制品管理链路有开销,不适合巨大文件 |
| archiveArtifacts | 归档到Build记录 | 受Controller/Artifact Manager存储与保留策略影响 |
| Maven/Nexus制品库 | 保存Jar等版本制品 | 跨Pipeline、可审计、可推广 |
| Registry | 保存OCI镜像 | 使用Tag和Digest,适合部署 |
原则:制品一次构建,多环境推广;测试和生产不要分别重新编译同一Commit。
十七、fingerprint解决什么
Fingerprint通过文件摘要关联制品与Build,帮助追踪某Jar在哪次构建产生、被哪些下游任务使用。但它不是数字签名,也不证明制品没有恶意来源。供应链还需要签名、SBOM、扫描和制品仓库权限。
十八、JENKINS_HOME保存什么
常见包括:
- 系统和Job配置。
- 插件及版本。
- 凭据加密数据与密钥材料。
- Build记录和日志。
- Pipeline执行状态。
- 用户、节点和队列相关状态。
- 归档产物,取决于Artifact Manager。
JENKINS_HOME是Controller关键状态,磁盘满或损坏会影响全站。备份必须保证一致性、加密、访问控制和恢复演练。
十九、为什么不能只备份Workspace
Workspace只是构建临时目录,不包含完整Job配置、插件、凭据密钥和Build状态。反过来,备份JENKINS_HOME也不等于备份外部Nexus、Registry、Git和云凭据系统。
恢复计划应覆盖:
- Jenkins核心版本。
- 插件锁定与兼容。
- JENKINS_HOME。
- Configuration as Code。
- 外部数据库/制品库/Registry。
- DNS、证书和Agent接入。
二十、日志和状态如何回传
Agent执行Shell/Batch等Step,标准输出通过Agent通道回传并写入Build日志。网络中断时,运行中进程、日志和Pipeline状态取决于Agent类型、Step和持久化策略。
不要输出:
- 密码和Token。
- kubeconfig全文。
- 私钥。
- Docker登录配置。
- 生产环境变量全集。
Credentials masking只是降低意外打印风险,不是绝对防泄漏;编码、分段输出或恶意脚本可能绕过。
二十一、Controller与Agent信任边界
Agent能执行仓库中的代码。若不可信PR可以在有生产凭据的Agent运行,就可能窃取Secret或控制部署环境。
应隔离:
- 不可信PR Agent与生产部署Agent。
- 开发凭据与生产凭据。
- Docker Socket和宿主机权限。
- Kubernetes ServiceAccount。
- 出网和内网访问。
- Workspace与缓存。
不要让普通构建Agent长期拥有生产集群管理员权限。
二十二、凭据什么时候注入
凭据应在最小Step范围内绑定:
withCredentials([string(credentialsId: 'deploy-token', variable: 'DEPLOY_TOKEN')]) {
sh './deploy.sh'
}边界:
- 不写入Jenkinsfile。
- 不写入镜像层。
- 不归档到artifact。
- 不输出到日志。
- 不留在Workspace。
- 不传给不可信子进程。
- 使用后端最小权限和短期凭据。
二十三、插件为什么是核心风险
Jenkins大量能力由插件提供,插件运行在Controller或参与Agent执行,可能影响:
- 安全漏洞。
- Pipeline语义。
- 凭据处理。
- 页面和API。
- 数据格式和升级。
不能在生产随意点击“全部更新”。应维护插件清单、依赖、兼容矩阵、测试环境、备份和回退方案。插件降级不一定兼容已升级的数据格式。
二十四、Controller高可用边界
Jenkins传统Controller是有状态系统。仅启动两个Controller指向同一JENKINS_HOME并不自动形成安全Active-Active,可能造成文件和任务状态冲突。
高可用通常依赖:
- 快速恢复而非简单双写。
- 可靠JENKINS_HOME存储与备份。
- Configuration as Code。
- 外部Artifact Manager。
- 动态Agent。
- DNS/LB切换。
- 明确RPO/RTO和演练。
二十五、商业执行链:Spring Boot镜像发布
flowchart TD
A["Git Webhook携带仓库事件"] --> B["Controller创建Queue Item"]
B --> C["匹配linux+jdk8+docker Agent"]
C --> D["Executor分配Workspace"]
D --> E["Checkout固定Commit"]
E --> F["JDK 8编译与测试"]
F --> G["构建镜像并记录Digest"]
G --> H["推送受信Registry"]
H --> I["部署测试环境并验证"]
I --> J["审批后推广同一Digest到生产"]
J --> K["健康、指标和回滚验证"]重点是同一镜像Digest从测试推广到生产,而不是生产阶段重新构建。
二十六、Queue卡住Runbook
- 打开Queue Item查看原因。
- 检查Job是否禁用并发或等待锁。
- 检查Jenkinsfile要求的Label。
- 检查匹配Node是否在线。
- 检查Executor是否占满。
- 检查磁盘/临时空间监控是否自动下线Node。
- 检查Throttle、Lock和上游依赖。
- 检查Controller线程、Heap和插件异常。
二十七、Agent离线Runbook
检查:
- Agent进程和Java版本。
- Controller URL、DNS和TLS。
- SSH/WebSocket/Inbound通道。
- Agent Secret或认证。
- 时钟与证书。
- 防火墙、代理和反向代理超时。
- Node磁盘和inode。
- Remoting版本兼容。
- Controller日志与Agent日志时间线。
不要只反复Reconnect,先确认断开原因和是否正在运行敏感部署。
二十八、Workspace磁盘满Runbook
Node磁盘满
→ Git checkout或构建写失败
→ Agent可能被标记临时离线
→ Queue继续堆积检查Workspace、依赖缓存、Docker数据、测试容器、日志、归档和deleted-open文件。清理前按Job、Build和标签确认归属,不执行无条件全局删除。
二十九、工具存在但Job找不到
常见原因:
- Agent非交互Shell不读取用户profile。
- Jenkins服务用户PATH不同。
- Tool配置没有应用。
- Label选到另一台Node。
- 容器Agent镜像缺工具。
- Windows/Linux命令不同。
- JDK版本被环境变量覆盖。
在Build中输出经过脱敏的:
pwd
id
java -version
mvn -version
git --version不要输出完整环境变量,因为可能含Secret。
三十、商业场景:@2目录导致部署错包
脚本硬编码 /var/lib/jenkins/workspace/order-service/target/app.jar。并发Build实际在 order-service@2构建,新脚本却部署了旧目录Jar。
修复:使用 $WORKSPACE和归档制品,不从固定Workspace路径部署;禁用同环境并发部署,制品按版本和Digest推广。
三十一、商业场景:Controller执行Docker构建后全站卡顿
多个Build在Controller执行Docker构建,占满CPU、磁盘IO和Docker Root Dir;UI、Queue和Pipeline持久化都变慢。
修复:Controller Executor设为受控值或0,构建迁移到隔离Agent;为Agent设置资源、日志和Docker清理策略,Controller只做控制面。
三十二、面试标准回答
Controller、Agent和Executor区别
Controller保存配置、调度Queue、管理凭据、插件和Pipeline状态;Agent是连接Controller并真正执行命令的运行环境;Executor是Node可同时承载的构建槽位,不等于CPU核。Executor过多会让多个Build的JVM、Docker和IO竞争资源。
Jenkins任务为什么一直排队
先看Queue Item的具体原因,常见是Label无匹配Node、Agent离线、Executor占满、禁止并发、Lock/Throttle等待、Quiet Period或Controller压力。不能只重启Jenkins,应结合Node、Executor、上一个Build和插件限制定位。
workspace@2是什么
同一Job在同一Node并发执行时,为隔离文件目录,Jenkins可能创建workspace、workspace@2等目录。脚本不能硬编码绝对Workspace路径;即使目录隔离,Maven缓存、Docker daemon、固定端口和部署环境仍可能冲突。
Workspace、stash和artifact区别
Workspace是Node临时工作目录;stash用于同一Pipeline跨Node传递相对小的文件;archive把产物关联到Build记录;正式Jar和镜像应进入Nexus/Registry等外部制品仓库。生产应一次构建、多环境推广同一不可变制品。
Jenkins运行JDK和业务编译JDK必须一样吗
不必。现代Jenkins Controller/Agent进程使用当前LTS支持的现代Java,而业务可通过Tool、Maven Toolchains、容器或不同Label Agent使用JDK 7/8/17/21编译。要固定具体版本并验证Maven、TLS和插件兼容。
三十三、学习验收
- 画出Webhook、Queue、Node、Agent、Executor和Workspace全过程。
- 制造Label不匹配、Agent离线和Executor占满三种Queue等待。
- 根据单Build峰值估算Executor,而不是按CPU核机械设置。
- 并发执行同Job并解释workspace@2和共享资源冲突。
- 用现代Agent运行时构建一个JDK 8项目并记录具体8u版本。
- 区分Workspace、stash、archive、Nexus和Registry。
- 从Commit到镜像Digest追踪一次发布。
- 设计JENKINS_HOME备份、插件清单和恢复演练。
- 隔离不可信PR Agent与生产部署凭据。
- 完成Queue、Agent离线和磁盘满Runbook。
