こんにちは、GMO インターネットグループ株式会社の岩本です。
モノレポ構成のプロジェクトにおける GitLab CI の設定方法について調べましたので紹介します。
目次
はじめに
今回モノレポ構成のプロジェクトを作る機会があり GitLab CI の設定をどのようにすればよいか調べました。
というのも、GitLab CI を動かすためにはプロジェクトルートの .gitlab-ci.yml
に CI の設定を書く必要がありますが、複数プロジェクトの CI の設定を 1 つのファイルで管理するのは大変に感じたためです。
trigger:include:local
, trigger:include:artifact
を使った方法と、include:local
を使った方法の計 3 種類の方法についてまとめました。
この記事では GitLab Community Edition 15.3.4, GitLab Runner 15.3.2 を使用しています。
trigger:include:local を使った方法
GitLab CI には trigger
というキーワードがあり、これを使うことで別リポジトリのパイプラインを実行したり1,2、同じリポジトリ内の別の .gitlab-ci.yml を実行したりできます3 。
今回はモノレポ構成のため、同一リポジトリ内にある .gitlab-ci.yml を対象にした trigger:include:local
を使います。
ディレクトリ構成は以下のようにしました。
今回は 2 つのプロジェクト project_a, project_b を 1 つのリポジトリで管理する構成とします。
この構成は後述の trigger:include:artifact
でも同様です。
.
|-- .gilab-ci.yml
|-- project_a
| └-- .gilab-ci.yml
└-- project_b
└-- .gilab-ci.yml
プロジェクトルートの .gitlab-ci.yml, 各プロジェクトの .gitlab-ci.yml は以下のように書きます。
プロジェクトルートの .gitlab-ci.yml
project_a:
trigger:
include: project_a/.gitlab-ci.yml
strategy: depend
rules:
- changes:
- "project_a/**/*"
project_b:
trigger:
include: project_b/.gitlab-ci.yml
strategy: depend
rules:
- changes:
- "project_b/**/*"
各プロジェクトの .gitlab-ci.yml
workflow:
rules:
- if: $CI_MERGE_REQUEST_IID
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
stages:
- test
- deploy
job1:
stage: test
script: echo test
job2:
stage: deploy
script: echo deploy
ポイント
trigger:strategy の設定
trigger:strategy
を付けないデフォルトの設定では、子パイプラインが作成された直後に親パイプラインが成功となります4。
今回は子パイプラインで起きたエラーを表示してほしいため trigger:strategy
を設定しています。
子パイプラインに rules:if: $CI_MERGE_REQUEST_IID を付けないと動作しない場合がある
マージリクエストのタイミングで実行される子パイプラインを、マージリクエストパイプラインであると明示的に設定する必要があります。
これを付けない場合子パイプラインがブランチパイプラインとして認識され、パイプラインが期待したとおりに動かない場合があります5。
ブランチパイプラインは git push
された内容を、マージリクエストパイプラインはマージリクエストの修正対象全体を見るという違いがあります6。
trigger で実行するパイプラインに有効なジョブがなければ失敗する
trigger
を発火したはいいものの、子パイプラインの中に実行するジョブが一つも無い場合、親パイプラインは失敗します。実行元でそもそも trigger
を発火させないなど何かしら工夫が必要です。
trigger:include:artifact を使った方法
今回は project_a, project_b だけでしたがもっとプロジェクト数が多い場合、プロジェクトルートの .gitlab-ci.yml
の管理が大変になりそうです。
そこで、各プロジェクトごとに実行するパイプラインを動的に生成し管理の手間を減らせないか検討しました。
具体的には trigger:include:artifact
を使った方法7です。
上記で紹介した trigger:include:local
を持つ .gitlab-ci.yml
を生成します。生成されたファイルをアーティファクトにし、後続のジョブで trigger:include:artifact
を使って読み込みます。
.gitlab-ci.yml
image: ubuntu
stages:
- build
- triggers
workflow:
rules:
- if: $CI_MERGE_REQUEST_IID
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
variables:
workflow: >
workflow:
rules:
- if: __CI_MERGE_REQUEST_IID
- if: __CI_COMMIT_BRANCH == __CI_DEFAULT_BRANCH
template: >
PROJECT_NAME:
trigger:
include: PROJECT_NAME/.gitlab-ci.yml
strategy: depend
rules:
- changes:
- "PROJECT_NAME/**/*"
generate-artifact:
stage: build
script:
- echo -e "${workflow}" | sed -i -e 's/__/$/g' > generated.yml
- >
find . -name .gitlab-ci.yml -type f -not -path "./.gitlab-ci.yml" | cut -d'/' -f2 |
while read PROJECT_NAME; do
export PROJECT_NAME
echo -e "${template}" | sed -e "s/PROJECT_NAME/$PROJECT_NAME/g" >> generated.yml
done
artifacts:
paths:
- generated.yml
child-pipeline:
stage: triggers
trigger:
include:
- artifact: generated.yml
job: generate-artifact
strategy: depend
ポイント
パイプラインの生成
各プロジェクトごとに用意している .gitlab-ci.yml
を find
コマンドを使って見つけ、trigger:include:local
の対象にしています。
これでプロジェクトルートの .gitlab-ci.yml
の管理の手間から開放されました。
YAML ファイル生成ジョブが毎回実行される
この方法では毎回ファイルを生成するため、その分時間がかかります。rules:changes
などを使って README.md だけの変更の場合は実行しない、などの工夫もしたほうがよいかもしれません。
子パイプラインが 1 つにまとめられる
個々のマージリクエスト画面で CI ジョブの成功/失敗がチェックマーク等で表示されますが、この方法では複数の子パイプラインが 1 つにまとめられてしまいます。
ぱっと見何を実行したのか分かりにくく、パイプラインが失敗した場合やどのプロジェクトの CI が実行されたか知りたい場合、パイプライン詳細画面にて確認する必要があります。
include:local を使った方法
プロジェクトごとの CI の設定は .gitlab/
ディレクトリに配置します。
また、ファイル名は <プロジェクト名>.gitlab-ci.yml
とします。
.
|-- .gilab-ci.yml
└-- .gitlab/
|-- project_a.gilab-ci.yml
└-- project_b.gilab-ci.yml
プロジェクトルートの .gitlab-ci.yml
stages:
- test
- deploy
default:
image: ubuntu
include:
- local: .gitlab/*.gitlab-ci.yml
各プロジェクトの .gitlab/*.gitlab-ci.yml
.project_a:rules:
rules:
- &mr
if: $CI_MERGE_REQUEST_IID
changes:
- project_a/**/*
- &main
if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
changes:
- project_a/**/*
project_a:test:
rules:
- *mr
- *main
stage: test
script:
- echo test
project_a:deploy:
rules:
- *main
stage: deploy
script:
- echo build
needs:
- project_a:build
ポイント
YAML anchors を使ったルールの定義
GitLab には extends
を使って共通する設定を再利用できますが、配列の中身はマージされないため8ここでは YAML anchors を使った方法を採用しました9。
ジョブ名や変数名が重複しないように
各プロジェクトごとに設定するジョブ名が重複すると、CI がうまく動かない場合があります。<プロジェクト名>:<ジョブ名>
のように、名前が被らないような工夫が必要です。
また、variables
を使って変数を定義できますが、こちらも重複しないように気を付ける必要があります。
needs を使った依存関係の整理
GitLab CI のパイプラインは基本的にステージ単位で進みます。上記であればステージは test, deploy があり、test ステージに含まれる全てのジョブを完了させてから、次の deploy ステージに進みます。
複数のプロジェクトを同時に更新していた場合、直接関係のないジョブの完了を待たなければならないかもしれません。
例: project_a:test は完了したが、project_b:test に時間がかかっているため、project_a:deploy が実行できない
この問題は needs
を使うことでジョブ間の依存関係を記述でき、無駄な待ち時間を削減できます。
また、ジョブ間の依存関係がグラフで表示されかっこよいです!10
その他
今回 .gitlab-ci.yml を書くにあたり VSCode を使いました。
その際 YAML プラグイン https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml を使うと、GitLab CI の設定ファイル内で使えるキーワードを補完してくれたり、マウスホバーした際にそのキーワードの説明文を表示できたりと便利でしたのでおすすめです。
まとめ
モノレポ構成のプロジェクトで GitLab CI を動かすという 1 つのお題に対し、今回挙げただけでも 3 種類の方法が考えられました。
最終的に 3 つ目の include:local
を使った方法を採用することとなりました。CI の結果の見やすさや設定ファイルが一か所にまとまっていること、needs
を使った効率的なパイプライン処理ができることなどが決めてです。
また、GitLab CI の設定方法を調査するにあたり、公式ドキュメントの充実さに改めて気づくことができました。感謝 :bow:
今回参考にしたドキュメントやリポジトリの URL を以下に記載します。
中でも GitLab の .gitlab-ci.yml
11はモノレポ構成でなくても参考になるかと思いますので、一度目を通すと新しい発見があるかもしれません。
参考資料
.gitlab-ci.yml
keyword reference
https://docs.gitlab.com/ee/ci/yaml/- CI/CD pipelines
https://docs.gitlab.com/ee/ci/pipelines/ - GitLab リポジトリの .gitlab-ci.yml
https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab-ci.yml
脚注
- Multi-project pipelines
https://docs.gitlab.com/ee/ci/pipelines/downstream_pipelines.html#multi-project-pipelines - include:project の例
https://docs.gitlab.com/ee/ci/pipelines/downstream_pipelines.html#use-a-child-pipeline-configuration-file-in-a-different-project - Parent-child pipelines
https://docs.gitlab.com/ee/ci/pipelines/downstream_pipelines.html#parent-child-pipelines trigger job to be marked as success as soon as the downstream pipeline is created
https://docs.gitlab.com/ee/ci/yaml/#triggerstrategy- ブランチパイプラインをマージリクエストパイプラインに切り替えるために
workflow:rules
を紹介している
https://docs.gitlab.com/ee/ci/yaml/workflow.html#switch-between-branch-pipelines-and-merge-request-pipelines - パイプライン実行時の対象とするファイルがマージリクエストパイプラインとブランチパイプラインとで異なる
https://gitlab.com/gitlab-org/gitlab/-/blob/v15.3.4-ee/app/models/ci/pipeline.rb#L1149-1152 - 子パイプラインの動的生成
https://docs.gitlab.com/ee/ci/pipelines/downstream_pipelines.html#dynamic-child-pipelines Doesn’t merge the values of the keys.
https://docs.gitlab.com/ee/ci/yaml/#extends- https://docs.gitlab.com/ee/ci/yaml/yaml_optimization.html#anchors
- needs を使ったジョブ間の依存関係の可視化
https://docs.gitlab.com/ee/ci/directed_acyclic_graph/index.html#needs-visualization - GitLab リポジトリの .gitlab-ci.yml
https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab-ci.yml
ブログの著者欄
採用情報
関連記事
KEYWORD
CATEGORY
-
技術情報(433)
-
イベント(159)
-
カルチャー(36)
-
デザイン(17)
TAG
- 5G
- Adam byGMO
- AI
- AWX
- BIT VALLEY
- blockchain
- ChatGPT
- cloudnative
- CloudStack
- CM
- CNDO
- CNDT
- CODEGYM Academy
- ConoHa
- CS
- CSS
- CTF
- DC
- Designship
- Desiner
- DevSecOpsThon
- Docker
- DTF
- GitLab
- GMO Developers Day
- GMO Developers Night
- GMO GPUクラウド
- GMO Hacking Night
- GMO kitaQ
- GMO SONIC
- GMOアドパートナーズ
- GMOアドマーケティング
- GMOイエラエ
- GMOグローバルサイン
- GMOソリューションパートナー
- GMOデジキッズ
- GMOブランドセキュリティ
- GMOペイメントゲートウェイ
- GMOペパボ
- GMOリサーチ
- Go
- GTB
- Hardning
- Harvester
- HCI
- iOS
- IoT
- ISUCON
- JapanDrone
- Java
- JJUG
- K8s
- Kids VALLEY
- LLM
- MetaMask
- MySQL
- NFT
- NVIDIA
- OpenStack
- Perl
- PHP
- PHPcon
- PHPerKaigi
- QUIC
- Rancher
- RPA
- Ruby
- Selenium
- Spectrum Tokyo Meetup
- splunk
- SRE
- SSL
- Terraform
- TLS
- TypeScript
- UI/UX
- VLAN
- VS Code
- アドベントカレンダー
- インターンシップ
- オブジェクト指向
- オンボーディング
- お名前.com
- カルチャー
- コンテナ
- スクラム
- スペシャリスト
- セキュリティ
- ソフトウェアテスト
- チームビルディング
- ドローン
- ネットワーク
- プログラミング教育
- ブロックチェーン
- ミドルウェア
- モバイル
- ゆめみらいワーク
- リモートワーク
- 京大ミートアップ
- 基礎
- 多拠点開発
- 大学授業
- 宮崎オフィス
- 応用
- 技育プロジェクト
- 新卒
- 暗号
- 機械学習
- 決済
PICKUP