GitLabのCI/CD環境作成からパイプラインを動かすまでやってみた

## やりたかったこと
GitLabのCI/CD環境で、自動ビルドと自動テストを実施出来るような環境を構築してみました。
パイプラインを実行するためのランナーについては、自前で用意したランナー上で実行されるような環境を作ります。

## 前提
* GitLabはオンプレに構築済み
* CI実行対象のリポジトリは用意済み
* ランナーはdockerコンテナとして構築する
* Rustで記載した簡単なコードに対して、ビルドとテストを実行する

今回用意したコードはこんな感じです。
とりあえずビルドとテストが実行出来れば良かったので簡単なものを用意しています。
```rust:main.rs
fn sum(a: u32, b: u32) -> u32 {
    return a + b;
}

fn main() {
    let c: u32 = sum(2, 3);
    println!("result: {}", c);
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_sum_1() {
        assert_eq!(super::sum(2, 3), 5);
        assert_eq!(super::sum(2, 5), 7);
    }

    #[test]
    fn test_sum_2() {
        assert_eq!(super::sum(1, 10), 11);
        assert_eq!(super::sum(0, 3), 3);
    }
}
```

## Specific Runnerの作成
Specific Runnerは、Github Actionsで言うところのSelf-hosted Runnerのようなものです。
プロジェクト単位で専有できるランナーを自前で構築して使用できます。
### GitLab側のRunner設定
GitLabの「設定」⇒「CI/CD」を選択し、「Runner」を展開することで対象プロジェクトのRunner設定を確認できます。

今回は「共有 Runner」は使用しないのでオフにします。
また、「Specific runners」に記載されているURLとtokenは後のRunner登録の手順で必要となるので、メモしておいてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2744609/5e0d9e24-7fad-5f44-4d2e-22dc7c35a432.png)

### Runner用のDockerコンテナ起動
下記コマンドでランナー用のコンテナを起動します。
[公式マニュアル](https://docs.gitlab.com/runner/install/docker.html)の記載を参考にしています。
```bash:Runner用コンテナ起動
docker run -it --name gitlab-runner --restart=always \
    --hostname gitlab-runner \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /srv/gitlab-runner/config:/etc/gitlab-runner \
    --tty \
    gitlab/gitlab-runner:latest register
```

### Runnerの登録
先述のコマンドでコンテナを起動すると、ランナーをGitLabのリポジトリに登録するための処理が始まります。
下記の順番で設定値を求められるので、入力します。
1. Enter the GitLab instance URL
    *  GitLabのURLを入力します。GitLabの設定ページに記載されているものをコピペでOKです。
2. Enter the registration token
    * 登録用トークンを入力します。 ここもGitLabの設定ページに記載されているものをコピペします。
3. Enter a description for the runner (任意)
    * このランナーに対する説明文です。今回は空欄とします。
4. Enter tags for the runner (任意)
    * タグを付与することで、ジョブの実行対象となるランナーをタグで指定することが可能です。今回は特にタグは付与しないので空欄です。
5. Enter optional maintenance note for the runner (任意)
    * このランナーに対するメンテナンスノートです。今回は空欄とします。
6. Enter an executor
    * runner executorを指定します。今回は```docker```を指定します。
7. Enter the default Docker image
    * デフォルトで使用されるdockerイメージを指定します。今回はRust向けのため、```rust:latest```で指定します。

ランナーの登録に成功すると、「Runner registered successfully.」とメッセージが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2744609/19351da1-3545-4cb5-874a-2131d4382d4c.png)

GitLabの設定ページの「利用可能なSpecific Runner」に作成したランナーが登録されていれば完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2744609/58fafb12-c694-db99-7cac-a95065e97da0.png)

#### トラブルシューティング1: Runner登録中に「dial tcp: lookup xxx on x.x.x.x:53: no such host」のエラーが発生
自分の環境では、外向きのホスト名を解決するための設定をしていなかったため、内向きのIPアドレスを設定する必要がありました。
Enter the GitLab instance URLで入力するURLに直接IPアドレスを指定することで解決しました。

#### トラブルシューティング2: 作成したランナーで「runner has never contacted this instance」という警告が発生
コンテナ上で```gitlab-runner verify```コマンドを実行することで解決しました。

## パイプラインの作成
### .gitlab-ci.ymlの作成
```.gitlab-ci.yml```を作成し、プロジェクト直下に配置します。
今回はRustで作成したアプリに対して、自動ビルドと自動テストを実施するように定義しています。
また、ビルドで作成された実行ファイルは、ブラウザからartifactsとしてダウンロードできるようにしています。
```yaml:.gitlab-ci.yml
image: rust:latest

stages:
  - build
  - test

before_script:
  - echo "this is test script"

build:
  stage: build
  script:
    - echo "this is build"
    - cargo build --release
  artifacts:
    paths:
      - target/release/rust_cicd

test:
  stage: test
  script:
    - echo "this is test"
    - cargo test
```

実際にリポジトリに登録するディレクトリ構成としては以下のような形になります。
（と言っても、```cargo new```で作成したプロジェクトに```.gitlab-ci.yml```を追加しただけです。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2744609/eafad29d-800a-9ae0-95f5-6e33be95f871.png)

## パイプラインの実行
### パイプラインが実行されるタイミング
作成した```.gitlab-ci.yml```をリモートリポジトリにpushすることで、CIが実行されるようになります。
以降は、対象ブランチへのpushが実行されるたびに、CIが実行されます。

### 結果確認
プロジェクトの「CI/CD」⇒「パイプライン」で過去に実行されたCIの結果を確認できます。
ステータスが成功になっていれば、CIで定義されたジョブが全て成功しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2744609/842e8183-733e-a539-b6cc-b1daa330af90.png)
各ステージを選択することで、そのジョブの実行ログを確認することが出来ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2744609/a316147d-e982-5f08-31d0-506181729a79.png)
作成したテストケースが全てパスしているので、ジョブのステータスも成功となっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2744609/82d791b0-c198-fee2-6599-222d5cafa7be.png)
また、パイプラインの右側のボタンを選択することで、artifactsをダウンロードできます。
ビルド時に作成される実行ファイルをartifactsとして登録するように設定しているため、ダウンロードされるフォルダ内には実行ファイルが含まれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2744609/fe7efa58-7144-4268-41b9-ce12386009f0.png)

#### トラブルシューティング1:「Getting source from Git repository」の段階で「Couldn't resolve host」のエラーが発生
GitLabの内向きのIPアドレスを指定するように、ランナーの設定を変更してやる必要がありました。
ランナー用コンテナに入り、```/etc/gitlab-runner/config.toml```を編集します。
* ```clone_url```に内向きのIPアドレスを設定
* ```extra_hosts```を設定
```bash:設定例
[[runners]]
  name = "Test runner for rust-cicd-test"
  url = "http://192.168.1.121:8080/"
  id = 2
  token = "xxxxxxxxxxxxxxxxxxxxx"
  token_obtained_at = 2023-05-02T09:41:41Z
  token_expires_at = 0001-01-01T00:00:00Z
  executor = "docker"
  clone_url = "http://192.168.1.121:8080"
  [runners.docker]
    tls_verify = false
    image = "ubuntu:latest"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache"]
    extra_hosts = ["example.com:192.168.1.121"]
    shm_size = 0
```

## 参考資料
* [Run GitLab Runner in a container](https://docs.gitlab.com/runner/install/docker.html)
* [GitLabのCI/CDで使用するgitlab-runnerのインストールもろもろ](https://qiita.com/tttol/items/6f9fd1c1522bf969b8f4)
* [GitLab CI/CD パイプライン設定リファレンス](https://gitlab-docs.creationline.com/ee/ci/yaml/)
* [GitLab CI Runnerの構築](https://blog.techlab-xe.net/post-5953/)