はじめに
GitLab CIでは長時間pending状態のジョブは強制的にfail扱いになる場合があります。ここではその原因と現時点(GitLab 12.2時点)での対策について書きます。
現象
発生する現象としては、下の画面のように長時間pending状態のジョブが実行されることなくfailedとなります。
"Check your timeout limits"などと書かれていますが、実のところジョブのtimeout値は全く関係ありません。
この現象はもう何年も前から何度もIssueが立っており、最新のIssueはこちらです。
https://gitlab.com/gitlab-org/gitlab-ce/issues/37122
当初は「単にtimeoutを増やせばいいんじゃない?」といってあしらわれていたようですが、現在では原因もはっきりしていて、修正する方向にはなっているようです。ただマイルストーンは12.5に設定されているものの、あまり作業は進んでいない感じなのでまだまだ時間がかかるかもしれません。
原因
現在のGitLab CIではジョブのpending時間の最大値は1時間で固定になっており、これを超えたジョブはstuck(つまりこのジョブは実行不能)と判定されます。さらにstuck状態のジョブを除去するためのタスクが定期的(デフォルトでは1時間に1回)に実行されており、このタスクがstuck状態のジョブをfailedにしています。
これによって1~2時間pendingするとfailedになる、というわけです。
普通のCI環境(例えばgitlab.comなど)では、runnerの数が十分多ければそれほど長時間pendingすることはないだろう、という想定だと思われます。しかしオンプレ環境で特殊なリソース(例えば特定のDBや専用ハードウェア、高価なライセンスなど)を扱うCIでは、リソース数に応じた少数のrunnerに対し、ジョブをたくさんため込むような運用があり得ます。
そのあたりの環境の違いが、GitLabの人になかなか伝わっていなかったように見えます。
対策
現時点で取り得る対策はいくつかあります。それぞれ一長一短あるので状況に合わせて選んでください。
タスクの時刻変更
stuck状態のジョブを除去するタスクの実行タイミングは設定から変更できます。もし問題のジョブが実行されない時間帯があらかじめわかっているのであれば、その時間を外してやればいいです。
例えば午前0時開始のリグレッションテストなら、その直前に指定するといいでしょう。
gitlab.rbに以下のような項目があるので、コメントを外して時刻を設定します。
# gitlab_rails['stuck_ci_jobs_worker_cron'] = "0 0 * * *"
↓
gitlab_rails['stuck_ci_jobs_worker_cron'] = "0 15 * * *"
ちなみにここの時刻はUTCなので、15:00に設定するとJSTで午前0時となります。
タスクの無効化
もし特定の時間を外すことが出来ないなら、完全に無効化する方法もあります。この場合、CIの設定ミスなどで本当にstuckしているジョブは永遠に残ることになります。(もしかしたら再起動などのタイミングで消えるかもしれませんがよく分かりません)
Admin Area -> Monitoring -> Background Jobs -> Cronで定期実行タスクの一覧が出てくるので、stuck_ci_jobs_workerの「無効にする」ボタンで無効化できます。
また、前述の時刻変更が効いているかどうかはこの画面で確認することができます。
stuckまでの時間を変える
今の1時間というリミットはソースコードに書かれているので、それを直接変更する方法です。
この場合、GitLabのアップデートで元に戻ってしまうので、その都度修正し直す必要があります。
BUILD_PENDING_STUCK_TIMEOUT = 1.hour
↓
BUILD_PENDING_STUCK_TIMEOUT = 12.hours
これでstuck判定までの時間が12時間になります。