GitLab CI/CDを並列(Runnerを複数同時)実行できるようにする(共有キャッシュも設定)

GitLabのCI/CDを並列実行(Runnerを複数同時に実行)できるようにする手順のまとめとなります。

やることとしては、
- パイプラインのJOBを並列実行可能にする
- 並列実行した際にもキャッシュが利用できるようにする
- キャッシュにはGCS(Google Cloud Storage)を利用する

GitLab RunnerでのCI/CD環境の構築手順については下記を参照ください。

https://qiita.com/kamina/items/bc4b20c6bbc47b98557c

# GitLab Runnerの並列処理数の変更

単純な並列処理数の変更だけであれば特に難しいことはなく、GitLab Runnerの設定ファイルを下記の通り変更します。

```toml:/etc/gitlab-runner/config.toml
concurrent = 2
```

デフォルトでは`1`が設定されているので、並列処理可能な上限値に変更してあげます。
数値についてはサーバーのスペックと相談ください。

設定を変更したら、一応GitLab Runnerを再起動しておきます。

```shell-session
$ sudo gitlab-runner restart
```

これでGitLabのパイプラインで、並列実行できるJOBは並列実行されるようになります。

# 共有キャッシュの設定

上記の手順で並列実行は可能となりましたが、JOBでキャッシュを利用する場合には共有キャッシュの設定が必要となります。

```yaml:gitlab-ci.yml
default:
  image: node:16
  tags:
    - shared
  cache:
    key:
      files:
        - yarn.lock
    paths:
      - node_modules

stages:
  - build
  - test

build:
  stage: build
  script:
    - yarn install -D
    - yarn run build

lint:
  stage: test
  script:
    yarn run lint

test:
  stage: test
  script:
    yarn run test
```

例えば上記のような、`build`で作成(`yarn install`)した`node_modules`の内容をキャッシュし、`lint` `test`で利用するような場合です。
上記のパイプラインは下記のようになり、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/287e6dcd-43a8-4b87-da3a-91c007e6e646.png)
まず`build`が実行され、その後に`lint` `test`が並列で実行されます。
そして、共有キャッシュの設定を行わないままこれを実行すると、`lint`か`test`のどちらかが失敗するはずです。
これは、一方は`build`のキャッシュが利用できたので成功、もう一方はキャッシュが利用できずに失敗、という状態になります。

各JOBのログを確認すると、下記のようなログが出力されていると思います。
- build
  `Running on runner-xxx-concurrent-0 via gitlab-vm...`
- lint
  `Running on runner-xxx-concurrent-0 via gitlab-vm...`
- test
  `Running on runner-xxx-concurrent-1 via gitlab-vm...`

ここで注目すべきは`concurrent-0 or 1`という部分です。
これは並列実行を可能にした為、`RunnerA-0` `RunnerA-1`という形でナンバリングされた2つのRunnerが実行されていることを意味します。
デフォルトのキャッシュはこのナンバリングされたRunner毎に保持する為、`build`と同じRunnerが利用されたJOBは成功し、違う方は失敗するという理屈になります。
下記のようなログも出力されているはずです。

```shell-session
Restoring cache
Checking cache for 337fc97642de98f9e28d99f19d9781b6e69631fa-non_protected...
No URL provided, cache will not be downloaded from shared cache server. Instead a local version of cache will be extracted. 
```

「共有キャッシュサーバーからダウンロードできないので、ローカルのキャッシュを利用します」的な内容です。

というわけで、これを解消するために共有キャッシュの設定を行います。

## Cloud Storageにキャッシュ用のバケットを作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/0fcd63e7-4001-af5e-2b22-8792e9081639.png)
Cloud Storageからバケットの作成。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/6ee42e50-6ef5-41e8-3cfe-b3971dc2f627.png)
キャッシュとしての利用なので、

- ロケーションタイプ：Region
- デフォルトストレージクラス：Standard

としています。
次に、キャッシュなので一定期間で削除するように設定しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/cc07320d-22dd-d12e-97e7-0e7c77b8be37.png)
バケットのライフサイクルにルールを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/e232cf76-b9ae-5561-1c75-a81f1405de01.png)
アクションは削除で
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/932fab9b-ca3b-cc73-6f8b-0a6747507cc4.png)
削除対象となる経過期間を設定します。
設定例として5日にしているのは、一週間(営業日で5日)保持すれば良いかなと。（次の週でも利用するなら再作成
ここは好きに設定してください。

## バケットにアクセスする為のサービスアカウント作成

バケットを作成したら、そこにアクセスする為のサービスアカウントを作成します。
今回はGitLab Runner用として作成しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/a1a3a970-954a-24df-8e7c-d2c19813a168.png)
サービスアカウントを作成したら、バケットの権限タブから「アクセス権を付与」を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/4b1c5a61-599e-4cab-6531-ec14fd5575f5.png)
プリンシパルには先ほど作成したサービスアカウントを指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/f80a1e61-2e7c-393c-3488-7de5e1b9de9c.png)
ロールは下記の２つを指定。

- Storage レガシー バケット オーナー
- Storage レガシー オブジェクト オーナー

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/fa1237c7-3f43-64c5-38ce-f50ec044be59.png)
次にサービスアカウントにキーを発行します。(GitLab Runnerの設定に使用するもの)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/89d47078-9050-f08a-24be-6d969c763b26.png)
「鍵を追加」でJSONで作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272546/15be340f-c274-1ff7-e47c-3245cf8bfd7b.png)
鍵を作成するとJSONファイルがダウンロードされますので、JSONファイルをGitLab Runnerのサーバに配置します。
下記ファイルを作成し、内容をコピペ。

```shell-session
$ sudo vim /etc/gitlab-runner/service-account.json
```

次にGitLab Runnerの設定ファイルを開いて、

```shell-session
$ sudo vim /etc/gitlab-runner/config.toml
```

下記の通り変更します。

```toml:/etc/gitlab-runner/config.toml
[[runners]]
  [runners.cache]
    Type = "gcs"
    Path = "cache"
    Shared = true
    [runners.cache.gcs]
      CredentialsFile = "/etc/gitlab-runner/service-account.json"
      BucketName = "funlab-gitlab-runners-cache"
```

公式のドキュメントだと下記になります。

https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runnerscachegcs-section

設定したら再起動。

```shell-session
$ sudo gitlab-runner restart
```

これで共有キャッシュが利用されるようになったはずです。
JOBのログに下記のようなログが出力されていれば成功です。

```shell-session
Downloading cache.zip from https://storage.googleapis.com/xxx
```