GitLab CI@CIツール¶
はじめに¶
本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。
01. GitLab CIの仕組み¶
アーキテクチャ¶
GitLab Runnerは、GitLabリポジトリのgitlab-ci.yml
ファイルをHTTPSで参照し、定義されたパイプラインを実行する。
GitLab Runner¶
▼ GitLab Runnerとは¶
GitLab CIのgitlab-ci.yml
ファイルで定義されたパイプラインを実行する。
パイプライン構成¶
記入中...
リリースノート¶
▼ パイプラインバッジ¶
![pipeline](https://gitlab.com/foo-project/foo-repository/badges/main/pipeline.svg)
▼ 最新バージョンタグバッジ¶
![release](https://gitlab.com/foo-project/foo-repository/badges/-/badges/release.svg)
02. セットアップ¶
インストール¶
repository/
├── .gitlab-ci.yml
他のプライベートリポジトリへのアクセス¶
他のプライベートリポジトリにアクセスするためには、GitLab CIで、Gitの認証情報をセットアップする必要がある。
go_mod:
stage: build
image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/golang:${GO_VERSION}
before_script:
# 他のプライベートリポジトリからモジュールをプルするために、認証情報をセットアップする
- echo "machine foo.gitlab.com" > ~/.netrc
- echo "login ${GIT_USER}" >> ~/.netrc
- echo "password ${GIT_TOKEN}" >> ~/.netrc
script:
# 他のプライベートリポジトリのモジュールをインポートする
- go mod tidy
03. API¶
パイプライン実行¶
他のリポジトリのパイプラインを発火する。
例えば、GitOps時にアプリリポジトリがKubernetesリポジトリのパイプラインを実行する場合に役立つ。
trigger_upstream_pipeline:
image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/alpine/git
before_script:
- apk update && apk --no-cache add curl curl-dev
script:
- |
curl -X POST "https://gitlab.com/api/v4/projects/<プロジェクトID>/trigger/pipeline"
-F token=<トークン>
-F ref=<ブランチ>
-F variables[<変数名>]=<値>
04. Global¶
予約変数¶
▼ CI_COMMIT_BRANCH
¶
現在のブランチ名が割り当てられている。
featureブランチの名前によらずにCIを実行する条件を定義できる。
foo_job:
stage: build
script:
- echo foo
rules:
# featureブランチ (develop、main、以外) のみで実行する
- if: $CI_PIPELINE_SOURCE == 'push' && $CI_COMMIT_BRANCH$CI_COMMIT_BRANCH != 'develop' && $CI_COMMIT_BRANCH != 'main'
▼ CI_COMMIT_TAG
¶
現在のタグ名が割り当てられている。
条件文と組み合わせれば、タグの作成時にパイプラインを発火させられる。
▼ CI_PIPELINE_SOURCE
¶
現在のパイプラインを発火させたイベント名 (MR作成/更新イベント、手動パイプライン実行イベント) が割り当てられている。
タグの付与時にパイプラインを発火させる場合、CI_COMMIT_TAG
変数を使用する。
foo_job:
stage: build
script:
- echo foo
rules:
# MRを作成/更新したタイミングで発火する
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
値 | 説明 |
---|---|
merge_request_event |
マージリクエストの作成時を表す。 |
push |
プッシュ時を表す。 |
web |
画面からの手動実行時を表す。 |
▼ CI_PROJECT_DIR
¶
GitLabの実行環境のルートディレクトリが割り当てられている。
GitLabは、ルートディレクトリにGitLabリポジトリをクローンする。
▼ GIT_SUBMODULE_STRATEGY
¶
デフォルトですと、GitLab CIがサブモジュールを無視して処理してしまうため、これを無視しないようにする。
foo_job:
variables:
GIT_SUBMODULE_STRATEGY: "recursive"
include¶
▼ includeとは¶
参照する別リポジトリとCIテンプレートファイルを設定する。
GitLab CIのJobの設定ファイルを、中央集中的なリポジトリで一括管理しておき、これを他のリポジトリからリモート参照できるようになる。
ポリレポ構成規約と相性がよい。
▼ 親リポジトリ側のCIテンプレート¶
子リポジトリでは、Jobを定義する。
GitLab CIでは、定義したJobは自動的に実行される。
一方で.
(ドット) をつけることで、使用を明示的に宣言しない限り実行できない『隠しJob』として定義できる。
また、子リポジトリで上書きできる変数を親リポジトリに設定しておく。
# foo-job.yaml
# 変数のデフォルト値を設定しておく
variables:
PATH: "default"
.foo_job:
stage: build
script:
- cat "${PATH}"/foo.txt
# bar-job.yaml
# 変数のデフォルト値を設定しておく
variables:
PATH: "default"
.bar_job:
stage: build
script:
- cat "${PATH}"/bar.txt
# baz-job.yaml
# 変数のデフォルト値を設定しておく
variables:
PATH: "default"
.baz_job:
stage: build
script:
- cat "${PATH}"/baz.txt
▼ 子リポジトリ側のリモートコール¶
子リポジトリでは、親リポジトリのCIテンプレート上の隠しJobをコールする。
include:
# GitLab CIのテンプレートを管理するリポジトリ
- project: project/ci-template-repository-1
ref: main
file:
- foo-job.yml
- bar-job.yml
- baz-job.yml
- project: project/ci-template-repository-2
ref: main
file:
- qux-job.yml
foo_1_job:
# 親リポジトリで定義した隠しJobをコールする
extends: .foo_job
stage: build
script:
- cat "${PATH}"/foo.txt
# 親リポジトリの変数を上書きする
variables:
PATH: "path_1"
foo_2_job:
# 親リポジトリで定義した隠しJobをコールする
extends: .foo_job
stage: build
script:
- cat "${PATH}"/foo.txt
# 親リポジトリの変数を上書きする
variables:
PATH: "path_2"
bar_job:
extends: .bar_job
stage: build
needs:
- foo1_job
- foo2_job
script:
- cat "${PATH}"/bar.txt
baz_job:
extends: .baz_job
stage: build
script:
- cat "${PATH}"/baz.txt
▼ ヒアドキュメントを使用したファイルの配布¶
ヒアドキュメントを使用して、CIの実行コンテナでファイルを動的に作成し、これを配布する。
artifacts
キーを使用して、後続のJobでも設定ファイルを使用できるようにしている。
variables:
GITLAB_COMMENT_VERSION: "6.0.1"
# github-commentを準備する
.install_github_comment:
stage: build
image: alpine/git:latest
script:
# github-commentをインストールする
- |
apk add --upgrade curl tar jq
LATEST_DOWNLOAD_URL=$(curl -sL https://api.github.com/repos/suzuki-shunsuke/github-comment/releases/latest | jq -r ".assets[].browser_download_url" | grep linux_amd64)
curl -sL -O "${LATEST_DOWNLOAD_URL}"
tar zxvf *.tar.gz
- ./github-comment --version
# CIの実行環境で各リポジトリに配布するgithub-comment.yamlファイルを作成する
- |
cat << 'EOF' > github-comment.yaml
# https://suzuki-shunsuke.github.io/github-comment/getting-started
---
exec:
# 静的解析以外の処理のためのテンプレート
# -kオプションで何も指定しない場合、defaultテンプレートになる
default:
- when: "true"
template: |
## `{{ .Vars.TestName }}`
| 項目 | 内容 |
|-----|--------------------|
| コマンド | `{{ .JoinCommand }}` |
| 説明 | {{ .Vars.Description }} |
| 実行Job | {{ template "link" . }} |
## 詳細
<details>
<summary>クリックで開く</summary>
```bash
$ {{ .JoinCommand }}
{{ .CombinedOutput | AvoidHTMLEscape }}
```
</details>
# 静的解析のためのテンプレート
test:
- when: "true"
template: |
## `{{ .Vars.TestName }}`
| 項目 | 内容 |
|-----|--------------------|
| 静的解析 | `{{ .JoinCommand }}` |
| 説明 | {{ .Vars.Description }} |
| 成否 | {{ template "status" . }} |
| 実行Job | {{ template "link" . }} |
## 詳細
<details>
<summary>クリックで開く</summary>
```bash
$ {{ .JoinCommand }}
{{ .CombinedOutput | AvoidHTMLEscape }}
```
</details>
EOF
cat github-comment.yaml
artifacts:
paths:
- ./github-comment
# github-commentの設定ファイルを配布する
- github-comment.yaml
variables (Jobレベルでも設定可)¶
▼ variablesとは¶
Job内で使用する変数を設定する。
値をダブルクオートかシングルクオートで囲わないと、.gitlab-ci.yml
ファイル自体で予期せぬ構文エラーになる。
variables:
BAR: "bar"
BAZ: "baz"
QUX: "qux"
▼ ファイルの切り分け¶
可能であれば、variables
キーをvariables.yml
ファイルとして切り分け、これを読み込むようにする。
可読性が高くなる。
# variables.ymlファイル
# variablesで空文字を設定する
variables:
FOO: ""
include:
- local: .gitlab-ci/variables.yml
foo_job:
stage: build
script:
# ダブルクオートがない
- echo ${FOO}
▼ 空文字の出力¶
空文字を設定したい場合、variables
キーに設定しても空文字として出力されない。
# variablesで空文字を設定する
variables:
FOO: ""
foo_job:
stage: build
script:
# ダブルクオートがない
- echo ${FOO}
# variablesを定義しない
foo_job:
stage: build
script:
# ダブルクオートがある
- echo "${FOO}"
▼ リスト¶
変数でリストを定義できる。
これを使用して、単一のJob内でfor
を実行できる。
foo_job:
variables:
LIST: foo1 foo2 foo3
script:
- |
for VALUES in $LIST
do
echo ${VALUES}
done
workflow¶
▼ workflowとは¶
GitLab CI自体の発火を制御する。
▼ if¶
GitLab CIが発火する条件を設定する。
# ブランチ名に応じて、CIで使用する実行コンテナ名を切り替える
# main、develop、MR作成/変更、の順に条件を検証する。
workflow:
rules:
# mainブランチにて、任意の方法でパイプラインを実行した場合
- if: $CI_COMMIT_REF_NAME == 'main'
variables:
ENV: "prd"
# developブランチにて、任意の方法でパイプラインを実行した場合
- if: $CI_COMMIT_REF_NAME == 'develop'
variables:
ENV: "stg"
# MRにて、任意の方法でパイプラインを実行した場合
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
variables:
ENV: "tes"
# 上記以外で、webから手動でパイプラインを実行した場合
- if: $CI_PIPELINE_SOURCE == 'web'
variables:
ENV: "tes"
setup-manifest:
stage: build
image: alpine/helm:latest
# ブランチ名に応じて、valuesファイルを切り替える
script:
- helm lint . -f "${VALUES_FILE_PATH}" -f "${SECRETS_FILE_PATH}"
- helm template . -f "${VALUES_FILE_PATH}" -f "${SECRETS_FILE_PATH}" > manifest.yaml
- cat manifest.yaml
▼ changes¶
workflow:
rules:
- changes: foo/**/*
- https://blogs.networld.co.jp/entry/2022/11/01/090000?_gl=11wxr8jb_gcl_au*MTg4NDE0MjQ1My4xNjkwODAzOTEy
05. Job¶
allow_failure¶
▼ 0
以外のすべての終了コードの場合¶
stages:
- build
# terraform fmt
fmt:
# サービスコンテナ
services:
- docker:dind
image: hashicorp/terraform:1.4.6
stage: build
script:
- terraform fmt -check -recursive
# 0以外の全ての終了コードの場合のみ終了する
# インデントを揃えるべき場所がある場合に、Jobを失敗させる
allow_failure: "true"
rules:
# MRを作成/更新したタイミングで発火する
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
foo_job:
stage: build
script:
# 『echo foo-1』が失敗しても、終了コードを1にしてJobを中断させない
- echo foo-1 || true
- echo foo-2
# 0以外の全ての終了コードの場合のみ終了する
allow_failure: "true"
▼ 0
以外の特定の終了コードの場合¶
foo_job:
stage: build
script:
- echo foo
# 特定の終了コードの場合のみ終了する
allow_failure:
exit_codes:
- 1
- 3
artifacts¶
▼ artifactsとは¶
GitLabでは、以前のステージのJobのファイルを後続のJobにデフォルトで継承できる (GitLabのバージョンが古いとこの機能がない場合がある) 。
しかし、needs
でJob間に依存関係を定義している場合、artifacts
を使用しても、needs
で指定しているJob以外のファイルを継承できない。
needs
で指定したJobのartifacts
のみを継承できる。
stages:
- build
- deploy
# ビルドステージ
foo_job:
stage: build
script:
- echo foo
artifacts:
paths:
- foo
bar_job:
stage: build
script:
- echo bar
artifacts:
paths:
- bar
# デプロイステージ
baz_job:
stage: deploy
# foo_jobのartifactsは継承できるが、bar_jobのartifactsは継承できない
needs:
- foo_job
script:
- echo baz
qux_job:
stage: deploy
# foo_jobとbar_jobの両方のartifactsを継承できる
needs:
- foo_job
- bar_job
script:
- echo qux
▼ artifactsが不要な場合¶
GitLabでは、以前のステージのJobのファイルを後続のJobにデフォルトで継承できる。
そのため、artifacts
は不要である。
# ビルドステージ
foo_job:
stage: build
script:
- echo foo
# デプロイステージ
bar_job:
stage: deploy
script:
# buildステージのファイルを使用する
...
before_script¶
▼ before_scriptとは¶
記入中...
▼ 共通化¶
before_script
キーを隠しJobとして定義することで、共通のスクリプトとして使用できる。
# GitLabの他のリポジトリからモジュールをプルするために、認証情報をセットアップする
.setup_git:
before_script:
- echo "machine foo.gitlab.com" > ~/.netrc
- echo "login ${GIT_USER}" >> ~/.netrc
- echo "password ${GIT_TOKEN}" >> ~/.netrc
go_mod:
stage: build
image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/golang:${GO_VERSION}
extends:
- .setup_git
script:
# 本モジュールはgo buildする必要はないため、go modのみを実行する
- go mod tidy
cache¶
▼ cacheとは¶
指定したディレクトリのキャッシュを作成する。
もしそのディレクトリに変化がなければ、前回のパイプラインのディレクトリを再利用する。
これにより、CIの時間を短縮できる。
bar_job:
stage: build
cache:
# キャッシュの名前を設定する
key: $CI_COMMIT_REF_SLUG
# キャッシュとして保管するディレクトリを設定する
paths:
- ./node_module
▼ policy¶
キャッシュ作成のルールを設定する。
bar_job:
stage: build
cache:
paths:
- ./node_module
# キャッシュのダウンロードのみを実行する
policy: pull
bar_job:
stage: build
cache:
paths:
- ./node_module
# キャッシュのアップロードのみを実行する
policy: push
dependencies¶
▼ dependencies¶
通常、dependencies
を指定せずにartifacts
を使用した場合に、全てのJobとファイルを継承する。
dependencies
を設定すれば、artifacts
が設定された特定のJobを指定し、そのJobのみをファイルを継承する。
stages:
- build
- deploy
foo_job:
stage: build
script:
- echo foo
artifacts:
paths:
- foo
bar_job:
stage: build
script:
- echo bar
artifacts:
paths:
- bar
baz_job:
stage: deploy
script:
- echo baz
# foo_jobのアーティファクトのみを継承する
dependencies:
- foo_job
image¶
▼ imageとは¶
Jobの実行コンテナを設定する。
foo_job:
stage: build
image:
name: alpine:1.0.0
entrypoint: ["sh"]
script:
- echo foo
▼ CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX
¶
CIでは、コンテナイメージのプルの頻度が高まるため、イメージレジストリ (例:DockerHub) によってはプル数の制限にひっかかってしまう。
イメージレジストリのパスのプレフィクスとしてCI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX
をつけると、コンテナイメージがGitLabのDependency Proxyを経由するようになる。
Dependency Proxyはコンテナイメージをキャッシュするため、毎回DockerHubにコンテナをプルしなくなり、プル数の制限にひっかかりにくくなる。
foo_job:
stage: build
image:
name: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/alpine:1.0.0
entrypoint: ["sh"]
script:
- echo foo
needs¶
▼ needsとは¶
Job間の依存関係を設定する。
同じステージ内に複数のJobがある場合に役立つ。
stages:
- build
foo_job:
stage: build
script:
- echo foo
# fooの後に、baz_jobと並行実行する
bar_job:
stage: build
needs:
- foo_job
script:
- echo bar
# fooの後に、bar_jobと並行実行する
baz_job:
stage: build
needs:
- foo_job
script:
- echo baz
parallel¶
▼ parallelとは¶
同じJobを複数並列実行する。
foo_job:
stage: build
parallel:
matrix:
- ENV:
- foo1
- foo2
- foo3
# foo1、foo2、foo3、を出力する異なるJobを並列実行する
script:
- echo ${ENV}
▼ アーティファクト依存関係¶
並列実行した各Jobに対して、アーティファクトの依存関係を設定できる。
stages:
- build
- deploy
foo_job:
stage: build
parallel:
matrix:
- ENV:
- foo1
- foo2
- foo3
# foo1、foo2、foo3、を出力する異なるJobを並列実行する
script:
- echo ${ENV}
baz_job:
stage: deploy
script:
- echo baz
# foo_jobのfoo1のアーティファクトのみを継承する
dependencies:
- "foo_job: [foo1]"
▼ Jobの依存関係¶
並列実行した各Jobに対して、Jobの依存関係を設定できる。
stages:
- build
- deploy
foo_job:
stage: build
parallel:
# foo1、foo2、foo3、を出力する異なるJobを並列実行する
matrix:
- ENV:
- foo1
- foo2
- foo3
script:
- echo ${ENV}
baz_job:
stage: deploy
script:
- echo baz
needs:
- job: foo_job
parallel:
matrix:
# foo_jobのfoo2に依存する
- ENV: foo2
rules¶
▼ rulesとは¶
Jobの発火条件を設定する。
複数の条件 (複数のif
キー、if
キーとchanges
キーの組み合わせ) を並べた場合、上から順番に条件を検証していくため、OR条件になる。
▼ if¶
条件に合致した場合のみ、Jobを発火する。
ブランチ名やタグを使用した発火を定義できる。
イベントの種類が設定されたCI_PIPELINE_SOURCE
変数を使用できる。
check_tag:
# OR条件
rules:
# mainブランチのみ
- if: $CI_COMMIT_BRANCH == 'main'
variables:
TAG_NAME: "main"
# hotfixから始まるブランチのみ
- if: $CI_COMMIT_BRANCH =~ /^hotfix.*$/
variables:
TAG_NAME: "hotfix-${CI_COMMIT_SHA}"
# 任意の名前のタグがついている場合のみ
- if: $CI_COMMIT_TAG
variables:
TAG_NAME: "$CI_COMMIT_TAG"
▼ changes¶
プッシュ時に、指定したファイルやディレクトリで差分があれば、Jobを発火する。
gemerate_template:
script:
- helm template . -f foo-values.yaml -f foo-secrets.yaml > foo.yaml
rules:
# webイベント (パイプライン実行ボタン) の場合
- if: $CI_PIPELINE_SOURCE == "web"
# ファイルやディレクトリ内に差分があった場合
- changes:
- template/**/*
- foo-values.yaml
services¶
▼ services¶
JobのCIの実行コンテナとは別のサービスコンテナを作成する。
両方のコンテナで使用するイメージのバーションは揃えるようにする。
foo_job:
# CIの実行環境
image: docker:19.03.0
# サービスコンテナ
services:
- name: docker:19.03.0-dind
▼ 複数のコンテナを同時に起動するため¶
Jobでアプリコンテナを動かし、DBコンテナを別に起動しておく場合、もう一つコンテナが必要になる。
これを回避するために使用する。
▼ TLSの無効化¶
GitLab CI上でDocker in Dockerを使用する場合、実行コンテナとサービスコンテナでセットアップが必要である。
variables:
# ドライバーの設定
DOCKER_DRIVER: "overlay2"
# ホストの設定
DOCKER_HOST: "tcp://docker:2375"
# TLSの無効化
DOCKER_TLS_CERTDIR: ""
foo_job:
# CIの実行環境
image: docker:19.03.0
# サービスコンテナ
services:
- name: docker:19.03.0-dind
# TLSの無効化
command: ["--tls=false"]
stage¶
▼ stage¶
Jobが所属するステージを設定する。
より前のステージ内のJobが全て成功しない限り、後続のJobを開始しない。
同じステージに所属するJobは、並行的に実行される。
stages:
- build
- test
- deploy
# ----------
# build
# ----------
foo_job:
stage: build
script:
- echo foo
bar_job:
stage: build
script:
- echo bar
# ----------
# test
# ----------
baz_job:
stage: test
# ----------
# deploy
# ----------
qux_job:
stage: deploy
script¶
▼ scriptとは¶
Jobで実行する処理を設定する。
foo_job:
stage: build
script:
- echo "Hello World"
trigger¶
Jobが発火した場合に、特定のアクションを実施する。
*例*
モノレポでGitLabCIを採用している場合に、 親の.gitlab-ci.yml
ファイルでディレクトリ配下の変更を検知し、子の.gitlab-ci.yml
ファイルを読み込む (include
) ようにする。
# 親の.gitlab-ci.yml
stage:
- build
foo:
stage: build
rules:
- if: $CI_COMMIT_BRANCH
changes:
- foo/*
- foo/**/*
trigger:
include: foo/.gitlab-ci.yml
strategy: depend
bar:
stage: build
rules:
# ディレクトリ配下の変更を検知する
- if: $CI_COMMIT_BRANCH
changes:
- foo/*
- foo/**/*
trigger:
# 各ディレクトリ配下に置いた子の.gitlab-ci.ymlファイルを読み込む
include: foo/.gitlab-ci.yml
strategy: depend
when¶
▼ whenとは¶
Jobを実行する条件を設定する。
▼ always¶
ワークフローのうちで、前のJobのステータスに関係なく、必ずJobを実行する
bar_job:
stage: build
script:
- echo bar
when: always
▼ manual¶
ワークフローのうちで、手動の場合にのみJobを実行する。
foo_job:
stage: build
script:
- echo foo
when: manual
▼ never¶
特定の条件の場合に、Jobを実行しない。
なお、when: never
のみの定義は意味がない。
baz_job:
stage: build
script:
- echo bar
# BAZ変数がfalseの場合は実行しない (never)
rules:
- if: $BAZ == 'false'
when: never