【GitLab CI/CD】パイプライン炎上を秒速で鎮火せよ:現場のテックリードが明かす極限のトラブルシューティングと構造化設計
開発のスピードを落とさず、プロダクトの品質を担保する——その要であるはずのGitLab CI/CDパイプラインが、赤く染まって爆発している。
「ローカルでは動くのに、なぜかCIで落ちる」
「突然、Runnerが応答しなくなった」
「機密情報(環境変数)が正しく渡っていない」
デプロイのたびにこんな無駄なコンテキストスイッチが発生していませんか?
こんにちは。数々の修羅場をくぐってきたテックリードの私から言わせれば、パイプラインの失敗は「運が悪かった」のではなく、「設計の解像度が足りていない」か「ツールの裏側の挙動を把握していない」かのどちらかです。
今回は、GitLabのパイプラインが失敗する代表的な原因を深掘りし、二度と同じエラーを踏まないための「実務で使える極限の知見」をすべて公開します。
—
1. エラーの急所を突く:真のログを見極める確認メソッド
パイプラインが赤くなった瞬間、慌てて数千行のジョブログを上からスクロールしていませんか? それはエンジニアの時間の無駄遣いです。
黄金のデバッグ手順
1. 失敗したジョブの末尾200行を見る: エラーの本丸はほとんどの場合、ログの最下部にあります。例外スタックトレースの「最初の行」か、シェルコマンドの「終了コード(Exit Code)」に注目してください。
2. `CI_DEBUG_TRACE: “true”` の即時投入:
どうしても原因がわからない場合は、`.gitlab-ci.yml`のグローバル、またはジョブ変数にこれを仕込みます。
variables:
CI_DEBUG_TRACE: “true” # すべての実行コマンドと環境変数がシェルログに露出する(機密情報のマスキングに注意)
3. Web IDEとシミュレーターの活用:
GitLab標準のCIlint(`/-/ci/lint`)で構文チェックをするのは当然として、複雑な変数の展開確認には、手元でローカル実行できる `gitlab-runner exec` を使い倒しましょう(※Docker executor環境が必須)。
—
2. Runnerのトラブルシューティング:お前はなぜ止まったのか
パイプラインが「Pending(待機中)」のまま永遠に進まない、あるいは突然「Job failed: API error」で落ちる場合、原因はGitLab本体ではなくGitLab Runner側にあります。
頻発する3大原因と処方箋
① タグのミスマッチ(Tag Mismatch)
- 症状: ジョブが永遠にアサインされない。
- 原因: `.gitlab-ci.yml` で指定した `tags`(例: `[aws-production]`)に一致するRunnerが存在しない、あるいはRunner側でそのタグが有効になっていない。
- 対処: GitLab管理画面の「Settings > CI/CD > Runners」を確認し、タグが正確に一致しているか、あるいは「Run untagged jobs」のチェック状態を確認する。
② ディスク容量・Dockerイメージの肥大化によるクラッシュ
- 症状: 「No space left on device」または突然のコンテナKill。
- 原因: キャッシュやビルドアーティファクト、古いDockerイメージがRunnerのホストを埋め尽くしている。
- 対処: 定期的なガベージコレクション(GC)の自動化。ホスト側で以下のクリーンアップcronを回すのは必須です。
# 未使用のDockerコンテナ、イメージ、ボリュームを強制削除
docker system prune -a –volumes –force
③ シェル実行環境(Shell Executor)の権限地獄
- 症状: `Permission denied` がファイル操作系で頻発する。
- 原因: Shell executorを使用している場合、ジョブはホスト上の `gitlab-runner` ユーザーで実行されます。プロジェクトディレクトリやデプロイ先フォルダのパーミッションが正しくないと即死します。
- 対処: ユーザー権限を適切に設計するか、極力コンテナベース(Docker/Kubernetes Executor)へ移行し、環境のクリーン性を担保する。
—
3. パーミッションエラーと環境変数の罠
チーム開発において最も事故りやすいのが、「誰の権限で動いているか」と「変数がどこでスコープされているか」のコントロールです。
パーミッションエラーの根絶:Deploy TokenとCI_JOB_TOKEN
リポジトリ間のクローンや、コンテナレジストリ(Container Registry)へのプッシュで弾かれる場合、大半はトークンの権限不足です。
- 修正の極意: プロジェクトをまたいだ操作や、外部サービスへの連携には、汎用的な個人のアクセストークンではなく、Deploy Tokens または CI/CD Job Tokens(`$CI_JOB_TOKEN`)を厳密にスコープを切って付与します。
- `.gitlab-ci.yml`でのレジストリログインのベストプラクティス:
docker-login:
script:
- echo “$CI_REGISTRY_PASSWORD” | docker login $CI_REGISTRY -u “$CI_JOB_USER” –password-stdin
環境変数の優先順位(Variable Precedence)を制す
GitLabには環境変数を設定できる場所が多すぎます(UI、YAML、Group、Instance)。この優先順位を理解していないと、「設定したのに値が反映されない」という怪奇現象に悩まされます。
1. Trigger variables / Scheduled pipeline variables (最高)
2. Project-level variables (UIで設定)
3. Group-level variables
4. Instance-level variables
5. `.gitlab-ci.yml`内の `variables` (最低)
> 💡 テックリードの教訓:
> 機密情報(DBのパスワードやAPIシークレットなど)は、絶対に `.gitlab-ci.yml` にハードコードしてはなりません。必ずプロジェクト/グループの「Settings > CI/CD > Variables」に登録し、「Masked」と「Protected」のチェックを入れ忘れないようにチームでルール化してください。
—
4. 開発スピードを爆上げする!GitLabの極意(ショートカット・プラグイン・共有化)
ここからは、日々の開発・運用スピードを極限まで高めるための「プロの隠し技」を伝授します。
⌨️ 開発スピードを高めるキーボードショートカット
ブラウザでのGitLab操作、マウスを使っていませんか? 以下のショートカットを体に叩き込んでください。
- `?`: キーボードショートカットヘルプの呼び出し(まずこれを覚える)
- `t`: ファイルファインダーの起動(リポジトリ内のファイルを瞬時に検索)
- `e`: コード表示画面で即座にWeb IDEを開く
- `r`: イシューやマージリクエスト(MR)のコメント欄で即座に返信モードへ
🔌 チーム全体の品質を底上げする神プラグイン・拡張
- GitLab Workflow (VS Code Extension):
VS Codeから離れるな。この公式拡張機能で、パイプラインのステータス確認、MRの作成・レビュー、さらにはCI/CD設定ファイルのオートコンプリートまで完結します。ローカルで書いたYAMLが正しいかをエディタ上で即座に検証できます。
🛠️ チーム開発で役立つ設定の共有化ルール(includeの活用)
モノレポや複数マイクロサービスを抱える組織で、全員がバラバラの `.gitlab-ci.yml` を書くのは悪夢です。共通のCI/CDテンプレートを別リポジトリで管理し、`include` を使って強制的に標準化します。
—
5. 実戦投入済み:美しく頑健な `.gitlab-ci.yml` ベストプラクティス構成例
最後に、上記すべての知見を詰め込んだ、プロダクション品質のモダンな `.gitlab-ci.yml` の設計サンプルを提示します。
=====================================================================
Production-Grade GitLab CI/CD Pipeline Configuration
=====================================================================
パイプラインのステージ定義
stages:
- lint 静的解析・構文チェック
- test 単体・結合テスト
- build ビルド・アーティファクト生成
- deploy デプロイ(本番・ステージング)
グローバル設定
default:
image: node:20-alpine # 標準のランタイム環境を指定
before_script:
- echo “=== Job started at $(date) ===”
- npm ci # キャッシュを活用した高速な依存関係インストール
ワークフロー制御(重複パイプラインの防止)
workflow:
rules:
- if: ‘$CI_PIPELINE_SOURCE == “merge_request_event”‘
- if: ‘$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH’
- if: ‘$CI_COMMIT_TAG’
— 1. Lint ステージ —
code-lint:
stage: lint
script:
- npm run lint
allow_failure: false # リントエラーは絶対に通さない
— 2. Test ステージ —
unit-test:
stage: test
script:
- npm run test:unit
coverage: /All files[^|]\|[^|]\s+([\d\.]+)/ # テストカバレッジの自動抽出
artifacts:
name: test-coverage
expire_in: 7 days
paths:
- coverage/
— 3. Build ステージ —
build-artifact:
stage: build
stage: build
script:
- npm run build
artifacts:
name: “app-build-$CI_COMMIT_SHORT_SHA”
expire_in: 1 days
paths:
- dist/
— 4. Deploy ステージ —
deploy-production:
stage: deploy
image: alpine:latest
environment:
name: production
url: https://example.com
rules:
- if: ‘$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH’ # デフォルトブランチ(main)へのマージ時のみ
when: manual # 本番デプロイは必ず人間の承認(Manual)を挟むのが鉄則
script:
- echo “Deploying to Production Server…”
- apk add –no-cache curl
- curl -X POST “$PROD_DEPLOY_WEBHOOK_URL”
—
まとめ:CI/CDは「育てる」インフラストラクチャ
パイプラインのエラーは、あなたの開発システムが発している「叫び声」です。それをただエラーログを読んで場当たり的に修正するのではなく、構造から見直し、Runnerの負荷を管理し、テンプレート化によって組織全体で知見を共有する。
このアプローチを徹底すれば、エラーに怯える日々から解放され、「ボタン一つで安全に、秒速でデプロイできる」理想郷に到達できます。
さあ、あなたのプロジェクトのパイプラインを、今すぐ緑色に染め直しましょう。