Status | Authors | Coach | DRIs | Owning Stage | Created |
---|---|---|---|---|---|
proposed |
@furkanayhan
|
@ayufan
|
@jreporter
@cheryl.li
| devops verify | 2023-05-15 |
CIパイプライン処理の未来
要約
GitLab CIはGitLabで最も古く、最も複雑な機能の一つです。長年にわたり、そのYAML構文はかなり大きく複雑になってきました。長年にわたって構文を高度に安定させるために、私たちは主に既存のデザインとパターンの上に追加的な変更を加えてきました。私たちのユーザー・ベースは過去数年間で飛躍的に成長しました。それに伴い、彼らのユースケースやワークフローのカスタマイズをサポートする必要が出てきました。
長年にわたって大きな価値を提供する一方で、構文へのさまざまな追加的な変更によって、パイプライン処理ロジックに驚くべき動作が発生しました。いくつかのキーワードは多くの責任を蓄積し、キーワードのあいまいな重複が発見され、動作の微妙な違いが時間の経過とともに導入されました。現在の実装とYAMLの構文は新しい機能を実装することを難しくしています。
この設計書では、問題点を議論し、パイプライン処理のための新しいアーキテクチャを提案します。これらの問題の大半は「キーワード時のCIジョブの再構築」エピックで以前に議論されています。
目標
- パイプラインの処理をより理解しやすく、予測しやすく、一貫性のあるものにしたいと考えています。
- DAGとSTAGEの動作を統一したいと思います。STAGEはDAGと書くことができ、その逆も可能です。
- 手動ジョブのブロック動作を
allow_failure
キーワードから切り離したいです。 -
when
キーワードの責任を明確にしたいです。
非ゴール
変更を壊さないようにする方法については、今は触れません。
動機
問題点のリストが、この設計書の主な動機です。
問題1:when
キーワードの責任
現在、when
キーワードには多くの責任があります;
on_success
(デフォルト):ジョブを実行するのは、それ以前のステージのジョブが失敗しないか、allow_failure: true
。on_failure
:以前のステージのジョブが少なくとも1つ失敗した場合にのみジョブを実行します。以前のステージのジョブでallow_failure: true
、常に成功と見なされます。never
:以前のステージのジョブのステータスに関係なく、ジョブを実行しません。rules
セクションまたはworkflow: rules
でのみ使用できます。always
:前のステージのジョブのステータスに関係なくジョブを実行します。また、workflow:rules
.manual
:手動でトリガーされたときのみジョブを実行します。delayed
:指定した時間だけジョブの実行を遅らせます。
3つの質問に答えます;
- 実行するには何が必要ですか? =>
on_success
,on_failure
、always
-
manual
実行するには?delayed
- パイプラインに追加しますか?
never
その結果、例えば、when: on_failure
でmanual
ジョブを作成することはできません。これは、personaが失敗時にのみ利用可能で、手動で再生する必要があるジョブを作成したい場合に便利です。例えば、専用のページや専用の外部サービスに失敗を公開する場合などです。
問題2:allow_failure
キーワードの乱用
手動ジョブのブロッカー動作をallow_failure
キーワードで制御します。実は、このキーワードには他にも責務があります。_「ジョブが失敗したときにパイプラインの実行を継続するかどうかを決定する」_のです。
現在、手動ジョブは
- は、
allow_failure: true
(デフォルト)の場合はブロッカーではありません。 -
allow_failure: false
の場合はブロッカーです。
その結果、例えば、allow_failure: false
、ブロッカーではないmanual
ジョブを作成することはできません。
job1:
stage: test
when: manual
allow_failure: true # default
job2:
stage: deploy
現在は;
-
job1
はスキップされます。 -
job2
が実行されるのは、job1
がallow_failure: true
を持っているため無視されるからです。 -
job1
;- を実行すると、失敗した場合は “success with warning “と表示されます。
allow_failure
とともにrules
allow_failure
を使うと、rules
がよりわかりにくくなります。
allow_failure
のデフォルトの動作は、when: manual
.when: manual
true を指定すると true に変わります。when: manual
ただしwhen: manual
、rules
とwhen: manual
併用when: manual
した場合、allow_failure
のデフォルトはfalse
になります。
allow_failure
のデフォルト値は次のとおりです:
true
手動ジョブの場合。false
rules
内部でwhen: manual
を使用するジョブの場合。false
それ以外の場合は
例えば
job1:
script: ls
when: manual
job2:
script: ls
rules:
- if: $ALWAYS_TRUE
when: manual
job1
とjob2
では挙動が異なります;
-
job1
はデフォルトでallow_failure: true
、ブロッカーではありません。 -
job2
はブロッカーですrules: when: manual
はデフォルトでallow_failure: true
を返しません。
問題3:DAG/ニーズで異なる動作
DAGとSTAGEの主な動作の違いは、「スキップ」と「無視」の状態についてです。
背景情報
- スキップ
- ジョブが
when: on_success
、その前のステータスが失敗であった場合、そのジョブはスキップされます。 - ジョブが
when: on_failure
、その前のステータスが「失敗」でない場合、そのジョブはスキップされます。
- ジョブが
- 無視されます:
- ジョブが
allow_failure: true
でwhen: manual
になると無視されます。
- ジョブが
問題があります:
skipped
とignored
の状態は、STAGE 処理では成功とみなされますが、DAG 処理では成功とみなされません。
問題3.1.手動ジョブで無視されたステータスの処理
例 1:
build:
stage: build
script: exit 0
when: manual
allow_failure: true # by default
test:
stage: test
script: exit 0
needs: [build]
-
build
が無視(スキップ)されるのは、when: manual
がallow_failure: true
であるためです。 -
test
がスキップされるのは、「無視」がDAGの処理で成功した状態ではないからです。
例 2:
build:
stage: build
script: exit 0
when: manual
allow_failure: true # by default
test:
stage: test
script: exit 0
-
build
が無視(スキップ)されるのは、when: manual
がallow_failure: true
であるためです。 -
test2
が実行され、成功します。
問題 3.2.when:on_failureによるステータスのスキップ処理
例 1:
build_job:
stage: build
script: exit 1
test_job:
stage: test
script: exit 0
rollback_job:
stage: deploy
needs: [build_job, test_job]
script: exit 0
when: on_failure
-
build_job
が実行され、失敗します。 -
test_job
はスキップされます。 -
rollback_job
がwhen: on_failure
で、失敗したジョブがあっても、needs
リストには「スキップされた」ジョブがあるため、スキップされます。
例 2:
build_job:
stage: build
script: exit 1
test_job:
stage: test
script: exit 0
rollback_job:
stage: deploy
script: exit 0
when: on_failure
-
build_job
が実行され、失敗します。 -
test_job
はスキップされます。 -
rollback_job
の前に失敗したジョブがあるために実行されます。
問題4:スキップされた状態と無視された状態
問題3を解決し、DAGとステージで「スキップされた状態」と「無視された状態」に違いがないと仮定しましょう。一般的にどのように振る舞うべきでしょうか?成功するのかしないのか?スキップ」と「無視」は異なるべきでしょうか?いくつかの例を見てみましょう;
例 4.1.手動ジョブでの無視ステータス
build:
stage: build
script: exit 0
when: manual
allow_failure: true # by default
test:
stage: test
script: exit 0
-
build
は “manual” 状態ですが、パイプライン処理では “skip” (無視) とみなされます。 -
test
は、”skipped” が成功した状態であるため、実行されます。
あるいは
build1:
stage: build
script: exit 0
when: manual
allow_failure: true # by default
build2:
stage: build
script: exit 0
test:
stage: test
script: exit 0
-
build1
は “manual” 状態ですが、パイプライン処理では “skip” (無視) とみなされます。 -
build2
が実行され、成功します。 -
test
success “+”skipped “が成功状態なので、”run “が実行されます。
例4.2.when: on_failureによるスキップ状態
build:
stage: build
script: exit 0
when: on_failure
test:
stage: test
script: exit 0
-
build
は、when: on_failure
、以前のステータスが “failed “でないため、スキップされます。 -
test
は、”skipped” が成功した状態であるため、実行されます。
あるいは
build1:
stage: build
script: exit 0
when: on_failure
build2:
stage: build
script: exit 0
test:
stage: test
script: exit 0
-
build1
は、when: on_failure
、以前のステータスが “failed “でないため、スキップされます。 -
build2
が実行され、成功します。 -
test
success “+”skipped “が成功状態なので、”run “が実行されます。
問題5:dependencies
キーワード
dependencies
キーワードは、アーティファクトをフェッチするジョブのリストを定義するために使用されます。これはneeds
キーワードと共有されます。さらに、これらは同じジョブで一緒に使うことができます。すべての可能なシナリオを議論する必要はないかもしれませんが、混乱を示すにはこの例で十分です;
test2:
script: exit 0
dependencies: [test1]
needs:
- job: test1
artifacts: false
情報1: キャンセルされたジョブ
キャンセルされたジョブと失敗したジョブは同じですか?両者には多くの違いがあります。しかし、1つだけ共通点があります。
まずその違いを定義しましょう;
- キャンセルされたジョブ;
- 終了したジョブではありません。
- Canceled はユーザーが要求したジョブの中断です。ジョブを中断するか、パイプライン処理をできるだけ早く停止することを意図しています。
- 結果はわからず、アーティファクトなどもありません。
- 実行されることはないので、
after_script
。 - 最終的な状態は “canceled “なので、それ以降のジョブは実行できません。
-
when: on_canceled
はありません。 -
when: always
もありません。
-
- 失敗したジョブです;
- ジョブ内容の実行に対するCIシステムの機械的な応答です。何らかの理由で実行に失敗したことを示します。
- それは成功に対するシステムの答えと同じです。何かが失敗したという事実は相対的なものであり、CI実行の望ましい結果かもしれません。
- 私たちは結果を知っていますし、アーティファクトもありえます。
-
after_script
が実行されます。 - 最終的な状態は “failed “であるため、後続のジョブは
when
の値に応じて実行することができます。-
when: on_failure
とwhen: always
が実行されます。
-
1つだけ似ているのは、「失敗させる」ことができるということです。
build:
stage: build
script: sleep 10
allow_failure: true
test:
stage: test
script: exit 0
when: on_success
-
build
、canceled
、test
。 -
build
、failed
、test
。
場合によっては、failed
の代わりにcanceled
を使うというアイデアもあります。
別の側面もあります。failure_reason
例えば、名前空間がコンピュートクレジット(CI分)を使い果たした場合や、制限を超えた場合などです。failed
の状態でジョブを落とすと、ユーザーにfailure_reason
を伝えることができ、より良いフィードバックが得られるので便利です。様々な理由でジョブをキャンセルする場合、それを示す方法がありません。パイプラインの実行中にユーザーがコンピュートクレジットを使い果たしたり、パイプラインが他のパイプラインによって自動キャンセルされたり、その他の理由でジョブをキャンセルします。もし、failure_reason
の代わりにstop_reason
があれば、キャンセルされたジョブにも失敗したジョブにも使用することができます。また、canceled
のステータスをより適切に使用することもできます。
情報2: 空の状態
最近、のドキュメントを[更新](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117856)し、when
キーワード を明確にしました;
on_success
:ジョブの実行は、それ以前のステージのジョブが失敗しないか、allow_failure: true
。on_failure
:以前のステージのジョブが少なくとも1つ失敗した場合にのみジョブを実行します。
例えば
test1:
when: on_success
script: exit 0
# needs: [] would lead to the same result
test2:
when: on_failure
script: exit 0
# needs: [] would lead to the same result
-
test1
が実行されるのは、前のステージで失敗したジョブがないからです。 -
test2
前のステージで失敗したジョブがないため、実行されません。
on_success
は「何も失敗しなかった」という意味であり、すべてが成功したという意味ではありません。on_failure
も同様で、すべてが失敗したという意味ではなく、”何かが失敗した “という意味です。このセマンティックは、パイプラインが成功し、これが幸せな道であることを期待しています。パイプラインが失敗するという意味ではありません。
技術的な期待
すべての提案または将来の決定は、これらの目標に従わなければなりません;
-
allow_failure
キーワードは、失敗したジョブを “警告付き成功 “としてマークすることにのみ責任を負わなければなりません。- なぜかというと、手動ジョブがブロッカーかどうかを判断するような、別の責任を持つべきではありません。
- どのように:手動ジョブのブロッカー動作を制御するために、別のキーワードが導入されます。
-
allow_failure
では、キャンセルされたジョブは “success with warning “としてマークされません。- なぜかというと、”キャンセル “は “失敗 “とは異なる状態だからです。
- どのように:
allow_failure: true
でキャンセルされたジョブは “success with warning” としてマークされません。
-
when
キーワードは「実行に必要なものは何か?そして、それはジョブが実行されるべきか否かを決定するための唯一の真実の情報源でなければなりません。 -
when
キーワードは、ジョブがパイプラインに追加されるかどうかを制御してはなりません。- 理由: それはキーワードの責任ではありません。
- どのように:ジョブがパイプラインに追加されるかどうかを制御するために、別のキーワードが導入されます。
- skipped “と “ignored “の状態を再考する必要があります。
- TODO: もっと議論する必要があります。
- ジョブが “自動”、”手動”、または “遅延 “ジョブであるかどうかを指定するために、新しいキーワード構造を導入する必要があります。
- 理由:これは
when
キーワードの責任ではありません。 - どのように:ジョブの動作を制御するために新しいキーワードが導入されます。
- 理由:これは
-
needs
キーワードはジョブの順序のみを制御しなければなりません。ジョブの動作を制御したり、ジョブを実行するかどうかを決定したりするために使用してはなりません。DAG と STAGE の動作は同じでなければなりません。- 理由: 異なる動作になり、ユーザーを混乱させます。
- How:
needs
キーワードは、ステージのように以前のジョブのみを定義します。
-
needs
とdependencies
キーワードは同じジョブ内で一緒に使用してはいけません。- 理由:混乱を招きます。
- How:
needs
とdependencies
キーワードは相互に排他的になります。
提案
該当なし
デザインおよび実施内容
該当なし