从排队查询到精准取消,用脚本和命令行驯服构建队列
在日常的 CI/CD 运维中,Jenkins 构建队列积压是家常便饭。无论是节点故障、并发风暴,还是误触发大量参数化构建,你都需要一套快速、精准的任务队列管理手段。本文将系统讲解如何查询指定 Job 的排队任务,以及如何取消特定 ID 区间或符合任意条件的构建,重点剖析脚本控制台(Groovy)和命令行/API 两种专家级操作方式。
一、基础:理解 Jenkins 队列与任务 ID
1.1 构建队列(Build Queue)
当 Jenkins 无法立即执行一个构建(例如所有执行器繁忙,或等待节点上线)时,该构建会进入构建队列。每个队列项包含:
- 所属任务(Job)
- 触发原因
- 构建参数(如果是参数化任务)
- 一个全局唯一的 Queue ID(自增整数,代表进入队列的顺序)
1.2 任务 ID vs 构建号
- Queue ID:排队期间的临时标识,可用于精准取消。
- Build Number:构建开始执行后分配,并持久化在构建历史中。
二者没有直接对应关系。一个任务可能多次排队并取消,最终只有一个成功执行并获得 Build Number。
二、快速入门:Web UI 基础操作
对于临时查看和少量操作,Jenkins 原生界面足够:
-
查看所有排队任务 :Dashboard → 构建队列 (左侧边栏)
列表显示任务名、预估等待时间,右侧红色 "X" 可取消单个。
-
查看特定任务的排队情况:进入该任务的详情页,左侧 "队列" 区域会直接显示当前该任务的排队位置(如有)。
局限:无法批量取消,无法按 ID 区间或参数筛选。
三、核心利器:脚本控制台(Script Console)
这是 Jenkins 管理员最强大的武器。通过 系统管理 → 脚本控制台 执行 Groovy 代码,可以直接操作 Jenkins 内部对象。
3.1 查询指定 Job 的所有排队任务
获取任务名、Queue ID、排队时长、等待原因等信息,为后续精准取消做准备。
groovy
def jobName = 'my-pipeline-job' // 替换为你的任务名
def queue = Jenkins.instance.queue
println "🔍 查询任务 '${jobName}' 的排队情况"
println "=" * 50
def items = queue.items.findAll { it.task.name == jobName }
if (items.isEmpty()) {
println "✅ 没有排队任务"
return
}
items.each { item ->
println "任务全名: ${item.task.fullDisplayName}"
println "Queue ID : ${item.id}"
println "排队时间: ${item.getInQueueSinceString()}"
println "等待原因: ${item.getWhy()}"
println "构建参数: ${item.params}"
println "-" * 50
}
3.2 取消特定 Queue ID 区间的任务
场景:某段时间触发了大量低优先级构建,ID 在 1000~2000 之间,需要批量清理。
groovy
// ====== 配置参数 ======
def targetJob = 'my-pipeline-job'
def startId = 1000L
def endId = 2000L
// ====================
def queue = Jenkins.instance.queue
def toCancel = queue.items.findAll { item ->
item.task.name == targetJob &&
item.id >= startId &&
item.id <= endId
}
println "找到 ${toCancel.size()} 个符合区间 [${startId}, ${endId}] 的排队任务"
toCancel.each { item ->
println "取消: ${item.task.name} (Queue ID: ${item.id})"
queue.cancel(item)
}
println "✅ 完成"
3.3 模糊匹配任务名并取消
当你有多个类似任务(例如多分支流水线 feature/*)需要同时清理时:
groovy
def namePattern = "feature-" // 任务名包含该字符串
def queue = Jenkins.instance.queue
queue.items.findAll { it.task.name.contains(namePattern) }.each { item ->
println "取消: ${item.task.name} (ID: ${item.id})"
queue.cancel(item)
}
3.4 按构建参数精准取消
对于参数化任务,可以基于某个参数值(如 BRANCH=dev)来取消排队:
groovy
def jobName = 'deploy-job'
def targetBranch = 'dev'
def queue = Jenkins.instance.queue
queue.items.findAll { item ->
item.task.name == jobName &&
item.params.any { param ->
param.name == 'BRANCH' && param.value == targetBranch
}
}.each { queue.cancel(it) }
3.5 清空整个队列(慎用!)
紧急情况下可以清空所有排队任务:
groovy
Jenkins.instance.queue.clear()
⚠️ 安全提示 :脚本控制台需要
Overall/Administer权限。在生产环境执行批量删除前,建议先运行查询脚本确认范围。
四、命令行与 API:自动化集成首选
当需要将队列管理集成到 Shell 脚本或平台工具(如 Ansible、Python)时,Jenkins CLI 和 REST API 是最佳选择。
4.1 Jenkins CLI
前提 :下载 jenkins-cli.jar,通常位于 http://<jenkins-url>/jnlpJars/jenkins-cli.jar。
查看整个队列(简单列表)
bash
java -jar jenkins-cli.jar -s http://localhost:8080 -auth user:token list-queue
输出示例:
1 my-pipeline-job #1
2 another-job #12
取消指定 Queue ID 的任务
CLI 没有直接的 cancel-queue-item 命令?在较新版本中可使用以下命令:
bash
java -jar jenkins-cli.jar -s http://localhost:8080 -auth user:token cancel-queue-item <queue-id>
如果版本较旧,可以改用 REST API(见下节)。
4.2 REST API -- 最通用
获取队列 JSON 信息
bash
curl -u "username:apiToken" \
"http://localhost:8080/queue/api/json?pretty=true"
返回结构示例:
json
{
"items": [
{
"id": 1234,
"task": {
"name": "my-pipeline-job",
"url": "..."
},
"params": "...",
"inQueueSince": 1715000000000
}
]
}
使用 jq 筛选指定任务的 Queue ID
bash
curl -s -u "user:token" "http://localhost:8080/queue/api/json" \
| jq -r '.items[] | select(.task.name=="my-pipeline-job") | .id'
取消指定 Queue ID 的任务
bash
curl -X POST -u "user:token" \
"http://localhost:8080/queue/cancelItem?id=1234"
组合:取消某任务 ID>1000 的所有排队任务(Shell + jq)
bash
jobName="my-pipeline-job"
minId=1000
curl -s -u "user:token" "http://localhost:8080/queue/api/json" \
| jq -r --arg job "$jobName" --arg min "$minId" \
'.items[] | select(.task.name==$job and .id>=($min|tonumber)) | .id' \
| while read id; do
echo "Cancelling $id"
curl -X POST -u "user:token" "http://localhost:8080/queue/cancelItem?id=$id"
done
4.3 使用 Python + Jenkins 库
python
import jenkins
server = jenkins.Jenkins('http://localhost:8080', username='user', password='token')
queue_info = server.get_queue_info()
for item in queue_info:
if item['task']['name'] == 'my-pipeline-job' and item['id'] > 1000:
# jenkins-python 库没有直接封装 cancel,可以发送原始 POST
server.jenkins_open(server._request('POST', f'/queue/cancelItem?id={item["id"]}'))
五、专家级实践与技巧
5.1 防止任务积压:流水线自清理
在 Jenkinsfile 中,可以在并行任务开始时自动取消当前任务的旧排队项:
groovy
// 在 pipeline 开头执行
def cancelOldQueueItems() {
def queue = Jenkins.instance.queue
def myId = currentBuild.rawBuild.getQueueId()
queue.items.findAll { it.task.name == env.JOB_NAME && it.id < myId }.each {
println "Cancelling older queued build ${it.id}"
queue.cancel(it)
}
}
5.2 定时清理陈旧任务
使用 Groovy 脚本结合 Jenkins 的 cron 触发器(通过 Groovy Script 插件或直接修改 init.groovy):
groovy
// 每小时清理排队超过 30 分钟的任务
import hudson.model.Queue
def queue = Jenkins.instance.queue
def now = System.currentTimeMillis()
queue.items.each { item ->
if (now - item.getInQueueSince() > 30 * 60 * 1000) {
println "Cancelling stale item ${item.id} (${item.task.name})"
queue.cancel(item)
}
}
5.3 安全注意事项
- 权限控制 :以上所有操作均需要
Overall/Administer或Cancel权限。在生产环境建议使用 API Token 而非明文密码。 - 日志审计 :Jenkins 的系统日志会记录队列取消操作。通过脚本控制台执行的操作会记录在
jenkins.log中。 - 测试先行:大规模取消前,先运行查询脚本确认影响范围。
六、概念关系总结
text
+----------------+ 排队时分配 +----------------+
| Jenkins Job | ------------------> | Queue Item |
| (任务定义) | | - Queue ID |
+----------------+ | - Build Params |
| - Timestamp |
+----------------+
|
开始执行后转为 |
v
+----------------+
| Build |
| - Build Number|
+----------------+
- Queue ID:临时、自增、全局唯一,用于排队期管理。
- Build Number:永久、任务内自增,用于历史追溯。
- 脚本控制台:通过 Groovy 直接操作 Jenkins 对象模型,适合复杂筛选。
- REST API:标准化接口,适合跨语言、跨平台集成。
七、总结
| 需求场景 | 推荐方式 | 核心命令/脚本 |
|---|---|---|
| 快速查看个别任务排队 | Web UI | 项目页面左侧"队列" |
| 查询指定任务的所有排队项 | 脚本控制台 | queue.items.findAll{it.task.name==...} |
| 批量取消 ID 区间任务 | 脚本控制台 | 结合 id>=start && id<=end 过滤 |
| 按构建参数精准取消 | 脚本控制台 | 检查 item.params |
| 清空整个队列 | 脚本控制台 / CLI | Jenkins.instance.queue.clear() / clear-queue |
| 集成到 Shell 脚本 | REST API + curl | curl -X POST /queue/cancelItem?id=... |
| 集成到 Python 应用 | python-jenkins + requests | 发送原始 POST 请求 |
掌握脚本控制台和 REST API,你就能像管理进程一样精细操控 Jenkins 的构建队列。无论是日常运维还是应急响应,都可以做到"指哪打哪"。
最后提醒 :在脚本控制台中使用 cancel 操作前,务必先用 findAll 加 println 预览结果,避免误取消重要构建。
🧠 思考:如果节点故障导致上千个任务排队,如何只取消某个特定 Git 分支的任务而保留其他?欢迎在评论区讨论。