CI/CDのトラブルシューティング

GitLab はパイプラインのトラブルシューティングを簡単にするためのツールをいくつか提供しています。

このガイドでは、よくあるイシューと考えられる解決策も挙げています。

構文の確認

問題の初期の原因は、構文が正しくないことです。パイプラインはyaml invalid バッジを表示し、構文やフォーマットの問題が見つかった場合は実行を開始しません。

パイプラインエディタで.gitlab-ci.yml を編集します。

パイプラインエディタは、(シングルファイルエディタやWeb IDEよりも)推奨される編集環境です。これには以下が含まれます:

  • 使用可能なキーワードのみを使用するようにするコード補完の提案。
  • 自動シンタックスハイライトと検証
  • CI/CD 設定を視覚化し、.gitlab-ci.yml ファイルをグラフィカルに表示します。

.gitlab-ci.yml を内部で編集します。

パイプライン設定をローカルで編集したい場合は、エディタでGitLab CI/CDスキーマを使って基本的な構文の問題を確認することができます。Schemastore をサポートしているエディタは、デフォルトで GitLab CI/CD スキーマを使います。

スキーマに直接リンクする必要がある場合は、以下にあります:

https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/editor/schema/ci.json

CI/CDスキーマがカバーするカスタムタグの全リストを見るには、スキーマの最新版をチェックしてください。

CI Lintツールで構文を確認

CI LintツールはCI/CD設定ファイルの構文が正しいことを確認する簡単な方法です。基本的な構文を確認するために、完全な.gitlab-ci.yml ファイルまたは個々のジョブの設定を貼り付けてください。

プロジェクトに.gitlab-ci.yml ファイルが存在する場合、CI Lint ツールを使って完全なパイプラインの作成をシミュレートすることもできます。このツールは設定構文をより深く検証します。

変数の検証

CI/CDのトラブルシューティングで重要なのは、パイプラインに存在する変数とその値を確認することです。パイプラインの設定の多くは変数に依存しており、変数の検証は問題の原因を見つける最も早い方法の1つです。

問題のある各ジョブで利用可能な変数の完全なリストをエクスポートします。期待する変数が存在するかどうか、そしてその値が期待するものであるかどうかをチェックします。

GitLab CI/CD ドキュメント

complete.gitlab-ci.yml reference には、パイプラインの設定に使える全てのキーワードの完全なリストが含まれています。

また、多くのパイプライン設定例や テンプレートを見ることもできます。

パイプラインタイプのドキュメント

ブランチパイプラインは最も基本的なタイプです。他のパイプラインタイプには、それぞれ詳細な使用ガイドがあります:

CI/CD 機能のトラブルシューティングガイド

CI/CDのいくつかの機能と関連するトピックについて、トラブルシューティングガイドを用意しています:

よくあるCI/CDのイシュー

よくあるパイプラインの問題の多くは、rules またはonly/except 設定の動作を分析することで解決できます。挙動が異なるので、同じパイプラインでこの2つの設定を使うべきではありません。動作が異なるため、同じパイプラインでこの2つの設定を使うべきではありません。このように動作が異なるパイプラインの動作を予測するのは困難です。

rulesonly/except 設定がCI_PIPELINE_SOURCECI_MERGE_REQUEST_IDのような定義済みの変数を使用している場合、最初のトラブルシューティングとしてそれらを検証する必要があります。

ジョブやパイプラインが期待通りに実行されない

ジョブがパイプラインに追加されるかどうかはrules またはonly/except キーワードによって決まります。パイプラインが実行されているにもかかわらず、ジョブがパイプラインに追加されない場合、通常はrules またはonly/except の設定に問題があることが原因です。

エラーメッセージもなく、パイプラインがまったく実行されないようであれば、rulesonly/except の設定、あるいはworkflow: rules キーワードが原因かもしれません。

only/except rules キーワードに only/except変換する場合は、only/except rules 設定の詳細 を注意深く確認する必要が only/exceptあります。とrulesonly/except 動作は異なって only/exceptおり、両者間のマイグレーション時に予期しない動作を引き起こす可能性があります。

rules](jobs/job_control.md#common-if-clauses-for-rules) の[共通のif 節は、期待通りの動作をするルールを書く方法の例として非常に役に立ちます。

2つのパイプラインを同時に実行

マージリクエストが開かれているブランチにコミットをプッシュする際に、2つのパイプラインを同時に実行することができます。通常、一方のパイプラインはマージリクエストパイプラインで、もう一方はブランチパイプラインです。

この状況は通常rules の設定に起因するもので、パイプラインの重複を防ぐ方法がいくつかあります。

ジョブがパイプラインにない

GitLab はジョブがパイプラインに追加されたかどうかを、ジョブに対して定義されたonly/exceptrules に基づいて判断します。実行されなかったのであれば、おそらく期待通りに評価されていないのでしょう。

パイプラインが実行されないか、間違ったタイプのパイプラインが実行された場合

パイプラインが実行される前に、GitLabは設定内のすべてのジョブを評価し、利用可能なすべてのパイプラインタイプに追加しようとします。評価の最後にジョブが追加されていなければ、パイプラインは実行されません。

パイプラインが実行されなかった場合、すべてのジョブにrulesonly/except があり、パイプラインへの追加がブロックされている可能性があります。

間違ったパイプラインタイプが実行された場合、rules またはonly/except の設定をチェックし、ジョブが正しいパイプラインタイプに追加されていることを確認する必要があります。例えば、マージリクエストパイプラインが実行されなかった場合、ジョブはブランチパイプラインに追加された可能性があります。

また、workflow: rules の設定がパイプラインをブロックしているか、間違ったパイプラインタイプを許可している可能性もあります。

多くのジョブを持つパイプラインが起動に失敗します。

インスタンスで定義されたCI/CD の制限を超える数のジョブを持つパイプラインが起動に失敗します。

パイプラインのジョブ数を減らすには、親子パイプラインを使用して.gitlab-ci.yml 設定を分割します。

ジョブの予期せぬ実行

ジョブがパイプラインに予期せず追加される一般的な理由は、changes 特定の場合にキーワードが常にtrueと評価される changesためです。changes 例えば changes、スケジュールされたパイプラインやタグのパイプラインなど、特定のパイプラインタイプでは常に真になります。

changes キーワードは、only/except またはrulesと組み合わせて使用します。)ジョブがブランチパイプラインまたはマージリクエストパイプラインにのみ追加されるように、rules またはonly/except 設定と一緒にchanges を使用することをお勧めします。

“fatal: reference is not a tree” エラー

GitLab 12.4で導入されました

以前はブランチをリモートリポジトリに強制的にプッシュすると、予期せぬパイプラインの障害が発生することがありました。次のようなワークフローで、問題を説明します。

  1. ユーザーがexample という名前の機能ブランチを作成し、それをリモートリポジトリにプッシュしたとします。
  2. 新しいパイプラインがexample ブランチで実行を開始します。
  3. ユーザーがexample ブランチを最新のデフォルトブランチにリベースし、リモートリポジトリに強制プッシュします。
  4. 新しいパイプラインが再びexample ブランチで実行を開始しますが、以前のパイプライン(2)はfatal: reference is not a tree: エラーのために失敗します。

これは、前のパイプラインがexample ブランチからcheckout-SHA(パイプラインレコードに関連付けられている)を見つけることができず、コミット履歴がすでにforce-pushによって上書きされているために発生します。同様に、マージ結果のパイプラインも 同じ理由で断続的に失敗することがあります。

GitLab 12.4では、パイプラインの参照を排他的に保持することで、この動作を改善しました。

  1. example という名前の機能ブランチにパイプラインが作成されます。
  2. refs/pipelines/<pipeline-id> に永続的なパイプライン参照が作成され、関連するパイプラインレコードのチェックアウト-SHAが保持されます。この永続的なrefは、example ブランチのコミット履歴がforce-pushによって上書きされても、パイプラインの実行中そのままの状態を保ちます。
  3. ランナーは永続的なパイプラインrefをフェッチし、checkout-SHAからソースコードを取得します。
  4. パイプラインが終了すると、永続的な ref はバックグラウンドプロセスでクリーンアップされます。

マージリクエストパイプラインメッセージ

マージリクエストパイプラインウィジェットは、マージリクエストのパイプラインステータスに関する情報を表示します。マージステータスウィジェットの上に表示されます。

「自動的にマージする能力をチェックしています。

マージリクエストがChecking ability to merge automatically メッセージで立ち往生する既知のイシューがあります。

マージリクエストにこのメッセージが表示され、数分経っても消えない場合は、以下の回避策をお試しください:

  • マージリクエストページを更新してください。
  • マージリクエストを閉じて再度開きます。
  • /rebase クイックアクションでマージリクエストを再ベースします。
  • マージリクエストがマージ可能であることをすでに確認している場合、/merge クイックアクションでマージすることができます。

「パイプラインステータスの確認」メッセージ

このメッセージはマージリクエストが最新のコミットに関連付けられているパイプラインを持っていない場合に表示されます。これは次のような理由が考えられます:

  • GitLab はまだパイプラインの作成を完了していません。
  • 外部のCIサービスを使っていて、GitLabがそのサービスからまだ返答を受け取っていません。
  • プロジェクトで CI/CD パイプラインを使っていない場合。
  • プロジェクトで CI/CD パイプラインを使用しているが、設定によってマージリクエストのソースブランチ上でパイプラインが実行できない状態になっています。
  • 最新のパイプラインが削除されました (これは既知のイシューです)。
  • マージリクエストのソースブランチは非公開フォークにあります。

パイプラインが作成されると、メッセージはパイプラインのステータスで更新されます。

マージリクエストステータスメッセージ

マージリクエストステータスウィジェットは表示します:

  • マージリクエストがマージ可能な場合。マージリクエストがマージできない場合、その理由が表示されます。
  • パイプラインが完了している場合はマージし、パイプラインが実行中の場合は自動マージに設定します。

「CI/CD パイプラインはマージする前に実行し、成功する必要があります。

このメッセージは、プロジェクトでパイプラインが成功する必要がある設定が有効で、パイプラインがまだ正常に実行されていない場合に表示されます。パイプラインがまだ作成されていない場合や、外部の CI サービスを待っている場合にも適用されます。プロジェクトでパイプラインを使用しない場合は、Pipelines must succeedを無効にしてマージリクエストを受け付けるようにしてください。

“マージがブロックされました: パイプラインが成功しなければなりません。失敗を修正する新しいコミットをプッシュしてください” メッセージ

このメッセージはマージリクエストパイプラインマージ結果パイプラインマージトレインパイプラインが失敗した場合、もしくはキャンセルされた場合に表示されます。基本ブランチパイプラインが失敗した場合は発生しません。

マージ・リクエスト・パイプラインまたはマージ結果パイプラインがキャンセルされた、または失敗した場合、次のことができます:

  • マージリクエストのパイプラインタブでパイプラインの実行を選択して、パイプライン全体を再実行します。
  • 失敗したジョブのみを再試行します。パイプライン全体を再実行する場合は、この操作は必要ありません。
  • 失敗を修正するために新しいコミットをプッシュします。

マージトレインパイプラインが失敗した場合は、次のようにします:

  • 失敗を確認し、/merge クイックアクション を使用して、マージリクエストを直ちにトレインに再度追加できるかどうかを判断します。
  • マージリクエストのパイプラインタブでパイプラインの実行を選択してパイプライン全体を再実行し、マ ージリクエストを列車に再度追加します。
  • コミットをプッシュして失敗を修正し、再度マージリクエストを列車に追加します。

マージトレインパイプラインが、マージリクエストがマージされる前に、失敗せずにキャンセルされた場合:

  • 再度列車に追加してください。

マージリクエストルールウィジェットは、スキャン結果ポリシーが無効または重複していることを示します。

GitLab 15.0 以降のセルフマネージメントでは、プロジェクトがグループからエクスポートされ、別のグループにインポートされ、スキャン結果のポリシールールがあったことが最も考えられる原因です。これらのルールはエクスポートされたプロジェクトとは別のプロジェクトに保存されています。その結果、プロジェクトには、インポートされたプロジェクトのグループには存在しないエンティティを参照するポリシールールが含まれています。その結果、ポリシー ルールが無効、重複、またはその両方になります。

GitLabインスタンスから無効なスキャン結果のポリシールールをすべて削除するには、管理者がRailsコンソールで以下のスクリプトを実行します。

Project.joins(:approval_rules).where(approval_rules: { report_type: %i[scan_finding license_scanning] }).where.not(approval_rules: { security_orchestration_policy_configuration_id: nil }).find_in_batches.flat_map do |batch|
  batch.map do |project|
    # Get projects and their configuration_ids for applicable project rules
    [project, project.approval_rules.where(report_type: %i[scan_finding license_scanning]).pluck(:security_orchestration_policy_configuration_id).uniq]
  end.uniq.map do |project, configuration_ids| # We take only unique combinations of project + configuration_ids
    # If we find more configurations than what is available for the project, we take records with the extra configurations
    [project, configuration_ids - project.all_security_orchestration_policy_configurations.pluck(:id)]
  end.select { |_project, configuration_ids| configuration_ids.any? }
end.each do |project, configuration_ids|
  # For each found pair project + ghost configuration, we remove these rules for a given project
  Security::OrchestrationPolicyConfiguration.where(id: configuration_ids).each do |configuration|
    configuration.delete_scan_finding_rules_for_project(project.id)
  end
  # Ensure we sync any potential rules from new group's policy
  Security::ScanResultPolicies::SyncProjectWorker.perform_async(project.id)
end

プロジェクトgroup/project が見つからないか、アクセスが拒否されました。

このメッセージは、設定がinclude 、以下のいずれかで追加された場合に表示されます:

  • 設定が見つからないプロジェクトを参照しています。
  • パイプラインを実行しているユーザーは、含まれているプロジェクトにアクセスできません。

これを解決するには、以下を確認します:

  • プロジェクトのパスはmy-group/my-project 形式で、リポジトリ内のフォルダを含んでいません。
  • パイプラインを実行するユーザーは、インクルードされたファイルを含むプロジェクトのメンバーです。ユーザーは、同じプロジェクトで CI/CD ジョブを実行する権限も持っている必要があります。

「パースされた YAML が大きすぎます。

このメッセージは YAML 設定が大きすぎる、もしくはネストが深すぎる場合に表示されます。インクルードの数が多く、全体で数千行の YAML ファイルはこのメモリ制限にぶつかる可能性が高くなります。たとえば、200kbのYAMLファイルはデフォルトのメモリ制限にぶつかる可能性が高いです。

設定サイズを小さくするには、次のようにします:

  • パイプラインエディタの[Full configuration]タブで、展開されたCI/CD設定の長さを確認します。削除または簡略化できる重複設定を探します。
  • 長い、または繰り返されるscript セクションをプロジェクト内のスタンドアロンスクリプトに移動します。
  • 親パイプラインと子パイプラインを使用して、一部のジョブを独立した子パイプラインのジョブに移動します。

セルフマネージドインスタンスでは、サイズ制限を増やすことができます。

.gitlab-ci.yml ファイルの編集時に 500 エラーが発生します。

ウェブエディタで .gitlab-ci.yml ファイルを編集する際に、インクルードされた設定ファイルのループにより 500 エラーが発生することがあります。

CI/CD ジョブが再実行されたときに新しい設定を使用しません。

パイプラインの設定はパイプラインが作成されたときだけ取得されます。ジョブを再実行すると、毎回同じ設定が使われます。include で追加された個別のファイルを含め、設定ファイルを更新した場合、新しい設定を使用するには新しいパイプラインを開始する必要があります。

他のプロジェクトからイメージをプルできない

GitLab16.3では、CI_JOB_TOKENの設定をLimit access_to_this project変更して、このプロジェクトへのアクセスを許可します。

Runner が非公開プロジェクトからイメージをプルしようとすると、以下のエラーでジョブが失敗することがありました:

WARNING: Failed to pull image with policy "always": Error response from daemon: pull access denied for registry.example.com/path/to/project, repository does not exist or may require 'docker login': denied: requested access to the resource is denied

このエラーは以下の両方が真である場合に発生します:

  • 画像をホストしている非公開プロジェクトで、この_プロジェクトへの_アクセスを制限するオプションが有効になっている場合。
  • 画像を取得しようとしているジョブが、非公開プロジェクトの許可リストにないプロジェクトで実行されています。

推奨される解決策は、非公開プロジェクトのジョブトークンスコープのallowlistにプロジェクトを追加することです。

パイプラインの警告

パイプライン設定の警告は、以下の場合に表示されます:

“ジョブは1つのアクションに対して複数のパイプラインを実行することができます” 警告

when 節でif 節を指定せずにrules を使うと、複数のパイプラインが実行される可能性があります。通常、これはマージリクエストが開かれているブランチにコミットをプッシュしたときに発生します。

パイプラインの重複を防ぐにはworkflow: rules を使用するか、実行可能なパイプラインを制御するルールを書き換えてください。

resource_group を使ったジョブがスタックした場合のコンソールでの回避策

# find resource group by name
resource_group = Project.find_by_full_path('...').resource_groups.find_by(key: 'the-group-name')
busy_resources = resource_group.resources.where('build_id IS NOT NULL')

# identify which builds are occupying the resource
# (I think it should be 1 as of today)
busy_resources.pluck(:build_id)

# it's good to check why this build is holding the resource.
# Is it stuck? Has it been forcefully dropped by the system?
# free up busy resources
busy_resources.update_all(build_id: nil)

ジョブログの更新が遅い

実行中のジョブのログページにアクセスすると、ログが更新されるまでに最大60秒の遅延が発生することがあります。デフォルトの更新時間は 60 秒ですが、ログが UI で表示された後、次のようなログの更新が 3 秒ごとに発生するはずです。

ディザスタリカバリ

継続的なダウンタイム中にデータベースへのストレスを軽減するために、アプリケーションの重要だが計算コストのかかる部分を無効にすることができます。

共有ランナーでのフェアスケジューリングの無効化

大量のジョブのバックログをクリアする場合、ci_queueing_disaster_recovery_disable_fair_scheduling 機能フラグを一時的に有効にすることができます。このフラグは共有 Runner のフェアスケジューリングを無効にし、jobs/request エンドポイントのシステムリソース使用量を削減します。

有効にすると、ジョブは多くのプロジェクトにまたがってバランスされるのではなく、システムに投入された順番に処理されます。

コンピュートクォータ強制の無効化

共有Runner上の計算クォータの実施を無効にするには、ci_queueing_disaster_recovery_disable_quota 機能フラグを一時的に有効にします。このフラグは、jobs/request エンドポイントのシステムリソース使用量を削減します。

この機能を有効にすると、直近の 1 時間に作成されたジョブは、クォータが不足しているプロジェクトで実行することができます。それ以前のジョブは、定期的なバックグラウンドワーカー (StuckCiJobsWorker) によってすでにキャンセルされています。

CI/CD トラブルシューティング Rails コンソールコマンド

以下のコマンドはrailsコンソールで実行します。

caution
データを直接変更するようなコマンドは、正しく実行しなかったり適切な条件下で実行しなかったりすると、損害を与える可能性があります。念のため、インスタンスのバックアップを復元できる状態にしてテスト環境で実行することを強くお勧めします。

保留中のパイプラインのキャンセル

project = Project.find_by_full_path('<project_path>')
Ci::Pipeline.where(project_id: project.id).where(status: 'pending').count
Ci::Pipeline.where(project_id: project.id).where(status: 'pending').each {|p| p.cancel if p.stuck?}
Ci::Pipeline.where(project_id: project.id).where(status: 'pending').count

マージリクエストのインテグレーションをお試しください。

project = Project.find_by_full_path('<project_path>')
mr = project.merge_requests.find_by(iid: <merge_request_iid>)
mr.project.try(:ci_integration)

.gitlab-ci.yml ファイルの検証

project = Project.find_by_full_path('<project_path>')
content = p.repository.gitlab_ci_yml_for(project.repository.root_ref_sha)
Gitlab::Ci::Lint.new(project: project,  current_user: User.first).validate(content)

既存のプロジェクトで AutoDevOps を無効にします。

Project.all.each do |p|
  p.auto_devops_attributes={"enabled"=>"0"}
  p.save
end

ランナー登録トークンの取得

Gitlab::CurrentSettings.current_application_settings.runners_registration_token

ランナー登録トークンの発行

appSetting = Gitlab::CurrentSettings.current_application_settings
appSetting.set_runners_registration_token('<new-runners-registration-token>')
appSetting.save!

パイプラインスケジュールを手動で実行

Railsコンソールからパイプラインスケジュールを手動で実行することで、通常は表示されないエラーを明らかにすることができます。

# schedule_id can be obtained from Edit Pipeline Schedule page
schedule = Ci::PipelineSchedule.find_by(id: <schedule_id>)

# Select the user that you want to run the schedule for
user = User.find_by_username('<username>')

# Run the schedule
ps = Ci::CreatePipelineService.new(schedule.project, user, ref: schedule.ref).execute!(:schedule, ignore_skip_ci: true, save_on_errors: false, schedule: schedule)

ヘルプの入手方法

パイプラインのイシューを解決できない場合は、以下のヘルプをご利用ください: