Jenkins からの移行のために今更だけど使ってみたメモ。
なお、うちの Gitlab はソースから入れていてデータベースも MySQL です。たまにしかバージョンアップしていないのでちょっと古いです(8.17.2
)。
参考
- https://docs.gitlab.com/ee/ci/
- 公式のドキュメント
- https://docs.gitlab.com/ee/ci/yaml/README.html
.gitlab-ci.yml
のリファレンス
- https://docs.gitlab.com/ee/ci/runners/README.html
- Runner のドキュメント
- https://docs.gitlab.com/runner/
- 公式の Runner の実装のドキュメント
- https://gitlab.com/gitlab-org/gitlab-ci-multi-runner
- ↑のリポジトリ
ざっくり
- Gitlab 8.0 からは Gitlab に統合されている
- Runner をどこかのサーバでセットアップする必要がある
- shell とか docker とかでビルドが実行される環境
- Gitlab と同じサーバじゃ無い方が良い
- リポジトリルートに
.gitlab-ci.yml
を追加する.gitlab-ci.yml
は Runner に何をさせるかを記述する
- どの Runner を使うかはプロジェクトごとに選択できる
- タグ付けでさらに細かく制御できる
Runner とは
ビルドを実行するための環境。公式の実装として gitlab-ci-multi-runner
があるけれども自前で実装することもできます、たぶん。
起動すると Gitlab に HTTP で常時接続して Gitlab からのビルドの通知を受け取ります。なので「Runner → Gitlab」の方向に HTTP で繋がるだけで良いです。
Runner には、特定のプロジェクトに固有のものと(Specific Runner)、すべてのプロジェクトで使用可能なものがあります(Shared Runner)。
Shared Runner は fair usage queue という方法でジョブをキューイングします。一方で Specific Runner は FIFO でキューイングされます。fair usage queue は、やたらたくさんビルドを要求するプロジェクトが原因で他のプロジェクトのビルドが滞るのを避けるためのアルゴリズムのようです。
Specific Runner は複数のプロジェクトで使いまわすこともできます。Shared Runner との違いはそれぞれのプロジェクトで個別に有効にする必要があるかどうかです。Shared Runner はデフォですべてのプロジェクトで有効になります。Specific Runner はプロジェクトの設定で選択しなければ有効になりません。Specific Runner は使い回しを禁止して特定のプロジェクトにロックすることもできます。
Shared Runner を登録するとき、普通は Runner に処理可能なジョブのタグを指定します。そうしないとすべてのジョブを実行しようとしてしまうので。もちろん Specific Runner でも特定の環境でだけ実行するジョブのためにタグを指定しても良い。
Shared Runner でジョブを実行する場合、同じ Runner で実行される他のプロジェクトのコードにアクセスできるので注意が必要。また、Runner のトークンが実行するコードで取得できるので、Runner のクローンを作成して誤ったジョブをサブミットできる??(Runner に間違ったビルドを支持できるということ? もしくは Runner を上書きできる? あるいは 任意の Runner が追加できてしまうということ?)
Runner のインストール
最新版だと今使ってる Gitlab とバージョンが合わなかったのでダウングレードします(最初からバージョン指定でインストールしても良い)。
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.rpm.sh | sudo bash sudo yum install gitlab-ci-multi-runner sudo yum downgrade gitlab-ci-multi-runner-1.11.4 rpm -ql gitlab-ci-multi-runner #=> /usr/bin/gitlab-ci-multi-runner #=> /usr/bin/gitlab-runner #=> /usr/share/gitlab-runner/clear-docker-cache #=> /usr/share/gitlab-runner/post-install #=> /usr/share/gitlab-runner/pre-remove
RPM に systemd のユニットファイルとかは含まれていませんが、インストール時に /usr/share/gitlab-runner/post-install
で生成されているようなのですぐ開始できます(というかインストールした時点で開始されているっぽいですが)。
sudo systemctl start gitlab-runner.service sudo systemctl status gitlab-runner.service sudo systemctl enable gitlab-runner.service
Runner を Gitlab に登録
特定のプロジェクト用に Runner を登録してみる。
プロジェクトの CI/CD Pipelines を開いて Specific Runners のとこの URL とトークンをメモって、下記のコマンドで登録します。
sudo gitlab-runner register \ --url http://gitlab.example.net/ci \ --registration-token "$gitlab_ci_token" \ --name ore-no-shell \ --executor shell
Enter 連打で登録できる。Executor で shell を指定しているのでビルドはこのホスト上の gitlab-runner
というユーザーを使ってそのままスクリプトが実行されます。
Docker の Runner も登録してみる。
sudo gitlab-runner register \ --url http://gitlab.example.net/ci \ --registration-token "$gitlab_ci_token" \ --name ore-no-docker \ --tag-list docker \ --executor docker \ --docker-image alpine:latest
タグを指定したのでこの Runner では docker
というタグが付けられたジョブだけが実行されます。また、指定している Docker image は .gitlab-ci.yml
で指定されなかったときのデフォルトになります。.gitlab-ci.yml
で指定できるイメージのホワイトリストとかも指定できるようですね。
.gitlab-ci.yml
.gitlab-ci.yml
をリポジトリルートに追加する。このファイルに Runner が何をするか記述します。
image: php:alpine before_script: - uname -n - id - pwd - which php - php -v job_shell: script: - echo "this is shell" job_docker: script: - echo "this is docker" tags: - docker
before_script
はすべてのジョブに先立って実行されます。job_shell
や job_docker
がジョブで任意の名前を付けることができる。ジョブの子要素には script
が必須。
このファイルをリポジトリに追加してプッシュすると次のようにビルドが実行されます(見やすくするために少し編集)。
job_shell
Running with gitlab-ci-multi-runner 1.11.4 (7e2b646) on ore-no-shell (9b476e4f) WARNING: image is not supported by selected executor and shell Using Shell executor... Running on ore.example.com... Fetching changes... HEAD is now at e20b44a .gitlab-ci.yml From http://gitlab.example.net/ore/testing e20b44a..fc613e6 master -> origin/master Checking out fc613e6d as master... Skipping Git submodules setup $ uname -n ore.example.com $ id uid=983(gitlab-runner) gid=981(gitlab-runner) groups=981(gitlab-runner) $ pwd /home/gitlab-runner/builds/9b476e4f/0/ore/testing $ which php /usr/bin/php $ php -v PHP 7.1.6 (cli) (built: Jun 7 2017 12:15:54) ( NTS ) Copyright (c) 1997-2017 The PHP Group Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies with Zend OPcache v7.1.6, Copyright (c) 1999-2017, by Zend Technologies with Xdebug v2.5.5, Copyright (c) 2002-2017, by Derick Rethans $ echo "this is shell" this is shell Job succeeded
job_docker
Running with gitlab-ci-multi-runner 1.11.4 (7e2b646) on ore-no-docker (adea055c) Using Docker executor with image php:alpine ... Pulling docker image php:alpine ... Running on runner-adea055c-project-42-concurrent-0 via ore.example.com... Cloning repository... Cloning into '/builds/ore/testing'... Checking out fc613e6d as master... Skipping Git submodules setup $ uname -n runner-adea055c-project-42-concurrent-0 $ id uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video) $ pwd /builds/ore/testing $ which php /usr/local/bin/php $ php -v PHP 7.1.6 (cli) (built: Jun 28 2017 20:57:42) ( NTS ) Copyright (c) 1997-2017 The PHP Group Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies $ echo "this is docker" this is docker Job succeeded
CI Lint
Gitlab の Pipelines のページから CI Lint に移動できます。その画面で .gitlab-ci.yml
の検証ができる。
Docker cache
Docker でジョブを実行するとき、composer install
で毎回パッケージがダウンロードされたのではビルドが遅くなりすぎて辛いので、ビルドの終了時に一部のディレクトリをキャッシュして次回のビルドで使うようにできます。
.gitlab-ci.yml
でキャッシュするディレクトリを指定します。
image: php:alpine phpunit: script: - php composer.phar install --prefer-dist --no-progress --ansi --dev tags: - docker cache: paths: - vendor/
ビルドの終了時に指定していたディレクトリがアーカイブされて保存されます。次回の実行時はそのアーカイブが展開してからビルドが開始します。
ちなみにアーカイブは自動的に作成された Docker Volume に保存されています。docker volume ls
で見れるアレです。
下記のように登録時に --docker-cache-dir
を指定するとホストのディレクトリが使われるので Docker Volume がぼこぼこ作成されることはなくなります。
sudo gitlab-runner unregister \ --url http://gitlab.example.net/ci \ --name ore-no-docker sudo gitlab-runner register \ --url http://gitlab.example.net/ci \ --registration-token "$gitlab_ci_token" \ --name ore-no-docker \ --tag-list docker \ --executor docker \ --docker-image alpine:latest \ --docker-cache-dir "/srv/gitlab-runner/cache/"
特定の処理だけ実行する Runner を作る
gitlab-runner
の --pre-build-script
でスクリプトを指定すると、この Runner に固有のビルド前の処理が実行できます。
さらに、指定したスクリプトはビルドのジョブの script
と繋げられて1つのスクリプトとして実行されるので、次のように exec
とかしてやるとジョブの script
が実行されずにジョブが終了します。
sudo gitlab-runner register \ --url http://gitlab.example.net/ci \ --registration-token "$gitlab_ci_token" \ --name ore-no-task \ --tag-list ore-no-task \ --executor shell \ --builds-dir /srv/gitlab-runner/builds \ --pre-build-script 'exec /opt/gitlab-runner/pre-build-script.sh' sudo mkdir -p /opt/gitlab-runner/ cat <<'EOS'| sudo tee /opt/gitlab-runner/pre-build-script.sh #!/bin/bash set -x id pwd echo "CI_PROJECT_PATH=$CI_PROJECT_PATH" git show -s EOS sudo chmod +x /opt/gitlab-runner/pre-build-script.sh
こんな感じに実行されます。.gitlab-ci.yml
の script
に何を書いていてもここで止まります。
Running with gitlab-ci-multi-runner 1.10.8 (2c34bd0) Using Shell executor... Running on ore.example.com... Fetching changes... HEAD is now at e20b44a .gitlab-ci.yml Checking out e20b44a6 as master... Skipping Git submodules setup $ exec /opt/gitlab-runner/pre-build-script.sh + id uid=983(gitlab-runner) gid=981(gitlab-runner) groups=981(gitlab-runner) + pwd /srv/gitlab-runner/builds/ce74c254/0/ore/testing + echo CI_PROJECT_PATH=ore/testing CI_PROJECT_PATH=ore/testing + git show -s commit e20b44a678298d9280ce0f6e90128512c078a955 Author: ore <ore@example.com> Date: Fri Jun 30 12:22:44 2017 +0900 .gitlab-ci.yml Build succeeded
複数のプロジェクトで横断的に特定のホストである決まった処理を実行するために使えそうです。
Jenkins を置き換える
今は Jenkins で下記のような感じで CI/CD してます。
- 開発環境を Jenkins Slave としてセットアップ
- 世間ではステージングと呼ばれるかも
- リポジトリにプッシュされたらテストを実行
- composer install とか
- migration とか
- phpunit とか
- php-cs-fixer とか
- phan とかもやりたい
- master ブランチでテストが通れば開発環境の公開ディレクトリへデプロイ
- Jenkins のワーキングディレクトリを公開ディレクトリに指定してる
- ので Jenkins がファイルを撒くまでやってくれる(チェックアウトするだけ)
- マイグレーションとかサービスのリスタートとかだけ後処理でやってる
- さらに master ブランチを Redmine にチェックアウトして fetch changesets で反映
- Redmine も Jenkins Slave として登録している
- トピックブランチは Redmine に反映したくない
これらを Jenkins の WebUI で設定するのは流石にしんどいので Jenkins DSL で設定しているのですが・・それはそれでツラミあります。あと、現状のビルドの一部を並列化して両方終わったらデプロイを実行、とかが Jenkins DSL だとかなりつらいです。Jenkins Pipeline で多少マシになっているっぽいですが。。。
Gitlab CI ならこんな感じでできそう(phan はそれっぽい Docker Image を作った)。
before_script: - curl -fsSL https://getcomposer.org/download/1.4.2/composer.phar > composer.phar phan: image: ngyuki/php-phan script: - php composer.phar install --prefer-dist --no-progress --ansi --no-dev - phan --version - phan -l src/ -l vendor/ -3 vendor/ tags: - docker cache: paths: - vendor/ cs-fixer: image: php:alpine script: - php composer.phar install --prefer-dist --no-progress --ansi --dev - vendor/bin/php-cs-fixer fix --dry-run --diff --ansi -vvv -- src/ tags: - docker cache: key: vendor paths: - vendor/ phpunit: image: php:alpine script: - php composer.phar install --prefer-dist --no-progress --ansi --dev - vendor/bin/phpunit tags: - docker cache: key: vendor paths: - vendor/ deploy: stage: deploy script: - php composer.phar install --prefer-dist --no-progress --ansi --no-dev --optimize-autoloader - ln -sfn -- "$PWD" /opt/myapp/current only: - master tags: - dev redmine: stage: deploy script: | set -eu mkdir -p -- "/srv/gitlab-runner/repos/${CI_PROJECT_PATH%/*}" ln -sfn -- "$PWD/.git" "/srv/gitlab-runner/repos/${CI_PROJECT_PATH}" git branch --force -- "$CI_BUILD_REF_NAME" "$CI_BUILD_REF" git symbolic-ref HEAD "refs/heads/$CI_BUILD_REF_NAME" curl -kfs "https://redmine.example.net/sys/fetch_changesets?id=ore" only: - master tags: - redmine
最後の redmine のジョブは gitlab-runner
の --pre-build-script
を使って Shared Runner でやると良さそう。
特定のユーザーで Runner を実行する
Runner がどのユーザーで実行されるかは gitlab-runner run
の引数で決まるのだけど、これは systemd のユニットファイルで指定されている。
cat /etc/systemd/system/gitlab-runner.service | grep ExecStart #=> ExecStart=/usr/bin/gitlab-ci-multi-runner "run" ... "--user" "gitlab-runner"
このファイルは gitlab-runner install
で生成されるので、下記のようにすれば別のユーザーで実行させることができる。
gitlab-runner uninstall gitlab-runner install -d /tmp -u ore systemctl restart gitlab-runner
なお gitlab-runner register
で登録する Runner ごとには変更できないっぽい。