Claude Codeで調べるGitLab CIの失敗 — glabでログを取り原因を絞る
GitLab CIが落ちたとき、glab ci traceでログを端末に取り、rules・needs・cacheのどれが原因かを切り分ける手順です。ログをClaude Codeに読ませる設定例も載せます。
GitLab CIのパイプラインが赤くなったら、ブラウザを開かずにglab ciでジョブのログを端末へ取り、Claude Codeに読ませて原因を絞れます。.gitlab-ci.ymlの失敗は、突き詰めるとrulesの条件漏れ、needsの依存、cacheのキーの不一致のどれかに落ちることが多いです。この記事では、その3つを症状から切り分ける順番と、ログをClaude Codeに渡す設定をまとめます。
@claudeメンションでMRを自動化する側の話は、Claude CodeとGitLab CI/CDの連携にあります。ここで扱うのは逆向きで、手元のClaude Codeが、すでに落ちたパイプラインを調べる場面です。
glab ciで失敗ジョブのログを端末に取る
glabはGitLabのコマンドラインツールで、glab ci以下にパイプラインとジョブを扱うコマンドが並びます。失敗の調査に使うのは次の5つです。インストールと認証の準備はClaude CodeとGitLabの連携方法で触れているので、glab auth loginが済んでいる前提で進めます。
| やりたいこと | コマンド |
|---|---|
| 失敗したパイプラインを探す | コマンドglab ci list --status=failed |
| パイプライン内の失敗ジョブを見る | コマンドglab ci get --status=failed --with-job-details |
| ジョブのログを流す | コマンドglab ci trace <job-id または job名> |
| 設定ファイルを検証する | コマンドglab ci lint --dry-run --ref <ブランチ> |
| 直したジョブだけ再実行する | コマンドglab ci retry <job-id または job名> |
glab ci traceは、ジョブのログを端末へ流すコマンドです。引数なしで実行すると、ジョブを対話的に選べます。ジョブIDかジョブ名を渡せば選択を飛ばせます。
# 現在のブランチのパイプラインから、失敗ジョブだけを一覧にする
glab ci get --status=failed --with-job-details
# ジョブ名を指定してログを見る(例: test というジョブ)
glab ci trace test
# パイプラインIDを指定して探す
glab ci trace test --pipeline-id 12345ジョブの検索先は、既定で現在のブランチです。--branchでブランチを、--pipeline-idでパイプラインを指定できます。
落とし穴
MRのパイプラインがブランチ指定で見つからない
フォークからのMRや、detachedパイプラインでは、MRのhead pipelineがソースブランチの最新パイプラインと食い違うことがあります。glab ci getには--merge-requestがあり、MRのIIDを渡すとそのhead pipelineを対象にできます。glab ci get --merge-request=42 --status=failed --with-job-detailsのように使います。
3つの原因を症状から振り分ける
ログを開く前に、症状でおおよその当たりを付けられます。失敗の見え方が3つで違うためです。
| 症状 | 疑う先 | 最初に打つコマンド |
|---|---|---|
| 期待したジョブがパイプラインに出てこない | 疑う先rules | 最初に打つコマンドglab ci lint --dry-run --ref <ブランチ> |
| パイプライン自体が作られない、または順序がおかしい | 疑う先needs | 最初に打つコマンドglab ci list --yaml-errors |
| ジョブは動くが依存の再取得で遅い、ファイルが無い | 疑う先cache | 最初に打つコマンドglab ci trace <ジョブ名> |
rulesの問題は「失敗」ではなく「不在」として現れます。ログを探しても何も無いので、取るべきは設定のシミュレーション結果です。needsの問題は、パイプラインの作成時点で弾かれることがあります。cacheは、ジョブが動いたうえで効いていない形になるので、ログを読む価値があるのはこの層です。
rules: ジョブがパイプラインに入っていない
rulesは先頭から順に評価され、最初に一致したルールで、ジョブが含まれるか除外されるかが決まります。どれにも一致しなければ、そのジョブはパイプラインに追加されません。ログが無いのはこのためです。
次の例は、MRパイプラインでは手動ジョブとして、スケジュール実行では通常ジョブとして動く設定です。
job:
script: echo "Hello, Rules!"
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: manual
allow_failure: true
- if: $CI_PIPELINE_SOURCE == "schedule"ブランチへのpushで走るパイプラインには、どちらのルールも一致しません。このジョブはそこに現れません。調べる手順は次のとおりです。
rulesの条件漏れを探す手順
- 1
ブランチを指定してシミュレーションする
glab ci lint --dry-run --ref <ブランチ> --include-jobsを実行します。--dry-runはパイプライン作成のシミュレーションで、--refでブランチかタグの文脈を決めます。--include-jobsを付けると、そのパイプラインに存在するジョブの一覧が出ます。 - 2
期待したジョブが一覧にあるか見る
無ければ、そのジョブの
rulesがどの条件にも一致していません。パイプラインの種類($CI_PIPELINE_SOURCE)がpush、MR、スケジュールのどれかを、実行されたパイプラインに合わせて確かめます。 - 3
when: neverの並びを確認する
when: neverを先頭に置いたルールが一致すると、後ろのルールは評価されません。除外条件が広すぎないかを、上から順に読みます。 - 4
スクリプトで作った変数をルールに使っていないか見る
ジョブのスクリプト内で作ったdotenv変数は、
rulesで使えません。ルールはどのジョブよりも先に評価されるためです。
glab ci listの--sourceオプションを使うと、merge_request_event、push、triggerといった起動元でパイプラインを絞れます。「MRでは走るのにpushでは走らない」という食い違いを、実際の実行履歴から見つけたいときに便利です。
needs: 存在しないジョブへの依存
needsはステージの順序を飛ばして、ジョブを先に動かすキーワードです。落とし穴は、needsで指したジョブがrulesで外れている場合に現れます。つまりrulesとneedsの合わせ技です。
needsの対象がonly、except、rulesで除外されうるジョブだと、パイプラインの作成に失敗することがあります。必須のままで、そのジョブがパイプラインに無いと、次のようなエラーで起動しません。
'job1' job needs 'job2' job, but it was not added to the pipeline対処は、依存先が無くても動いてよいならoptional: trueを付けることです。
test-job:
needs:
- job: build-job
optional: true
script: echo "test"依存先が存在するときは、完了を待ってから始まります。存在しなければ、他のneedsが満たされた時点で始まります。needsの中身がすべて任意で、どれもパイプラインに無い場合は、needs: []と同じく即座に始まります。
もうひとつ、needsを付けたジョブは、前のステージの成果物を自動では受け取りません。needsに列挙したジョブの成果物だけをダウンロードします。needsを足したあとで「ファイルが見つからない」と落ちるなら、依存先の列挙漏れを疑います。
needsに書ける要素は最大50個です。dependenciesと同じジョブで併用しない指針もあります。
パイプラインが作られない場合、ジョブのログは存在しません。glab ci list --yaml-errorsは、設定が不正なパイプラインだけを返します。glab ci lintで.gitlab-ci.ymlを手元から検証して、作成時のエラーを再現してください。
cache: ジョブは動くが効かない
cacheの不一致は、ジョブがエラーで止まるより、毎回依存を取り直して遅くなる形で現れます。ジョブが動いている以上、glab ci traceでログを読み、依存の取得に時間を食っていないかを見るのがここでの出番です。
キャッシュは最適化であり、常に効くとは限りません。実際に使えるかどうかは、ランナーのexecutorの種類と、ジョブ間で同じランナーが使われるかに左右されます。公式のトラブルシューティングには、不一致の原因がいくつか挙がっています。
- 分散キャッシュ無しで、複数のスタンドアロンランナーが1つのプロジェクトにつながっている
- 分散キャッシュ無しで、オートスケールのランナーを使っている
- ランナーのマシンかS3バケットの空き容量が足りない
- 異なるパスをキャッシュするジョブが、同じキーを使っている
最後のものは、設定ファイルの側で直せます。同じキーで別のパスを保存すると、アーカイブが上書きされます。
キーを共有していて、キャッシュが上書きされる
同じキーで別のパスを保存
build-job:
cache:
key: shared
paths: [vendor/]
test-job:
cache:
key: shared
paths: [node_modules/]ジョブごとにキーを分ける
build-job:
cache:
key: build-$CI_COMMIT_REF_SLUG
paths: [vendor/]
test-job:
cache:
key: test-$CI_COMMIT_REF_SLUG
paths: [node_modules/]依存ファイルの内容でキーを決めるなら、cache:key:filesを使います。列挙したファイルの内容からSHAでキーが作られ、内容が変わらなければブランチやパイプラインをまたいで同じキーになります。Gemfile.lockやpackage-lock.jsonを指定する使い方が典型です。注意点が2つあります。
- ファイルの個数には上限があり(GitLabのバージョンと機能フラグで変わります)、CI/CD変数は使えません
- 指定したファイルがどれも存在しないとき、キーは
defaultになります
キーが一致しないときの保険には、fallback_keysが使えます。1つのキャッシュエントリに最大5個の代替キーを指定でき、主キーが見つからなければ順に試されます。どれも見つからなければ、キャッシュ無しで依存を取得し、ジョブの終了時に新しいキャッシュを作ります。
なお、同じパスをcacheとartifactsの両方で保存すると、キャッシュが上書きされることがあります。キャッシュはartifactsより先に復元されるためです。
ログをClaude Codeに読ませて原因を絞る
ログの読み取りは、Claude Codeの非対話モードに渡すのが手早い方法です。標準入力でログを渡し、-pで指示文を与えます。Claude Codeの公式の例にも、ビルドエラーのファイルをパイプで渡して根本原因を説明させる形があります。
# ログの末尾だけを切り出してファイルに保存する
glab ci trace test | tail -n 300 > /tmp/ci-test.log
# 設定ファイルと一緒に原因候補を挙げさせる
cat /tmp/ci-test.log | claude -p \
"このCIログの失敗の根本原因を3行で説明し、.gitlab-ci.ymlの該当箇所を示して"ログ全体ではなくtailで末尾だけを渡すのは、巨大なログでトークンを使い切らないためです。失敗の直前の出力は、ログの末尾に集まります。
調査を対話セッションで続けるなら、読み取り系のglabコマンドだけを許可しておくと、確認の回数が減ります。permissions.allowの書式は、サブコマンドの後ろに*を置くプレフィックス一致です。次の例は.claude/settings.jsonに入れる形です。
{
"permissions": {
"allow": [
"Bash(glab ci list *)",
"Bash(glab ci get *)",
"Bash(glab ci trace *)",
"Bash(glab ci lint *)"
]
}
}glab ci retryとglab ci runは、パイプラインを実際に動かします。これらをallowに入れなければ、通常の権限モードでは実行前に確認が入ります。意図しない再実行を避けたいリポジトリでは、この状態を保つほうが安全です。
リポジトリのCLAUDE.mdには、調べる順番を書いておくと、毎回同じ手順で動きます。
## CIが落ちたとき
1. `glab ci get --status=failed --with-job-details` で失敗ジョブを特定する
2. ジョブが出てこないなら `glab ci lint --dry-run --ref <ブランチ> --include-jobs`
3. `glab ci trace <ジョブ名>` の末尾300行から原因を絞る
4. 直したら `glab ci lint` で検証してから、再実行はユーザーに確認するこの手順の肝は、ログを読む前に症状で3つに振り分けることです。Claude Codeにログだけを渡すと、rulesの不在のような「ログが無い失敗」を取りこぼします。
CI上のClaudeジョブ自体が落ちる場合
ここまでは、アプリのテストやビルドのジョブが落ちる話でした。GitLab CI/CDにClaude Codeを組み込んでいるなら、Claudeを動かすジョブ自体が落ちるケースもあります。公式のトラブルシューティングは、次の3つを挙げています。
@claudeに反応しない: パイプラインが起動しているか、ANTHROPIC_API_KEYなどの変数が入っているか、コメントが/claudeでなく@claudeか- コメントやMRを書けない:
CI_JOB_TOKENの権限、またはapiスコープのProject Access Token。--allowedToolsへのmcp__gitlabの追加 - 認証エラー: Claude APIならキーの有効期限。BedrockやGoogle CloudならOIDC/WIFの設定とリージョン
これらはrules・needs・cacheとは別の層の問題です。ジョブのログを開いて認証系の文言が出ているなら、こちらを先に当たります。Bedrockの認証はGitLab CI/CDでBedrock OIDCを組む手順に詳しくあります。
まとめ
GitLab CIの失敗調査は、ログを開く前に症状で3つへ振り分けるほうが速く終わります。ジョブが出てこないならrulesで、glab ci lint --dry-runで確かめます。パイプラインが作られないならneedsで、optional: trueが効く場面かを見ます。動いているのに遅い、ファイルが無いならcacheで、キーとランナーの構成を疑います。
Claude Codeに任せる範囲は、読み取り系のコマンドまでにとどめるのが扱いやすい構成です。再実行は人が決めると、赤いパイプラインを闇雲に回し続ける事態を避けられます。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。