DockerのGitLab Runnerを使ってローカルでCI/CDを動かす

## 概要
ローカルのDockerコンテナ上にGitLabにてCIを動かすためのセッティングをします。
単純にGitlab Runnerの使用方法の記事はわかりやすいものがたくさんありますが、docker for macだと特殊なハマりポイントもあるので改めてまとめました。
(そもそもGitlabをローカルで使うゴリゴリ使うことはあまりないかもしれないですが）

## 今回のゴール
GitLabでコミットを検知したらDockerコンテナが起動し、コンテナの中でphpファイルの構文チェックが走るようにする

## 環境
||version|
|:-- |:--|
| ホストOS | MacOS 10.14.6 |
| Docker | 19.03.5, build 633a0ea |
| gitlab-ce Image | gitlab/gitlab-ce:12.6.4-ce.0 |
| gitlab-runner Image|gitlab/gitlab-runner:v12.7.1 |
※Imageのバージョンは2020/2/10時点でのlatest

## GitLab Runnerとは
Gitlabと連携し、プロジェクトのコミットを検知して動作するジョブの実行環境です。
GitLab上にはGitlab CI/CDという管理ツールがあり、ジョブの実行ログなどを管理できます。
[公式ドキュメント](https://docs.gitlab.com/runner/)

#### Runnerの種類
Runnerには共有範囲が3種類あります。

* Shared ： 全プロジェクトで共有
* Group ： 同一グループ内で共有
* Specific ： プロジェクト占有

Runner自体はあくまで実行環境なので、ジョブの中身はプロジェクト毎に記載することになります。
特化した環境ならSpecific、汎用的ならShared、といった使い分けになると思われます。

#### Executor
ジョブの実行方法(Executor)にはいくつか種類があり、ジョブ毎に設定することができます。
簡単なものであればサーバ内で実行する`Shell Executor`、実行環境を気にするジョブの場合はコンテナを個別に起動させる`Docker Executor`を用いるなど、臨機応変に使い分けることができます。
([Executor一覧はこちら](https://docs.gitlab.com/runner/executors/README.html))

## Dockerコンテナの構築
まずはdocker-composeを用いてコンテナを作ります。
今回は`172.20.10.1/24`のネットワーク内にgitlab本体とrunnerのコンテナを作ります。

```yml
version: '3.5'

services:
  gitlab:
    container_name: gitlab
    image: gitlab/gitlab-ce:latest
    hostname: 'gitlab-local'
    restart: always
    networks:
      gitlab_net:
       ipv4_address: 172.20.10.2
    environment: 
      GITLAB_OMNIBUS_CONFIG : |
        external_url 'http://gitlab'
        unicorn['socket'] = '/opt/gitlab/var/unicorn/gitlab.socket'
        gitaly['internal_socket_dir'] = '/var/opt/gitlab/gitaly/soc'
    ports:
      - '1080:80'
      - '1443:443'
      - '1024:22'
    volumes: 
      - './gitlab/config:/etc/gitlab'
      - './gitlab/logs:/var/log/gitlab'
      - './gitlab/data:/var/opt/gitlab'
  gitlab_runner:
    container_name: gitlab_runner
    image: 'gitlab/gitlab-runner:latest' 
    hostname: gitlab-runner
    restart: always
    networks: 
      gitlab_net:
        ipv4_address: 172.20.10.3
    volumes:
      - ./gitlab-runner/config:/etc/gitlab-runner
      - /var/run/docker.sock:/var/run/docker.sock
networks: 
  gitlab_net:
    name: gitlab_net
    driver: bridge
    ipam: 
      driver: default
      config:
        - subnet: 172.20.10.1/24
```

### コンテナ作成上の注意
* `GITLAB_OMNIBUS_CONFIG`の下2つの設定はdocker for macでgitlabを使う場合に起こる問題への対策です。[詳しくはこちら](https://qiita.com/tk_ppp/items/1b54e8c43d7fd1cd1809)
* docker-composeのバージョン3.5以降の記法でしかnetworksに固定名称を与えられません。([公式のリリース情報](https://docs.docker.com/compose/compose-file/compose-versioning/#version-35))

## Runnerの登録
コンテナを立ち上げてgitlab自体にアクセスできたら、続いてRunnerの登録をしていきます。
なお、今回はプロジェクト占有型のSpecificで設定を進めます。

#### トークンの取得
登録にはGitlab CIのトークンが必要になります。
「対象のプロジェクト」>「Settings」>「CI/CD」と進み、「Runner」の項目を開くと以下のような画面が表示されるので、トークンをコピーしておきます。
<img width="555" alt="スクリーンショット 2020-02-22 15.49.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86185/e0055e6d-f65f-2919-866b-92828381f8ef.png">

#### Runnerのセットアップ
トークンを取得できたらRunnerにトークンをセットします。
これによって、Gitlab本体とRunnerが連携され、ジョブを実行できるようになります。
なお、今回はphpのインストールが必要ということもあり、`Docker Executor`を採用します。

セットアップにはRunnerのコンテナを立ち上げた状態で以下のコマンドを実行します。

```bash
docker exec -it gitlab_runner gitlab-runner register \
 --url "http://gitlab" \
 --registration-token "コピーしておいたトークン" \
 --name "docker_php" \
 --tag-list "docker_php" \ 
 --executor "docker" \
 --docker-image "php:7.4.3-apache" \
 --docker-network-mode "gitlab_net" \
 --docker-pull-policy "if-not-present"
```
ポイントは、必ず`docker-network-mode`でGitlab本体と同じネットワークを指定することです。
docker for macの場合、私の知る限りSocksプロキシなどを使わないとホストOSからドメインで当たれないので、こうしないとジョブ実行時に立ち上がるコンテナからGitlab本体にアクセスができず詰みます。

このコマンドを実行すると、対話形式で各設定項目について確認されますが、オプションで指定してあればそれがデフォルトになるのでEnterを連打していればOKです。
(逆にオプション1つもつけず対話形式で設定を入力する方法もあります）

設定が成功すると先ほどの画面に有効なRunnerとして表示されます。

<img width="500" alt="スクリーンショット 2020-02-23 0.41.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86185/11078341-8d6a-e0ff-6f2e-2653675f822c.png">

## ジョブの作成
実行環境となるRunnerの登録が完了したので、いよいよ実際に実行するジョブを作成します。
`.gitlab-ci.yml`という名前のファイルを実際に使用したいプロジェクト直下にを配置することでジョブの定義を行います。

なお、プロジェクトのTOPに表示される`Set up CI/CD`を押すと`.gitlab-ci.yml`の作成画面が表示されます。
<img width="500" alt="スクリーンショット 2020-02-23 22.55.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86185/7541851c-5164-0e44-a770-a3b883c699a6.png">

いくつかテンプレートも用意されていますが、今回はただコマンドラインで構文チェックできればいいので手で作ります。

```yml
lint:
    tags:
        - docker_php
    script: 
        - pwd
        - echo 'start lint'
        - php -l ./test.php

```

`lint`はジョブの名前で、自由に命名することができますし、複数のジョブを定義することもできます。
`script`にはジョブの中で実行するコマンドを記載します。今回はほぼlintを走らせているだけです。
`tags`にはRunnerの登録時に記入したタグを記載します。これによって、1つのプロジェクトでもジョブによって実行環境を変えることができます。
※もっと色々設定できるので興味ある方は[公式ドキュメント](https://docs.gitlab.com/ee/ci/yaml/README.html#image-and-services)へどうぞ

## 実行結果の確認
以上でジョブの設定は完了しました。
肝心な`test.php`を用意していなかったので、以下のファイルをpushして実行結果を確認していきます。

```php
<?php
echo 'Hello World!';
```

実行結果は「CI/CD」>「Pipeline」または「CI/CD」>「Jobs」から確認できます。
<img width="555" alt="スクリーンショット 2020-02-23 23.56.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86185/a3f4937c-523e-1679-8257-b031c763183b.png">

ここで今回の結果を選択し、さらにジョブを選択すると実行内容の詳細を確認できます。
<img width="300" alt="スクリーンショット 2020-02-24 0.00.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86185/8f27e5cf-e082-3db1-9d10-1873ee408f7b.png">
<img width="703" alt="スクリーンショット 2020-02-24 0.03.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86185/44d93a3f-fc17-7c92-17e4-c06028fa3ea5.png">

今回は問題のない記述だったので普通に成功しています。
次に、問題があるファイルをpushしてみます。

```php
<?php
echoooo 'Hello World!';
```
すると、先ほどは`Passed`だったのが`Failed`になりました。
<img width="810" alt="スクリーンショット 2020-02-24 0.09.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86185/c7d9a36a-f335-5810-9440-77463f3f83a8.png">

さらに実行の詳細をみてみます。
<img width="500" alt="スクリーンショット 2020-02-24 0.12.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86185/aa1ae721-c76b-a153-4ab5-48b880954d53.png">

しっかりシンタックスエラーが出ています。
これで問題のある記述がある場合は簡単に検知することができますし、どこに問題があるのかもわかります。

## 感想とまとめ
mac特有の問題で何度かくじけかけましたが、なんとか無事lintを走らせることができました。

実用的にするにはもっと`.gitlab-ci.yml`を作りこむ必要がありますが、ひとまずここで切ります。
今後はユニットテストやデプロイ的なことも含め、複数のジョブを走らせたり色々試してみたいと思います。

## 参考
* [【公式】GitLab Runner Docs](https://docs.gitlab.com/runner/)
* [【公式】GitLab CI/CD Pipeline Configuration Reference(.gitlab-ci.ymlの書き方など)](https://docs.gitlab.com/ee/ci/yaml/)
* [初期設定に関するGitLabのissue](https://gitlab.com/gitlab-org/gitlab-runner/issues/1285)
* [GitLab RunnerでCI/CDしてみる(前編)](https://dev.classmethod.jp/ci/gitlab-runner-ci-cd-1/)
* [CI/CDのためにGitLab RunnerをDockerで動かす準備～EHW2018「環境整備⑧」](https://qiita.com/hrkt/items/111bada83225a1864c9b)
* [GitLab-CIでHerokuにデプロイするまでの奮闘記](https://qiita.com/teracy55/items/2f9ecaafc0a042c1f6df)