一次次看着Jenkins任务卡在Pending状态,程序员小张忍不住对着屏幕大喊:“你到底在等什么?!”
简单来说,当Jenkins任务既没有在执行,也没有失败或成功,而是停留在“等待中”(Pending)状态时,它们就成为了“未完成任务”。最常见的提示就是那句让人头疼的:“Pending - Waiting for Next Executor”。
想象一下,你去餐厅吃饭,服务员告诉你:“不好意思,现在没有厨师,也没有厨具,您得等一会儿”。然后这个“一会儿”变成了无限期——这就是Jenkins中未完成任务的真实写照。
任务卡住的原因多种多样,就像交通堵塞的原因也各不相同。以下是最常见的几种情况:
这是最常见的原因,好比节假日的高速收费站,所有收费口都排满了车,新来的车只能无奈等待。
Master节点Executor数为0:有人用Helm安装Jenkins时,就发现master节点的执行器数量默认是0,导致任务永远等待所有Executor都在忙碌:当并发任务数超过Executor数量时,新任务只能排队有些任务就像迷路的司机,在路口一动不动:
等待人工输入:配置了需要人工确认的流程,但没人去点击死循环或长时间执行:任务进入无限循环,或者有大量工作要处理资源锁未释放:任务占用了某个资源但未释放,其他任务无法获取当Jenkins服务器本身资源不足时,就像交通指挥中心自己断电了:
CPU使用率过高内存不足磁盘空间满:特别是硬盘被占满后,会导致无法构建Jenkins master与slave节点之间的连接不稳定,就像指挥员与交警之间的对讲机时好时坏。
Jenkinsfile中指定的节点标签不存在,就像你把车开到了一个不存在的停车场。
排查Jenkins任务问题,就像侦探破案,需要一步步搜集线索。
第一站,查看“交通流量”情况:
登录Jenkins管理界面点击“Manage Jenkins”选择“Manage Nodes and Clouds”查看所有节点和Executor状态正常情况:所有节点在线,且有可用Executor
异常情况:节点离线,或Executor全部忙碌
检查“指挥中心”本身的运行状况:
# 检查CPU和内存
top
# 检查磁盘空间
df -h
# 检查Jenkins日志
tail -f /var/log/jenkins/jenkins.log
有时候,任务卡住是因为在等“同伴”:
检查是否有上游任务未完成查看是否存在循环依赖检查共享资源是否被占用Jenkins的主日志文件通常位于
JENKINS_HOME/logs/jenkins.log,搜索关键词如
Executor、
Queue、
Node、
Offline、
Blocked可以帮助定位问题。
找到原因后,就要对症下药了。
如果是Executor不足,可以考虑增加“收费口”:
进入Manage Jenkins → Manage Nodes and Clouds点击对应节点(包括master节点)配置Executor数量(如果是master节点,记得不要设为0)保存设置对于已经卡住的任务,有时候需要“强硬手段”:
在Jenkins主页面,找到卡住的任务点击任务旁边的×按钮强制停止如果无法停止,可以尝试重启Jenkins确保Jenkins服务器有足够的资源:
定期清理不需要的构建历史增加磁盘空间增加内存或CPU使用监控插件(如Monitoring、Resource Monitor)跟踪资源使用情况对于master-slave连接问题:
检查网络连接验证节点凭证配置自动重连机制检查Jenkinsfile中的节点配置,确保引用的标签实际存在:
// 错误的配置 - 如果'test'节点不存在
node('test') {
// 步骤
}
// 正确的配置 - 使用存在的节点标签
node('linux-slave') {
// 步骤
}
// 或者使用任何可用节点
node('any') {
// 步骤
}
下面我们创建一个完整的示例,用于监控并报告Jenkins中未完成的任务。
pipeline {
agent any
stages {
stage('Check Node Status') {
steps {
script {
// 获取所有节点
def nodes = Jenkins.instance.nodes
nodes.each { node ->
echo "检查节点: ${node.nodeName}"
echo "执行器数量: ${node.numExecutors}"
echo "空闲执行器: ${node.computer.countIdle()}"
echo "在线状态: ${node.computer.online}"
// 检查节点负载
if (node.computer.countIdle() == 0) {
echo "警告: 节点 ${node.nodeName} 没有空闲执行器!"
}
}
}
}
}
stage('Check Queue Items') {
steps {
script {
// 获取排队中的任务
def queue = Jenkins.instance.queue
def items = queue.items
if (items.length > 0) {
echo "发现 ${items.length} 个任务在队列中等待:"
items.each { item ->
echo "任务: ${item.task.name}, 等待时间: ${item.inQueueFor} ms"
}
} else {
echo "队列中没有等待的任务。"
}
}
}
}
stage('Generate Report') {
steps {
script {
// 生成HTML报告
def reportFile = "${env.WORKSPACE}/pending-tasks-report.html"
def htmlContent = """
<!DOCTYPE html>
<html>
<head>
<title>Jenkins未完成任务报告</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.warning { color: #ff9900; font-weight: bold; }
.critical { color: #ff0000; font-weight: bold; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
</style>
</head>
<body>
<h1>Jenkins未完成任务报告</h1>
<p>生成时间: ${new Date()}</p>
</body>
</html>
"""
writeFile file: reportFile, text: htmlContent
echo "报告已生成: ${reportFile}"
}
}
}
}
post {
always {
emailext (
subject: "Jenkins未完成任务报告 - ${currentBuild.result ?: 'SUCCESS'}",
body: """
构建结果: ${currentBuild.result ?: 'SUCCESS'}
构建URL: ${env.BUILD_URL}
请检查附件中的详细报告。
""",
to: "team@example.com",
attachmentsPattern: "**/pending-tasks-report.html"
)
}
}
}
对于临时检查,可以使用Jenkins的脚本命令行功能:
// 获取所有离线节点
println "离线节点:"
Jenkins.instance.nodes.each { node ->
if (!node.computer.online) {
println " - ${node.nodeName} (离线)"
}
}
// 获取排队任务
println "
排队中的任务:"
def queue = Jenkins.instance.queue
queue.items.each { item ->
println " - ${item.task.name} (在队列中 ${item.inQueueFor} ms)"
}
// 检查所有执行器状态
println "
执行器状态:"
Jenkins.instance.computers.each { computer ->
println "计算机: ${computer.name}"
computer.executors.each { executor ->
if (executor.isBusy()) {
println " - 执行器 ${executor.number}: 忙碌"
}
}
computer.oneOffExecutors.each { executor ->
if (executor.isBusy()) {
println " - 一次性执行器 ${executor.number}: 忙碌"
}
}
}
创建一个定期清理卡住任务的脚本:
pipeline {
agent any
triggers {
cron('H 2 * * 1-5') // 工作日凌晨2点执行
}
stages {
stage('Clean Stuck Tasks') {
steps {
script {
def queue = Jenkins.instance.queue
def items = queue.items
def cleanedCount = 0
items.each { item ->
// 如果任务在队列中超过2小时
if (item.inQueueFor > 2 * 60 * 60 * 1000) {
echo "清理长时间等待的任务: ${item.task.name}"
queue.cancel(item)
cleanedCount++
}
}
echo "共清理 ${cleanedCount} 个卡住的任务"
}
}
}
}
}
除了解决问题,更重要的是预防问题。
为任务设置超时时间,避免无限期等待:
pipeline {
agent any
options {
timeout(time: 1, unit: 'HOURS') // 设置1小时超时
}
stages {
stage('Build') {
steps {
sh './build.sh'
}
}
}
}
合理使用并行执行,提高Executor利用率:
pipeline {
agent any
stages {
stage('Build and Test') {
parallel {
stage('Build') {
steps {
sh './build.sh'
}
}
stage('Test') {
steps {
sh './test.sh'
}
}
}
}
}
}
确保构建环境健康稳定:
定期重启Jenkins:使用safeRestart命令安全重启更新插件:保持插件最新版本,避免已知问题备份配置:使用ThinBackup等插件定期备份配置Jenkins未完成任务就像马路上的抛锚车辆,不及时处理就会导致整个交通瘫痪。通过系统化的监控、分析和处理,我们可以最大限度地减少这类问题对开发流程的影响。
关键要点总结:
定期检查节点和执行器状态,确保资源充足监控系统资源,避免资源瓶颈设置合理的超时和重试机制,防止任务无限期等待使用Pipeline作为代码,实现可重复、可维护的构建流程建立预警机制,及时发现问题并处理记住,一个健康的Jenkins环境就像顺畅的交通系统,需要定期的维护和优化。不要让未完成任务成为你开发流程中的“堵点”!
以上内容基于Jenkins实践经验和社区知识整理,具体实施时请根据你的环境进行调整。祝你构建愉快!