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で開発部門に使ってもらうことで、組織全体がよりセキュアに持っていけるのではないかと思う。