GitLabで並列にCIを行う方法について調査しました。
GitLab 11.5 でテストしていますが、現時点(2018年12月)の最新版 11.6 でも同じはずです。
もし差分が見つかれば随時反映します。(ので、詳しい方からのコメントもお待ちしています)
同一stageに複数jobを定義
一番基本的な戦略として、並列にビルド・デプロイする処理を、それぞれ「同じstageに属するjob」として定義する方法があります。
.gitlab-ci.yml の例
stages:
- a
- b
.job_a_common:
script: mkdir build && touch build/${CI_JOB_NAME}.txt
artifacts:
paths:
- build
job_a1:
stage: a
extends: .job_a_common
job_a2:
stage: a
extends: .job_a_common
job_a3:
stage: a
extends: .job_a_common
job_b:
stage: b
script: ls -l build/
実行結果
下の様にjobが並列に動作します。
artifacts をまとめることができて、後のstageでそれらを扱うこともできます。
: job_b の出力
$ ls -l build/
total 0
-rw-r--r-- 1 root root 0 Dec 28 04:38 job_a1.txt
-rw-r--r-- 1 root root 0 Dec 28 04:38 job_a2.txt
-rw-r--r-- 1 root root 0 Dec 28 04:38 job_a3.txt
Job succeeded
parallel
による多重度指定
2018年11月にリリースされた GitLab 11.5 から、 parallel
というキーワードが導入されました。
https://docs.gitlab.com/ee/ci/yaml/#parallel
指定した数値を多重度として、jobを並列実行することができます。
それぞれのjobの処理内容は、環境変数 CI_NODE_INDEX
CI_NODE_TOTAL
を使って変化させることができます。
.gitlab-ci.yml の例
stages:
- a
- b
job_a:
stage: a
parallel: 3
script: mkdir build && touch build/${CI_NODE_INDEX}_of_${CI_NODE_TOTAL}.txt
artifacts:
paths:
- build
job_b:
stage: b
script: ls -l build/
実行結果
Pipelineの表示では、ぱっと見では並列に見えませんが、よく見ると多重度3
が見えています。
Jobsの表示では、確かに並列にJobが動いていることがわかります。
artifactsについても、複数Jobを定義した場合と同様に扱うことができます。
: job_b の出力
$ ls -l build/
total 0
-rw-r--r-- 1 root root 0 Dec 28 04:45 1_of_3.txt
-rw-r--r-- 1 root root 0 Dec 28 04:45 2_of_3.txt
-rw-r--r-- 1 root root 0 Dec 28 04:46 3_of_3.txt
Job succeeded
ファイル単位で並列にビルド (parallel
の応用)
parallel
では並列多重度を静的に指定することしかできません。
例えば「ファイル毎の並列ビルド」など、多重度を動的に決定したいことはあると思います。
ですが、現時点(2018年末、GitLab 11.6リリース時点)でそれを行う方法はありません。
機能の提案はされているので、将来的には可能になる可能性はあります。
https://gitlab.com/gitlab-org/gitlab-ce/issues/23455
少し妥協して、適当な多重度で立ち上げたJobで対象ソースを切り替えることで、ビルドを平準化することができます。
以降はその例です。
repository構造
$ tree
.
├── 1.txt
├── 2.txt
├── 3.txt
├── 4.txt
└── 5.txt
0 directories, 5 files
$ head *.txt
==> 1.txt <==
11111
==> 2.txt <==
22222
==> 3.txt <==
33333
==> 4.txt <==
44444
==> 5.txt <==
55555
.gitlab-ci.yml
stages:
- a
- b
job_a:
stage: a
parallel: 3
script:
- mkdir build
- NF=$(ls -1 *.txt | wc -l)
- ls -1 *.txt
| awk "(NR-1)%${CI_NODE_TOTAL}==${CI_NODE_INDEX}-1"
| xargs -P$((NF/CI_NODE_TOTAL+1)) -I_ sh -c "cat _ | tee build/_"
artifacts:
paths:
- build
job_b:
stage: b
script: head build/*.txt
途中で少々複雑な awk
の式がありますが、行数の剰余を取って、順にJobに割り振っているだけです。
また、Job内でもプロセスを並列化するため、対象ソースの数から必要なプロセス数を計算して xargs
に -P
オプションで渡しています。
実行結果
Pipelineの見た目は、前のものと同じなので割愛します。
それぞれのJobの出力を見ると、ちゃんと処理が平準化されていることがわかります。
: job_a 1/3 の出力
$ mkdir build
$ NF=$(ls -1 *.txt | wc -l)
$ ls -1 *.txt | awk "(NR-1)%${CI_NODE_TOTAL}==${CI_NODE_INDEX}-1" | xargs -P$((NF/CI_NODE_TOTAL+1)) -I_ sh -c "cat _ | tee build/_"
44444
11111
: job_a 2/3 の出力
$ mkdir build
$ NF=$(ls -1 *.txt | wc -l)
$ ls -1 *.txt | awk "(NR-1)%${CI_NODE_TOTAL}==${CI_NODE_INDEX}-1" | xargs -P$((NF/CI_NODE_TOTAL+1)) -I_ sh -c "cat _ | tee build/_"
22222
55555
:job_a 3/3 の出力
$ mkdir build
$ NF=$(ls -1 *.txt | wc -l)
$ ls -1 *.txt | awk "(NR-1)%${CI_NODE_TOTAL}==${CI_NODE_INDEX}-1" | xargs -P$((NF/CI_NODE_TOTAL+1)) -I_ sh -c "cat _ | tee build/_"
33333
job_b でも build 以下にファイルが残っているため、意図したとおりに動作したことがわかります。
: job_b の出力
$ head build/*.txt
==> build/1.txt <==
11111
==> build/2.txt <==
22222
==> build/3.txt <==
33333
==> build/4.txt <==
44444
==> build/5.txt <==
55555
Job succeeded
以上です。