GitLabでパイプラインの分割を調べると、include
と一緒にtrigger
もよく出てくる。
どう使えるんだろう、と思って調べた時のメモ。
trigger
は複数のプロジェクトのパイプラインを実行するMulti-project piplinesと親子関係を持たせたパイプラインを実行するParent-child pipelinesというものそれぞれで利用できるが、ここでは親子パイプラインに絞ってメモを残している。
全体的にかなり荒い点はご了承を。
trigger
概要
trigger
を使うとパイプラインに親子関係を持たせることが出来る。
オフィシャルドキュメントはこのあたり。
親子関係と言ってもしっくり来ないが、ざっくり言うとパイプラインを独立・並行して複数本走らせることが出来る、と考えておけば良さそう。
自分の頭の中のイメージは以下のような感じ(汚いが。。。)。
A,B,C,Dはそれぞれパイプラインのstage相当と考えるとよい。
普通のパイプラインは.gitlab-ci.yml
に書ききってしまうが、include
を使うケースだと外に出すことが出来る(参考:昔の記事)。
trigger
は更に中で独立したパイプラインを作って.gitlab-ci.ymlのパイプラインとは独立して動かす感じになる。
オフィシャルドキュメントの親子パイプラインの説明では以下のようなものもあった。(DeepLでの翻訳)
マルチプロジェクトパイプラインと同様に、パイプラインは、同じプロジェクト内で、同時に実行される一連の子パイプラインをトリガーすることができます。
- 子パイプラインは、ステージシーケンスに従って各ジョブを実行しますが、親パイプラインの無関係なジョブの終了を待つことなく、自由にステージを進めることができます。
- 構成は、より小さな子パイプラインの構成に分割され、理解しやすくなっています。これにより、構成全体を理解するための認知的負荷が軽減されます。
- インポートは子パイプラインレベルで行われるため、衝突の可能性が低くなります。
- 各パイプラインは関連するステップのみを持ち、何が起こっているのか理解しやすくなります。
子パイプラインは、GitLabの他のCI/CD機能とうまく連動する。only: changes
は、特定のファイルが変更されたときだけパイプラインを起動します。これは、モノレポ構成などで有効です。
.gitlab-ci.yml
にある親パイプラインと子パイプラインは通常のパイプラインとして動作するので、トリガーに関する独自の動作や順序を持たせることができる。
これらはすべてinclude:
機能で動作するので、子パイプラインの設定を構成することができます。
上記には単一リポジトリに複数の種類のコードを入れる(例えばFrontendとBackendを同じリポジトリに突っ込む)、いわゆるモノレポ構成だと使えるとの記述がある。
これはBackendの修正でFrontend向けのCI/CDを動かしたくない、といったようなニーズがある場合、triggerでBackendとFrontendのパイプラインをそれぞれincludeし、only: changes
でパスを指定してFrontend向け修正契機で動いたのかBackend向け修正契機で動いたのかを検知して特定のパイプラインだけ動かす、といったことが出来ることを指している。
この辺りの詳細はモノレポでのCI/CDパイプラインの定義方法を考える(GitLab)というブログが非常に分かりやすいのでそちらを参考にするとよいと思う。(今回触るにあたり、非常に参考にさせていただいた)
trigger
の仕様
ここでの説明はオフィシャルの説明をざっくり意訳したものとなる。
triggerのジョブ内で使えるkeyword(同じ高さで書けるkeyword)には制限があり、以下の利用可能となっている。
allow_failure
extends
-
needs
(ただしneeds:project
は不可) -
only
とexcept
rules
stage
trigger
variables
-
when
(on_success
,on_failure
,always
のいずれかのみ).
またtriggerで実行できるパイプラインはtrigger:project
でGitLab内プロジェクトを指定するか、trigger:include
でinclude
を使って引っ張ってくる。
trigger
で使える他のkeywordとしてはtrigger:strategy
とtrigger:forward
がある。
trigger:strategy
通常、親のパイプラインは子の結果を待たずに終了するが、trigger
の中でstrategy: depend
を指定すると子のパイプラインの結果を待って終了する。
後ろの方で実際に使った結果を載せているので、詳細はそちらを参照してほしい。
trigger:forward
環境変数の引き継ぎを指定する。
デフォルトでは以下のようになっている。
- GitLab内の環境変数:子に引き継がれない
-
variables
の環境変数:子に引き継がれる
fowardの中でpipeline_variables: true
を指定すると、GitLab内で設定した環境変数を引き継ぐことが出来るようになる。また、yaml_variables: false
を指定するとvariables
で定義した環境変数が引き継ぎを拒否できる。
参考までに、オフィシャルの説明のサンプルを翻訳したものを以下に示す。
## MYVARはGitLabのCI/CDで設定する環境変数
variables: # 各ジョブのデフォルトの環境変数
VAR: value
# デフォルトの振る舞い
# - VARは子に渡る
# - MYVARは子に渡らない
child1:
trigger:
include: .child-pipeline.yml
# CI/CDの環境変数を転送する
# - VARは子に渡る
# - MYVARは子に渡る
child2:
trigger:
include: .child-pipeline.yml
forward:
pipeline_variables: true
# YAMLのvariablesを転送しない
# - VARは子に渡らない
# - MYVARは子に渡らない
child3:
trigger:
include: .child-pipeline.yml
forward:
yaml_variables: false
trigger
のお試し
検証用なので、Runner用のYAMLのみの以下の構成で検証する。
.
├── README.md
├── .gitlab-ci.yml
└── gitlab-ci
└── trigger.yml
ファイルは以下の様に作成した。
stages:
- build
build-job:
stage: build
trigger:
include: gitlab-ci/trigger.yml
include対象はこちら。stage
には親には定義されていないものを使っている。
stages:
- huga
hoge:
stage: huga
script:
- date
実行結果はこちら。UpstreamにProject名が表示され、Parent
というのが表示されていることが分かる。
なお、.gitlab-ci.ymlを以下のようにtrigger
を外すと、Lintでエラーが出る。
stages:
- build
include:
- gitlab-ci/trigger.yml
build-job:
stage: build
script:
- echo "dummy"
stage: huga
の定義が.gitlab-ci.yml
にないのでデフォルトのstageを使うよう促される。
余談だが、trigger
を利用するstageではscript
は利用できない。こちらも文法的にNGとなる。
Triggerの並列実行
triggerで子のパイプラインを生成する際、親のstageは子の完了を待たないため、並列実行のような形になる。それを確認する。
include
で呼び出すパイプラインを以下のように用意する。
stages:
- huga
hoge:
stage: huga
script:
- date
stages:
- huga
hoge:
stage: huga
script:
- sleep 30
- date
- exit 1
sleepさせているのは、最初のstage側でsleep30s.ymlをincludeして、後続のstageが追い抜くことを確認するために入れている。
また、それらを呼び出すように.gitlab-ci.yml
を以下のように修正する。
stages:
- build
build-job-1:
stage: build
trigger:
include:
- gitlab-ci/sleep30s.yml
build-job-2:
stage: build
trigger:
include:
- gitlab-ci/trigger.yml
待たないのであれば、build-job-1の子のパイプラインが30秒sleepしている間にbuild-job-2の実行が終わる。また、build-job-1自体も子のパイプラインを起動した時点で完了する。
これを確認するために、修正をpushしてパイプラインを実行した結果が以下となる。Passedにはなるが、子はまだ完了していない。
また、build-job-1, build-job-2で作成されたパイプラインのジョブのdate
の結果を見比べると以下のようになった。
- build-job-1:
Tue Jan 31 04:46:31 UTC 2023
- build-job-2:
Tue Jan 31 04:46:01 UTC 2023
build-job-2が追い抜いていることが分かる。
このように、
- 子の完了を待たずに親は終了する
- 子が失敗しても、親側では分からない
- 子の完了を待たずに次のstageが実行される
といったことがこの結果から言える。
また、include
するファイルにそれぞれhuga
というstageを用意しているが、通常同一stage名を含む複数ファイルをincludeするとどれかで上書きされてしまう。しかし、triggerでそれぞれincludeした場合は上書きされることがない。
stage名を意識しなくてもincludeが使える点はtriggerのメリットとも言えそう。
なお、子の完了を待って親に状態を反映するには前述のtrigger:strategy
を使えば良い。.gitlab-ci.ymlを以下のように修正する。
stages:
- build
build-job-1:
stage: build
trigger:
include:
- gitlab-ci/sleep30s.yml
strategy: depend
build-job-2:
stage: build
trigger:
include:
- gitlab-ci/trigger.yml
パイプラインの完了はsleep 30の処理が終わってからとなり、sleep30s.ymlの最後のexit 1で子のジョブが失敗するため、それを引き継いでエラーとなる。