はじめに
前回の記事では、2回にわたりGitLab CI/CDの基本的なパイプライン作成方法 を解説しました。ジョブの定義からステージの構成まで、一通りの流れを実際に試しながら理解できたと思います。
しかし、実際にチーム開発を進めていくと、パイプラインの定義ファイルである.gitlab-ci.ymlがすぐに肥大化しがちです。
- 同じ処理を複数のジョブに書いてしまう
- プロジェクトごとに似たような CI 定義をコピペして使い回す
こうした状態になると、修正や共通化が難しくなり、運用コストが増えてしまいます。
そこで今回の記事では、GitLab CI/CDが用意しているincludeとextendsの仕組みに注目します。これらを活用することで、設定を整理し、再利用しやすい形に整えることができます。
小さな検証を通して、それぞれの機能がどのように役立つかを紹介していきます。
include機能:外部ファイルのインポート
includeは、別ファイルに書かれたCI定義を取り込む機能です。別のプロジェクトにあるファイルを読み込めるのが大きな特徴です。今回の検証では、テスト用にGitLab上で 2 つのプロジェクトを用意し、一方のプロジェクトにあるパイプライン定義を、もう一方のプロジェクトからincludeで読み込む形を確認しました。
検証環境
- メインプロジェクト:runner-test-project-1
- .gitlab-ci.yml(実際にパイプラインを実行する定義ファイル)
- インポート用プロジェクト:runner-test-project-2
- ci/shared.yml(他のプロジェクトから取り込んで使うためのジョブ定義ファイル)
設定例(実行側)
# runner-test-project-1/.gitlab-ci.yml
variables:
FROM_PROJECT: "overridden-in-runner-test-project-1"
include:
- project: "group/runner-test-project-2"
ref: "main"
file: "/ci/shared.yml"
stages: [test]
check-local:
stage: test
script:
- echo "FROM_PROJECT=${FROM_PROJECT}"
設定例(includeされる側)
# runner-test-project-2/ci/shared.yml
project-job:
stage: test
variables:
FROM_PROJECT: "default-in-provider-project"
script:
- echo "FROM_PROJECT=${FROM_PROJECT}"
実行結果
- check-local(ローカルジョブ)と project-job(外部ジョブ)がどちらもパイプラインに表示される
- すべてのジョブが成功
- FROM_PROJECT=overridden-in-runner-test-project-1 が出力され、実行側の変数が優先されることを確認


ポイント
変数の上書き:同じ名前の変数が複数定義されている場合、最終的にはメインの.gitlab-ci.ymlファイルで定義された値が優先されます。外部ファイル側はデフォルト値を置き、環境依存の値はメイン側で上書きするのがベストです。
権限:実行するユーザーが参照先リポジトリを読む権限を持っている必要があります。安定運用のためには、参照元・参照先を同じグループ内にまとめておくとよいでしょう。
ref の固定:main を参照すると、外部ファイルの変更が即座に反映されてしまうため、安定性を重視するなら、タグやリリースブランチを指定するのがおすすめです。
参考:Use CI/CD configuration from other files | GitLab Docs
extends機能:共通設定の継承
extendsは、ジョブごとに共通する設定をテンプレートとしてまとめ、そこから継承できる仕組みです。同じ処理や変数を繰り返し記述せずに済むのが大きな特徴です。今回の検証では、共通設定を.default-templateとして定義し、job_aではそのまま継承、job_bでは一部の変数を上書きする形を確認しました。
検証環境
- 任意のプロジェクトに extends-gitlab-ci.yaml を作成し、次の内容を記述しました。
設定例
stages: [test]
# 共通設定(テンプレート)
.default-template:
stage: test
image: registry.gitlab.local.example.com/root/registry-project/alpine:latest
tags:
- devsecops-runner
before_script:
- echo "BEFORE(from base) run common setup"
variables:
BASE_VAR: from-base
OVERRIDE_ME: from-base
# 継承のみ(上書きなし)
job_a:
extends: .default-template
script:
- echo "A BASE_VAR=$BASE_VAR OVERRIDE_ME=$OVERRIDE_ME"
- echo "A IMAGE=$(cat /etc/alpine-release 2>/dev/null || echo unknown)"
# 一部上書き(variables を上書き)
job_b:
extends: .default-template
variables:
OVERRIDE_ME: from-job-b
script:
- echo "B BASE_VAR=$BASE_VAR OVERRIDE_ME=$OVERRIDE_ME"
実行結果
- すべてのジョブが成功
- job_a
- BASE_VAR=from-base
- OVERRIDE_ME=from-base
- before_script の “BEFORE(from base) run common setup” が実行され、image も alpine:latest が利用されている

- job_b → 変数 OVERRIDE_ME はジョブ側の値で上書き、BASE_VAR や before_script はテンプレートの値がそのまま反映されている
- BASE_VAR=from-base(テンプレートのまま)
- OVERRIDE_ME=from-job-b(ジョブ側の値で上書き)
- before_script と image はテンプレートの設定が適用されている

ポイント
共通化のメリット:before_script や変数をテンプレートにまとめることで、修正が必要になった際に1箇所を直すだけで全ジョブに反映できます。結果として、ジョブテンプレートの管理や保守がシンプルになり、影響範囲も明確に把握できます。
変数の上書き:テンプレートとジョブの両方に同じキーが定義されている場合、ジョブで定義された値が優先されます。テンプレートは共通の値を設定し、必要に応じてジョブ側で上書きするのが基本的な使い方です。
参考:Optimize GitLab CI/CD configuration files
まとめ
- includeを使うと、外部ファイルを取り込んで 共通ジョブを複数のプロジェクトで再利用できます。
- extendsを使うと、共通設定をテンプレート化してジョブごとの差分だけを簡潔に記述できます。
- どちらの機能も、複雑化しやすい .gitlab-ci.yml を整理し、変更や保守を効率化するために欠かせない仕組みです。
チームやプロジェクトが大きくなるほど、パイプラインの管理は難しくなります。include とextendsを活用すれば、CI/CD 設定をシンプルかつ再利用性の高い形に整え、大規模な開発環境でも安定して運用できる基盤を作ることができます。