庖丁解牛:Jenkins 任务队列管理完全指南

从排队查询到精准取消,用脚本和命令行驯服构建队列

在日常的 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 原生界面足够:

  1. 查看所有排队任务 :Dashboard → 构建队列 (左侧边栏)

    列表显示任务名、预估等待时间,右侧红色 "X" 可取消单个。

  2. 查看特定任务的排队情况:进入该任务的详情页,左侧 "队列" 区域会直接显示当前该任务的排队位置(如有)。

局限:无法批量取消,无法按 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/AdministerCancel 权限。在生产环境建议使用 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 操作前,务必先用 findAllprintln 预览结果,避免误取消重要构建。

🧠 思考:如果节点故障导致上千个任务排队,如何只取消某个特定 Git 分支的任务而保留其他?欢迎在评论区讨论。

相关推荐
凤舞飘伶2 天前
jenkins问题处理方法
jenkins
lbb 小魔仙2 天前
告别腾讯会议40分钟限制:用ToDesk协作版开在线会议,免费不限时远程会议新方案
python·langchain·jenkins
恼书:-(空寄2 天前
从手动部署到一键发版:Java项目CI/CD流水线搭建实录
ci/cd·jenkins·流水线部署
星梦清河2 天前
微服务-Elasticsearch02
微服务·架构·jenkins
终端行者2 天前
Jenkins Pipeline 构建后推送到Nexus制品库 jenkins 如何连接Nexus?企业级实战 --中 Jenkins 连接Nexus 实战
运维·ci/cd·docker·jenkins·nexus
隔窗听雨眠2 天前
一份完整的Jenkins故障排查指南
jenkins
终端行者2 天前
Jenkins Pipeline 构建后推送到Nexus制品库 jenkins 如何连接Nexus?企业级实战 --上 Nexus部署
运维·ci/cd·jenkins·nexus
小闫BI设源码3 天前
当20个节点选出两个Master时:Elasticsearch的致命故障与解决方案
java·elasticsearch·jenkins·php·面试宝典·深入解析
醉颜凉3 天前
Elasticsearch 核心原理:Posting List 倒排列表深度详解
大数据·elasticsearch·jenkins