GitLab CI
GitLabから他のツールに連携して、ビルド、テスト、デプロイなどのCI連携ができる機能です。
使ってみると意外と色んなことが出来て面白そうなので、作り方をメモしておきます。
今回の構成と動作の流れ
バージョンなど
OS...Debian 9
GitLab...12.6
GitLab Runner...12.6
Ansible...2.10
流れ
- Debian_1にDockerがインストールされていて、GitLabがコンテナで動作している。ここにレポジトリがあり、ソースが保存される。
- 別のDebian_2にGitLab Runner(後述)とAnsibleがインストールされている。
- Ansibleはさらに別のDebian_3を構築する。
- pushやmergeなどのタイミングでGitLab本体がGitLab runnerに通知を送る
- Runnerは実行条件に合致していたら動作を開始する
- Runnerはまずgit cloneコマンドでレポジトリの内容をローカルにコピーして、そのレポジトリに
cd
する - Runnerはそのノード上で
ansible-playbook
コマンドを実行する - Ansibleは普通に実行した時と同じようにdebian_3を構築する
- 2のところの実行条件と、4で実行するコマンドはジョブの定義として作ってあげる必要があります。
- また、5のところでAnsibleがちゃんとDebian_3を構築できるようにinventryを設定してあげる必要があります。
環境変数とかを使ってうまくやってください。
GitLab Runnerのセットアップ
上の図でDebian_2にあるGitLab Runnerをインストールします。
Runnerの種類
GitLab Runnerには大きくShared RunnerとSpecific Runnerがあります。
自分もよくわかってないので、詳しくはここを見てください。
Shared Runner
複数のプロジェクトで共通して利用できるRunner。
いつどれが使われるかはRunnerとCIジョブに付けられたタグで決まります。
Specific Runner
特定のプロジェクトでのみ利用されるRunner
Runnnerのインストール
GitLabの公式サイトから.deb
のバイナリを落としてくることができます。
Debian以外にもRHEL, Windows, Mac, FreeBSDなど複数に対応します。
これだけでインストールが完了します。
root@debian:~# dpkg -i gitlab-runner_amd64.deb
Runnerの初期設定
Runner側でコマンドをいくつか打つことで、GitLabにRunnerとして登録することができます。
root@debian:~# gitlab-runner register
Runtime platform arch=amd64 os=linux pid=4496 revision=1b659122 version=12.8.0
Running in system-mode.
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
http://10.91.77.8/ #GitLabサーバーのURLを入力
Please enter the gitlab-ci token for this runner:
dx5bFN4PJQ9XNUj_Vxbf #GitLabのadmin画面から取得できるトークンを入力
Please enter the gitlab-ci description for this runner:
[debian]: Ansible runner #このRunnerの説明を入力
Please enter the gitlab-ci tags for this runner (comma separated):
ansible #コンマ区切りでタグを入力
Registering runner... succeeded runner=dx5bFN4P
Please enter the executor: docker+machine, docker-ssh+machine, kubernetes, docker, docker-ssh, shell, virtualbox, custom, parallels, ssh:
shell #ジョブの実行方法を入力
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
ジョブの実行方法はローカルでシェルを実行する、SSHでリモートを操作する、Dockerコンテナを立ち上げるなど複数から選べます。
.gitlab-ci.yml
の作成
環境が完成したら実際にCIを実行する定義.gitlab-ci.yml
を作ります。
このファイルはレポジトリのルートに置いておき、他のソースと同じようにバージョン管理されます。
今回はレポジトリに登録されたAnsible playbookを自動実行するように以下のようなYAMLを書きました。
詳細な文法などはGitLabの公式サイトを見てください。
# ステージ定義
stages:
- test
- deploy
- result
# Pushするたびに文法チェックをする
sanity_check:
stage: test
script:
- "ansible-playbook site.yml --syntax-check"
only:
- push
tags:
- ansible
# MRを作成したらDRY RUNで実行し、エラーにならないかを確認
run_playbook_dry:
stage: test
script:
- ansible-playbook -i ansible_hosts.ini site.yml --check
only:
- merge_requests
tags:
- ansible
# develブランチにマージされたタイミングで自動デプロイ
run_playbook:
stage: deploy
script:
- ansible-playbook -i ansible_hosts.ini site.yml
only:
- devel
tags:
- ansible
# ジョブが失敗したらMRにコメントを残す
send_failure_message:
stage: result
script:
- 'MR_IID=$(curl --request GET --header "Private-Token: $API_PRIVATE_TOKEN" "http://10.91.77.8/api/v4/projects/$CI_PROJECT_ID/repository/commits/$CI_COMMIT_SHA/merge_requests" --insecure | jq --raw-output ".[0].iid")'
- 'PROJECT_URL=$(curl --request GET --header "Private-Token: $API_PRIVATE_TOKEN" "http://10.91.77.8/api/v4/projects/$CI_PROJECT_ID" --insecure | jq --raw-output ".web_url")'
- 'curl --request POST --header "Private-Token: $API_PRIVATE_TOKEN" --data "body=CIジョブに失敗しました。<br><a href=$PROJECT_URL/pipelines>パイプラインの詳細</a>から内容を確認してください。" http://10.91.77.8/api/v4/projects/$CI_PROJECT_ID/merge_requests/$MR_IID/notes --insecure'
when: on_failure
only:
- merge_requests
tags:
- ansible
まずGitLabから対象のサーバーにレポジトリがgit clone
されます。
前述の通りクローンした後はそのディレクトリにcd
されるので、scriptブロックに記載するコマンドはすべてレポジトリのルートから見える相対パスで書きます。
ジョブ
YAMLの一番上の階層に書いてあるもの(予約語以外のもの)がジョブで、この単位でスクリプトや実行条件を指定します。
上のスクリプトでいうとsanity_test
やrun_playbook_dry
とかsend_failure_message
ですね。
stages
順序立ててジョブを実行する必要がある場合、stagesを定義してジョブを所属させると順番を決めて実行できます。
同じstageで複数のジョブが定義されている場合はパラレルで実行されます。
上のでいうと、まずtest
に所属するジョブが実行され、次にdeploy
、終わったらresult
が動きます。
only,except
ジョブごとに実行条件を指定できます。(exceptは除外条件)
push
はpushした時、merge_request
はMRを作成した時、ブランチ名を書くとそのブランチが更新された時です。
今回は実際にplaybookを動かすところをonly: devel
にしているので、devel
ブランチの中身が更新された時だけPlaybookが適用されます。
tag
Runnerのタグを指定します。
ここに指定したRunnerでジョブが実行されます。
APIを使ってコメント投稿
ジョブsend_failure_message
ではジョブに失敗した時という条件でMR画面にコメントを書き込んでいます。
script
に書かれてる内容は普通にRunnerがインストールされているOS上でコマンドを打ってるのと同じなので、GitLab APIを使ってコメント残したり色々できます。
$API_PRIVATE_TOKEN
というのはgitlab-runnerのconfig.toml
内で定義した環境変数的なやつです。変数の中身はGitLabのWeb画面から取ってきたbot用アカウントのプライベートトークンです。
実際に試す
Push
ローカルからpushすると、レポジトリの一番上に緑のチェックがつきます。
YAMLの文法に問題なかったということがわかります。
MR作成
MRを作成するとAnsibleのDRY RUNが走り、不具合が起きないかをチェックできます。
成功したらさっきと同じように緑のチェックが付きます。
クリックしないと具体的に何のジョブが実行されたのかわからないのは難点ですね。
マージ
マージされると実際にPlaybookが実行され、サーバーに構成が反映されます。
上の画像の下にもう一つチェックが付きます。
参考:詰まったところ
よくわからんエラーで詰まったのでメモしておきます。
エラーはコピーしてなかったので後からネットで拾ってきました。
HTTP 403
Running with gitlab-ci-multi-runner 9.4.2 (6d06f2ec)
on host-vm (a126d8fa)
Using SSH executor...
Running on host-vm via host-vm...
Cloning repository...
Cloning into 'builds/a126d8fa/0/project/appName'...
fatal: unable to access 'https://gitlab-ci-token:xxxxxxxxxxxxxxxxxxxx@gitlab.my-company.com/project/appName.git/': The requested URL returned error: 403
ERROR: Job failed: Process exited with: 1. Reason was: ()
→ユーザー権限の問題でした。権限振り直したら直りました。
git fetch-packのエラー
Running with gitlab-runner 11.9.2 (fa86510e)
on stage-test yMEso5rx
Using SSH executor...
Running on d1.XXXXXX.com via d1.XXXXXX.com...
warning: templates not found builds/yMEso5rx/0/kudja/postel-deluxe.tmp/git-template
Reinitialized existing Git repository in /home/qapd/builds/yMEso5rx/0/XXXX/XXXXXX/.git/
Clean repository
Fetching changes with git depth set to 10...
fatal: remote origin already exists.
fatal: git fetch-pack: expected shallow list
ERROR: Job failed: Process exited with: 1. Reason was: ()
→Runnerに入ってるGitのバージョンを上げたら直りました。