Jenkins CLIを使用した失敗ビルドの迅速なトラブルシューティング

Jenkins CLIを使用して、UIを操作することなく、失敗したビルドの調査、ログのストリーミング、パラメータの確認、ジョブの再実行を行います。

Jenkins CLIを使用した失敗ビルドの迅速なトラブルシューティング

JenkinsのWeb UIは探索時に便利です。Jenkins CLIは、ジョブ名が既にわかっている場合、すぐにログが必要な場合、またはページをクリックせずに同じチェックを繰り返したい場合に適しています。

失敗したビルドでは通常、何が実行されたか、どこで失敗したか、最後の成功ビルドから何が変更されたかの3つの情報が必要です。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、余分なスペースが含まれたトークン、権限の不足、または使用しようとしているCLIトランスポートを無効にするJenkinsセキュリティ設定です。

共有スクリプトでは、シェルエイリアスを避けてください。コマンドを小さなスクリプトでラップするか、CIシークレットストアで環境変数を使用して、トークンがシェル履歴に残らないようにします。

失敗したビルドを見つける

ジョブ名とビルド番号がわかっている場合は、コンソールログに直接スキップします。最近何かが失敗したことだけを知っている場合は、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

名前にスペースが含まれる場合は、シェルで引用符で囲みます:

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が失敗しました」よりもはるかに有用です。

実用的なトラブルシューティングフロー

ビルドが失敗し、迅速にシグナルが必要な場合は、次の順序を使用します:

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はすべての失敗ビルドを魔法のように診断するわけではありません。しかし、摩擦を取り除くことには優れています。ログ、メタデータ、再実行コマンドを迅速にターミナルに取得し、そこで検索、差分、保存、そして次回パイプラインが壊れたときに同じチェックを繰り返すことができます。