GitLab Runner の Kubernetes Executor

## はじめに

GitLab で CICD をする際に、実際にジョブを実行してくれる GitLab Runner 君ですが、Kubernetes 上に Pod として実行可能な、 [Kubernetes Executor](https://docs.gitlab.com/runner/executors/kubernetes.html) という仕組みが提供されています。

## Kubernetes Executor の Job 実行の流れ

[公式ドキュメント](https://docs.gitlab.com/runner/executors/kubernetes.html)にも記載がありますが、ざっくり下図のような流れでCICDを実行するpodが作成されます。

![k8s-exec.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221423/25e8787a-01b5-6c22-e644-f1183a833096.png)

1. まず、Kubernetes 上に Deploy された GitLab Runner Pod が、GitLab のインスタンスに対して、実行可能な Job があるか問い合わせます。
1. 次に、仕事をもらった Runner Pod は、Kubernetes API に対して Pod の作成を Request し、
1. 生成された Job 実行 Pod が実際に仕事をこなします。
1. 仕事を終えた Job Pod はひっそりと姿を消します :innocent: 

さて、この Job Pod には、最大3種類のコンテナが含まれているのですが、なんと[2年前のアドベントカレンダーで詳しく説明してくださっている](https://qiita.com/samskeyti/items/27b3590be453227d2333#kubernetes-executor%E3%81%AE%E6%A7%8B%E6%88%90)ので、割愛させていただきます :pray: 

## Kubernetes Executor のデプロイ

Kubernetes 上への、 GitLab Runner のデプロイは、[GitLab Kubernetes Agent](https://docs.gitlab.com/ee/user/clusters/agent/index.html) と呼ばれる、[Agent を Kubernetes Cluster に導入して Agent 経由でデプロイする方法](https://docs.gitlab.com/runner/install/kubernetes-agent.html)と、[Helm chart を用いて、Runner のみを Kubernetes Cluster にデプロイする方法](https://docs.gitlab.com/runner/install/kubernetes.html)の2つがあります。

Helm Chart リポジトリは[こちら](https://gitlab.com/gitlab-org/charts/gitlab-runner)です。
Chart.yaml をみてみましょう。

```yaml:Chart.yaml
apiVersion: v1
name: gitlab-runner
version: 0.35.0
appVersion: 14.5.0
description: GitLab Runner
keywords:
- git
- ci
- deploy
sources:
- https://hub.docker.com/r/gitlab/gitlab-runner/
- https://docs.gitlab.com/runner/
icon: https://gitlab.com/uploads/-/system/project/avatar/250833/runner_logo.png
maintainers:
- name: GitLab Inc.
  email: support@gitlab.com

```

12/11現在の、安定板 Chart バージョンは `0.35.0` で、だいたい毎月20日前後にマイナーバージョンが更新されています。
なお、Chart.yaml に併記されている appVersion は values.yaml で image 名を上書かないかぎり、デプロイされる`gitlab/gitlab-runner` image の tag バージョンに使用されます。

```bash
gitlab/gitlab-runner:alpine-v14.5.0
```

## Kubernetes Executor の設定項目

[Helm Chart の values.yaml](https://gitlab.com/gitlab-org/charts/gitlab-runner/-/blob/0-35-0-stable/values.yaml) を中心に、Kubernetes Executor の設定項目を見ていこうと思います。
GitLab Runner の Helm Chart values.yaml には、GitLab Runner Pod の設定と、Job 毎に生成される Job Pod の設定の両方が記載されています。

### GitLab Runner Pod 自体の設定項目

- `gitlabUrl`
    - GitLab Runner が問い合わせる GitLab インスタンスの URL を指定します

```yaml:values.yaml
## The GitLab Server URL (with protocol) that want to register the runner against
## ref: https://docs.gitlab.com/runner/commands/README.html#gitlab-runner-register
##
# gitlabUrl: http://gitlab.your-domain.com/
```

- `runnerRegistrationToken`
    - GitLab Runner を GitLab インスタンスに登録するためのトークンです
    - サブグループやリポジトリ単位で異なり、`CI/CD Settings` 画面の Runners セクションから取得できます
    - サブグループやリポジトリのトークンを使用することで、Runner の使用をそのサブグループ下、リポジトリなど任意のスコープに限定することができます

```yaml:values.yaml
## The Registration Token for adding new Runners to the GitLab Server. This must
## be retrieved from your GitLab Instance.
## ref: https://docs.gitlab.com/ce/ci/runners/README.html
##
# runnerRegistrationToken: ""
```

- `concurrent`
    - この GitLab Runner が生成し、同時に実行できるジョブの数を制限します
    - 値10は、同時に実行できるジョブが10個以下であることを意味しています

```yaml:values.yaml
## Configure the maximum number of concurrent jobs
## ref: https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section
##
concurrent: 10
```

- `rbac.rules`
    - この GitLab Runner で実行されるジョブにどれだけの権限を与えるか細かく設定可能です

```yaml:values.yaml
  ## Define list of rules to be added to the rbac role permissions.
  ## Each rule supports the keys:
  ## - apiGroups: default "" (indicates the core API group) if missing or empty.
  ## - resources: default "*" if missing or empty.
  ## - verbs: default "*" if missing or empty.
  rules: []
```

- `rbac.clusterWideAccess`
    - この GitLab Runner で実行されるジョブのアクセス範囲を、Cluster 全体とするか、GitLab Runner がデプロイされている namespace に閉じるか、選択します

```yaml:values.yaml
  ## Run the gitlab-bastion container with the ability to deploy/manage containers of jobs
  ## cluster-wide or only within namespace
  clusterWideAccess: false
```

- `runners.config`
    - この GitLab Runner が生成する Job Pod の設定をここで実施します
    - 詳しくは次のセクションで説明します
   
```yaml:values.yaml
## Configuration for the Pods that the runner launches for each new job
##
runners:
  # runner configuration, where the multi line strings is evaluated as
  # template so you can specify helm values inside of it.
  #
  # tpl: https://helm.sh/docs/howto/charts_tips_and_tricks/#using-the-tpl-function
  # runner configuration: https://docs.gitlab.com/runner/configuration/advanced-configuration.html
  config: |
    [[runners]]
      [runners.kubernetes]
        namespace = "{{.Release.Namespace}}"
        image = "ubuntu:16.04"
```

values.yaml の中には ` DEPRECATED: See https://docs.gitlab.com/runner/install/kubernetes.html#additional-configuration` と書かれている箇所が多々あります。
以前は、yaml の中に直接設定を記述することで、それぞれの機能を実現していましたが、最近はここ `runners.config` に config.toml に従って、設定を記述する方向にシフトしているようです。機能が増えるごとに template のメンテナンスが必要となることや、より柔軟な設定ができるようにすることが目的だと思います。

### 生成される Job Pod の設定項目

生成される Job Pod の設定は、values.yaml の `runners.config` に toml ファイルのフォーマットで記述していきます。 
ここにも、[Kubernetes Executor ならではの設定](https://docs.gitlab.com/runner/executors/kubernetes.html#the-available-configtoml-settings)がありますので、いくつか見ていきます。

まず1つ目が各種リソースクォータです。
下記のように、Job Pod に対して、 cpu / memory に request / limit を設定することができます。

```toml
[[runners]]
  executor = "kubernetes"
  [runners.kubernetes]
    cpu_limit = "1000m"
    cpu_limit_overwrite_max_allowed = "2000m"
    cpu_request = "100m"
    cpu_request_overwrite_max_allowed = "1000m"
    service_cpu_request = "100m"
    service_cpu_request_overwrite_max_allowed = "1000m"
    memory_limit = "2Gi"
    memory_request = "128Mi"
    memory_request_overwrite_max_allowed = "2Gi"
    service_memory_request = "256Mi"
    service_memory_request_overwrite_max_allowed = "2Gi"
```

ここで面白いのが、`*_overwrite_max_allowed` という名前の設定群です。
特定のリポジトリで実行されるパイプラインのみ、必要とするリソース量が多い場合などに対応するため、overwrite する仕組みが設けられています。
例えば何も設定しない場合のリソースリクエストは `*_request` ですが、 `*_request_overwrite_max_allowed` の設定は、リポジトリごとで overwrite できるリクエストの最大値を規定しています。

リポジトリ側では、`.gitlab-ci.yml` に下記のように overwrite したい値を変数で設定することで、そのコンテナのリソースクォータを上書きすることができます。

```yaml:.gitlab-ci.yml
 variables:
   KUBERNETES_CPU_REQUEST: 1               # build container
   KUBERNETES_CPU_LIMIT: 2                 # build container
   KUBERNETES_MEMORY_REQUEST: 1Gi          # build container
   KUBERNETES_SERVICE_CPU_REQUEST: 1       # service container
   KUBERNETES_SERVICE_MEMORY_REQUEST: 2Gi  # service container
```

更に、`.gitlab-ci.yml` で overwrite 可能な設定はリソースクォータだけではなく、下記のように使用する service account や Job Pod がデプロイされる namespace、pod annotation まで overwrite させることができます。

```toml:config.toml@values.yaml
[[runners]]
  executor = "kubernetes"
  [runners.kubernetes]
    service_account_overwrite_allowed = ".*"
    namespace_overwrite_allowed = "ci-.*"
    pod_annotations_overwrite_allowed = ".*"
```

それぞれリポジトリ側で `.gitlab-ci.yml` を下記のように記述し、overwrite できます。

```yaml:.gitlab-ci.yml
variables:
  KUBERNETES_SERVICE_ACCOUNT_OVERWRITE: ci-service-account
  KUBERNETES_NAMESPACE_OVERWRITE: ci-${CI_COMMIT_REF_SLUG}
  KUBERNETES_POD_ANNOTATIONS_1: "Key1=Val1"

```

また、toleration や、node selector も記述することとができるため、Job Pod の生成される Node Pool を制限することも可能です。

```toml:config.toml@values.yaml
[[runners]]
  executor = "kubernetes"
  [runners.kubernetes]
    [runners.kubernetes.node_selector]
      dedicated = "ops"
    [runners.kubernetes.node_tolerations]
      "app=ops" = "NoSchedule"
```

その他にも、例えば [host パス、pvc、config map、secret、empty dir のボリュームマウント](https://docs.gitlab.com/runner/executors/kubernetes.html#using-volumes)をして CICD の中で利用することや、[Job Pod に Security Context を設定する](https://docs.gitlab.com/runner/executors/kubernetes.html#using-security-context)こと、[Pod affinity を設定する](https://docs.gitlab.com/runner/executors/kubernetes.html#pod-affinity-and-anti-affinity)こと、[life cycle hook を設定する](https://docs.gitlab.com/runner/executors/kubernetes.html#container-lifecycle-hooks)ことなどなどが可能なようですが、今回はここまでにしたいと思います :innocent: 

## まとめ

GitLab Runner の Kubernetes Executor について、その構成と設定内容の一例をご紹介しました。
今回紹介したもの以外にも、まだ試したことのない面白い設定がたくさんあるので、もっといろいろ試してみたいと思います。
ここまで読んでいただきありがとうございましたー :smiley_cat: