使用 Jenkins CLI 快速排查构建失败

使用 Jenkins CLI 检查失败的构建、流式查看日志、检查参数并重新运行任务,无需在 UI 中层层点击。

使用 Jenkins CLI 快速排查构建失败

Jenkins Web UI 在探索时很有用。当您已经知道任务名称、需要立即获取日志或希望无需点击页面即可重复相同检查时,Jenkins CLI 更胜一筹。

一次失败的构建通常需要三部分信息:运行了什么、在哪里失败、以及自上次成功运行以来发生了什么变化。如果一次性设置好 CLI,这三部分信息都能快速获取。

设置可重复使用的 CLI 命令

从您自己的 Jenkins 控制器下载 CLI jar:

curl -O "$JENKINS_URL/jnlpJars/jenkins-cli.jar"

使用 API 令牌,而不是您的账户密码:

export JENKINS_URL="https://jenkins.example.com"
export JENKINS_USER="your-user"
export JENKINS_API_TOKEN="your-api-token"

alias jcli='java -jar jenkins-cli.jar -s "$JENKINS_URL" -auth "$JENKINS_USER:$JENKINS_API_TOKEN"'

在排查实际故障前测试身份验证:

jcli who-am-i
jcli help

如果失败,请先修复 CLI 访问。常见原因包括:URL 错误、令牌复制时包含多余空格、权限不足、或 Jenkins 安全设置禁用了您尝试使用的 CLI 传输方式。

对于共享脚本,避免使用 shell 别名。将命令封装到小型脚本中,或使用 CI 密钥存储中的环境变量,以免令牌泄露到 shell 历史记录中。

查找失败的构建

如果您知道任务名称和构建编号,可直接跳转到控制台日志。如果只知道最近有构建失败,可通过 CLI 使用 Groovy 开始:

jcli groovy = <<'EOF'
Jenkins.instance.getAllItems(hudson.model.Job.class).each { job ->
  def b = job.getLastBuild()
  if (b != null && b.result == hudson.model.Result.FAILURE) {
    println "${job.fullName} #${b.number} ${b.getTime()}"
  }
}
EOF

对于文件夹和多分支流水线,请使用 Jenkins 识别的完整任务名称。分支任务可能如下所示:

team-service/main
team-service/PR-42
folder-a/folder-b/deploy-prod

如果名称包含空格,请在 shell 中加引号:

jcli console "Folder With Spaces/My Pipeline" 128

首先获取控制台日志

对于大多数故障,控制台日志仍然是最快的真相来源:

jcli console MyPipelineJob 123

对于长日志,保存副本并搜索上下文:

jcli console MyPipelineJob 123 > build-123.log
grep -niE "error|failed|exception|traceback|permission denied|timeout" build-123.log | head -40
grep -niC 3 "permission denied" build-123.log

不要只依赖包含 "error" 的第一行。构建工具通常会打印警告、测试名称或预期的失败文本。查找阶段开始后第一个有意义的失败,然后阅读其前面的行。原因通常位于堆栈跟踪之上。

对于正在运行的构建,实时跟踪日志:

jcli console MyPipelineJob 123 -f

某些 Jenkins 版本和 CLI 模式接受 --follow,其他则使用 -f。请在您的控制器上检查 jcli help console

识别失败的阶段

流水线日志包含阶段标记。在失败附近搜索它们:

grep -n "\[Pipeline\] stage" build-123.log

然后检查错误之前最后一个阶段标记之后的部分。如果日志内容嘈杂,请使用 less 打开:

less build-123.log

less 中,搜索 /ERROR/Exception/[Pipeline] stage

一个好的 Jenkinsfile 通过在风险操作前打印清晰的步骤名称来简化此过程:

echo 'Installing npm dependencies'
sh 'npm ci'
echo 'Running unit tests'
sh 'npm test'

如果每个步骤都只是 sh './ci.sh',CLI 仍然可以获取日志,但日志可能无法提供足够信息。在这种情况下,请在事件发生后改进流水线。

检查参数和原因

构建可能因为使用了错误的分支、错误的环境或过时的参数而失败。使用 Groovy 获取构建元数据:

jcli groovy = <<'EOF'
def job = Jenkins.instance.getItemByFullName('MyPipelineJob')
def build = job?.getBuildByNumber(123)
if (build == null) {
  println 'Build not found'
  return
}
println "Job: ${job.fullName}"
println "Build: #${build.number}"
println "Result: ${build.result}"
println "Duration: ${build.durationString}"
println 'Causes:'
build.getCauses().each { println "- ${it.shortDescription}" }
println 'Parameters:'
build.getAction(hudson.model.ParametersAction)?.parameters?.each {
  println "- ${it.name}=${it.value}"
}
EOF

这对于部署任务尤其有用。使用 BRANCH=feature/test 的生产部署失败与预期发布标签的部署失败是完全不同的问题。

打印环境变量时要小心。它们可能包含密钥。如果您需要环境详细信息,请打印特定的安全键,而不是转储所有内容:

jcli groovy = <<'EOF'
def job = Jenkins.instance.getItemByFullName('MyPipelineJob')
def build = job?.getBuildByNumber(123)
def env = build?.getEnvironment(TaskListener.NULL)
['JOB_NAME', 'BUILD_NUMBER', 'BRANCH_NAME', 'GIT_COMMIT', 'NODE_NAME'].each { key ->
  println "${key}=${env?.get(key)}"
}
EOF

如果您的脚本缺少权限,或者某个插件更改了可用的元数据,请回退到控制台日志和任务配置。

与上次成功构建比较

单个失败日志很有用。但失败日志与上次成功日志对比更好。

查找上次成功构建的编号:

jcli groovy = <<'EOF'
def job = Jenkins.instance.getItemByFullName('MyPipelineJob')
def b = job?.getLastSuccessfulBuild()
println b == null ? 'No successful build found' : b.number
EOF

然后获取两个日志:

jcli console MyPipelineJob 122 > build-122-success.log
jcli console MyPipelineJob 123 > build-123-failed.log

比较命令、依赖版本、代理标签、分支和检出 SHA。许多失败源于基础镜像变更、依赖移动、不同代理或构建之间过期的凭据。

对于基于 Git 的流水线,这些值通常足以开始排查:

grep -nE "Checking out|GIT_COMMIT|BRANCH_NAME|git rev-parse|Docker|image:" build-123-failed.log

谨慎重新运行构建

一旦您理解了失败原因或应用了修复,触发新构建:

jcli build MyPipelineJob

对于参数化任务:

jcli build MyPipelineJob -p BRANCH=main -p ENVIRONMENT=staging

要等待并流式输出,请检查控制器支持的选项:

jcli help build

常见选项包括等待完成和打印控制台输出,但名称可能因 Jenkins 版本和插件而异。

不要仅仅为了看看会发生什么而重新运行生产部署任务。首先确认参数、凭据和目标环境。如果任务不是幂等的,重新运行可能会使事件恶化。

在事件记录中使用 CLI 输出

CLI 适合留下痕迹。保存您使用的确切日志和元数据:

mkdir -p incident-jenkins-123
jcli console MyPipelineJob 123 > incident-jenkins-123/console.log
jcli get-job MyPipelineJob > incident-jenkins-123/job-config.xml

如果 job-config.xml 包含凭据引用、URL 或内部名称,请将其视为敏感信息。不要将其粘贴到公开工单中。

一个紧凑的事件记录可能包括:

Job: MyPipelineJob #123
Failed stage: Unit tests
First failing command: npm test
Commit: abc1234
Agent: linux-build-07
Likely cause: dependency install used Node 22 instead of expected Node 20
Action: pinned tool version and reran #124 successfully

这比 "Jenkins failed" 有用得多。

实用的故障排查流程

当构建失败且需要快速获取信号时,请按此顺序操作:

jcli who-am-i
jcli console MyPipelineJob 123 > build-123.log
grep -niE "error|failed|exception|permission denied|timeout" build-123.log | head -40
grep -n "\[Pipeline\] stage" build-123.log

然后使用 Groovy 检查参数和原因,与上次成功构建比较,并且只有在确定失败是代码、基础设施、凭据还是输入参数导致之后才重新运行。

Jenkins CLI 不会神奇地诊断每次构建失败。它的优势在于消除摩擦。它能快速将日志、元数据和重新运行命令带入您的终端,您可以在其中搜索、比较、保存,并在下次流水线中断时重复相同的检查。