GitLab CIには外部のYAMLファイルを取り込む方法としてincludeというキーワードが用意されている。これを使うとどのような感じで.gitlab-ci.ymlが分割出来るかを調べた時のメモ。
includeとは
.gitlab-ci.ymlとは別にパイプラインを記述しておいて、それを引っ張ってくる機能である。これにより.gitlab-ci.ymlを分割できるため、以下の効果が期待できる。
- 可読性の向上
- 設定の外出しによるメンテナビリティ向上
includeには4種類の外部ファイルの引っ張り方が用意されている。
| 方法 | 説明 |
|---|---|
| local | 同一プロジェクト内の別ファイルをinclude |
| file | 同一GitLab内の別プロジェクト内のファイルをinclude |
| remote | リモートで公開されているファイルのURLを指定してinclude |
| template | GitLabが提供しているテンプレートをinclude |
事前準備
検証用にGitLab上に以下の空のプロジェクトを作成し、ほぼ何もしない.gilab-ci.yamlを作成する。ディレクトリ構造は以下となる。
.
├── .gitlab-ci.yml
└── README.md
作成した.gitlab-ci.ymlは以下となる。
stages:
- build
build-job:
stage: build
script:
- echo "dummy"
このパイプライン(build stage)の実行結果は以下。
Executing "step_script" stage of the job script
$ echo "dummy"
dummy
Job succeeded
local
localは同一プロジェクト内の別ファイルをincludeする。
この機能の確認のために、最初に別ファイルを格納するディレクトリを作成する。
mkdir .gitlab-ci
次に適当なジョブを記載したファイル(.gitlab-ci/included.yml)を作成する。
cat << EOF > .gitlab-ci/included.yml
test:
stage: build
script:
- echo "included"
EOF
事前準備の際に作成した/.gitlab-ci.ymlで上記の.gitlab-ci/included.ymlを呼び出すように修正する。
stages:
- build
include:
- local: .gitlab-ci/included.yml
build-job:
stage: build
script:
- echo "dummy"
これをコミットすると、2つのジョブが平行して実行される。
.gitlab-ci/included.ymlで実装したtestジョブの出力結果は以下となった。
Executing "step_script" stage of the job script
$ echo "included"
included
Job succeeded
ちなみに公式のサンプルにも説明があるが、localとremoteに関しては省略可能であるため以下のようにも書ける。
include:
- .gitlab-ci/included.yml
ネストも出来るようなので、こちらも試してみる。デフォルトの設定値を定義したユースケースを想定し、config-defaults.ymlを作成する
cat << EOF > .gitlab-ci/config-defaults.yml
variables:
DEFAULT_VAL_A: "default_val_a"
DEFAULT_VAL_B: "default_val_a"
EOF
.gitlab-ci/included.ymlを以下のように修正する。
include: .gitlab-ci/config-defaults.yml
test:
stage: build
script:
- echo "included"
.gitlab-ci.ymlを以下のように修正する。
variables:
DEFAULT_VAL_B: "b"
stages:
- build
include:
- local: .gitlab-ci/included.yml
build-job:
stage: build
script:
- echo "$DEFAULT_VAL_A"
- echo "$DEFAULT_VAL_B"
これをpushした時のbuild-jobの結果は以下となった。
Executing "step_script" stage of the job script
$ echo "$DEFAULT_VAL_A"
default_val_a
$ echo "$DEFAULT_VAL_B"
b
Job succeeded
ネスト先の.gitlab-ci/config-defaults.ymlから値が引っ張れることと、値が上書き出来ることが確認できた。
file
fileは同一のGitLab内の別プロジェクトからファイルを引っ張ってくる。
file:と同列にproject:、ref:も指定し、プロジェクト名およびGit Refを指定する。refはデフォルトでHEADになっており、スキップすることも可能。
ここでは同一GitLab内にmyapp/common-gitlab-ciを作成し、includeするYAMLを配置する。
git cloneしたディレクトリで以下を実行して作成する。
mkdir .gitlab-ci
cat << EOF > ./.gitlab-ci/common.yaml
common:
stage: build
script:
- echo "common"
EOF
次に最初に作成したプロジェクトのincluded.ymlを以下のように修正する
include:
- .gitlab-ci/config-defaults.yml
- project: myapp/common-gitlab-ci
file: .gitlab-ci/common.yaml
test:
stage: build
script:
- echo "included"
これをpushしてパイプラインを実行すると、build-job、test、commonの3つのジョブが実行され、commonの結果を見ると外部プロジェクトから引っ張ってこれているのが分かる。
Executing "step_script" stage of the job script
$ echo "common"
common
Job succeeded
これにより、開発リポジトリを変更することなく外部リポジトリのYAMLを修正するだけでパイプラインを変更することが実証できた。
複数プロジェクトでGitLab CIを使う際、共通パラメータなどを格納するのはfileで指定するプロジェクトにしておくと良さそうだ。
remote
remoteは外部公開しているYAMLのURLを直接指定する。検証用に先程作成したプロジェクトmyapp/common-gitlab-ciをPublic設定に変更し、適当なYAMLを追加してrawのURLを指定することで検証しようとしたのだが、GitLab側でPublicに変更出来なかったため、GitHubで同等のプロジェクト(リポジトリ)を作成して代用した。
以下のファイルを作成する。
cat << EOF > ./.gitlab-ci/remote.yml
remote:
stage: build
script:
- echo "remote"
EOF
GitHubにpushした結果、以下でYAMLが取得できるようになった。
https://raw.githubusercontent.com/imurata/common-gitlab-ci/main/.gitlab-ci/remote.yml
これをincluded.ymlで呼び出すよう、以下のようにremote行を追加する。
include:
- .gitlab-ci/config-defaults.yml
- project: myapp/common-gitlab-ci
file: .gitlab-ci/common.yaml
- remote: https://raw.githubusercontent.com/imurata/common-gitlab-ci/main/.gitlab-ci/remote.yml
test:
stage: build
script:
- echo "included"
なお、前述のようにremote:は省略することも可能だが、ここでは可読性を重視して省略していない。
パイプラインを実行した結果、remoteのジョブが増え、以下の結果を得た。
Executing "step_script" stage of the job script
$ echo "remote"
remote
Job succeeded
YAMLを共通化したいが同一のGitLabにプロジェクトを置けないようなケースではremoteが利用できそうだ。
template
templateはGitLabが持っているテンプレートを呼び出すことが出来る。
ここではBash.gitlab-ci.ymlを試しに呼び出してみる。
Bash.gitlab-ci.ymlが内部で呼び出しているstageはbuild、test、deployなので、/.gitlab-ci.yml側でもそれらを使うように変更する。
修正後の/.gitlab-ci.ymlは以下となる。
variables:
DEFAULT_VAL_B: "b"
stages:
- build
- test
- deploy
include:
- local: .gitlab-ci/included.yml
build-job:
stage: build
script:
- echo "$DEFAULT_VAL_A"
- echo "$DEFAULT_VAL_B"
またincluded.ymlもtemplateを使うように修正する。
include:
- .gitlab-ci/config-defaults.yml
- project: myapp/common-gitlab-ci
file: .gitlab-ci/common.yaml
- remote: https://raw.githubusercontent.com/imurata/common-gitlab-ci/main/.gitlab-ci/remote.yml
- template: Bash.gitlab-ci.yml
test:
stage: build
script:
- echo "included"
これでパイプラインを実行すると、buildステージ以外にBash.gitlab-ci.ymlで定義していたtest、deployステージが実行され、buildステージではBash.gitlab-ci.ymlが定義していたbuild1ジョブが実行されることが分かる。
templateに関しては元々用意してあるものをどう活用するかというよりは、自作したものをGitLab内でテンプレート化した際に使うものだと思われる。
テンプレートの作成についてはDevelopment guide for GitLab CI/CD templatesが参考になるが、ここでは本旨ではないため触れない。
まとめ
includeを使うことで単なるファイル分割ではなく、リポジトリ外のYAMLを引っ張ってパイプラインを実行することが出来、.gitlab-ci.ymlをプロジェクト単位で管理するリスクを軽減できる見込みを得た。
インフラ部門がGitLab CIによるCI/CDを推進する場合、開発部門がプロジェクト毎にジョブを作り込むよりも、インフラ部門で標準化やコンテナイメージの最新化を図ったYAMLを作成してincludeで開発部門に使ってもらうことで、組織全体がよりセキュアに持っていけるのではないかと思う。