GitLab と AKS クラスタの連携

## 概要

[GitLab](https://about.gitlab.com/) には [Kubernetes 統合機能](https://docs.gitlab.com/ee/user/project/clusters/)があり、プロジェクト・グループ・インスタンスの各レベルで Kubernetes クラスタを連携させることで CI/CD や DevOps に便利な環境を簡単に構築できます。

GitLab には連携する Kubernetes クラスタをクラウドサービスで新規作成する機能がありますが、 2020 年 2 月リリースの [GitLab 12.8](https://about.gitlab.com/releases/2020/02/22/gitlab-12-8-released/) の時点ではこの機能は GKE (GCP) または EKS (AWS) にしか対応していません。AKS (Azure) を含むそれ以外のサービスでは Kubernetes クラスタを自分で作ってから GitLab に手動で追加することになります。

これは[ドキュメント](https://docs.gitlab.com/ee/user/project/clusters/add_remove_clusters.html#add-existing-cluster)に詳しく説明されていますが、人間に出力の一部を拾ってコピペさせるような間違いやすい手順が気になったので、 `kubectl -o jsonpath` を駆使して何も考えずに欲しい結果が得られるコマンドに書き直してまとめてみました。

また AKS のクラスタ作成から GitLab への追加までを一度に行える Terraform スクリプトを作りました。これを使うと GitLab + Kubernetes の環境を作ったり消したりするのが簡単にできるようになります。

## 既存 Kubernetes クラスタの追加

GitLab に既存 Kubernetes クラスタを追加するには、プロジェクトやグループで緑色の「Add Kubernetes cluster」ボタンを押した後に「Add existing cluster」タブを選択し、フォームに必要な情報を入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61759/4a4e6d8a-b2b0-49ec-70c0-3dacd112175a.png)

フォームの入力内容は次のとおりです。前提条件として追加する Kubernetes クラスタに管理者権限があり、Bash または互換のシェルで kubectl コマンドが使用できる必要があります。

- Kubernetes cluster name
  - 任意の名前を入力します。
- API URL
  - 次のコマンドで表示される URL を入力します。

```console:コマンド
kubectl config view --minify -o jsonpath="{.clusters[0].cluster.server}" && echo
```
```text:出力例
https://k8stest-4dafbbc7.hcp.japaneast.azmk8s.io:443
````

- CA certificates
  - 次の 2 つのコマンドで PEM 形式の証明書が表示されます。テキスト全体をフォームに入力します。

```console:コマンド
secret=$(kubectl get sa default -o jsonpath="{.secrets[0].name}")
kubectl get secret $secret -o jsonpath="{.data.ca\\.crt}" | base64 -D
```
```text:出力例
-----BEGIN CERTIFICATE-----
MIIEyTCCArGgAwIBAgIQSsdlou1Ri2v7Pt1pOQrimzANBgkqhkiG9w0BAQsFADAN
MQswCQYDVQQDEwJjYTAgFw0yMDAyMjMxMDIwNTBaGA8yMDUwMDIxNTEwMzA1MFow
DTELMAkGA1UEAxMCY2EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/
qMW3DWhK/UdTMGFuWZf0y5dPnVEfOEI1XTVwkCDcvg7H1khx2Y+hSF5XCsU8mKf9
VLwJ7S3+V/BwpgxJl8y4edztsmkSIrttC7JAHuSYkDOVZ38N2lsQ8xwFUzkVeh37
r3/mq8XzMrY3R2mn6o8QRiF7tjaln62IpsFWHpYSXubtnyvK2f+chfWTrNwlJdW0
CHNxjEhpcKZTAAbtwMINzAbDYEeK/SR9mB7vDXXqx0ha+pzl0ZW68fUR0MPDMJZ+
D3N48MBow0BAYUJLMRa5JFE6mATxGyX3sgUHPGW7RUy0ig+kR0gbF++em2UcLTDA
/L+9ev4Le3gRBtI144nVqxPe/4ir6jx2OIeU1E8P0/0Yp6BErpaU8dqxwphMpOO1
07oeki+Wh6aE5To4kBU7wW8o82/UlSk+RhE43Tp7J9RE4uNopMSKICzn/kpgIGF8
MMAw7FI366YNURkbgnBQ1X18A9LmVYBSwGSfeBd2i5MkslEsbToMOGmNpRofkWVK
ia9Jyta8v43Joyx72nDueIK7M6ce4gEmLMeI7kk/hAMNAmR+BoW50nK5Pl+vq8p9
ftD8A+wtzCW3be/xW3R7HryKclU4nGwsaCXjEOEw/iOLGa0DgptMBxsmiEFtLGic
IQgtupuppdvqP1fdFdJNtUDN7Oj+h3rFsPij5XZ7FQIDAQABoyMwITAOBgNVHQ8B
Af8EBAMCAqQwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAc3BX
2NG/OdkUwj91X7hoZ1U2dZEyD1EkxhsCR3ZqOpz9kbC3Tzv2LQhNQ8CASqRziibW
sq3jga7J8DF9J9VTgMVw8yVyLCUwzy5gVDq10gusk8qI1y9DOVLWYoHesUlk2ngM
YC1x4rAcg6MFpQ3S6y8jLj6YJOl0nDVd9YAdoz/KEHK6zggplEdCr04EMmU+a8ez
+O5DUmI6nd6ufSxMEfcMaJZpdLph7Tp4WSM0MdqCRs/gGDPeZt8OAhO3ZfFvnK9x
zTEedAMawJlYFk/hRMD+Q2qYmlhEu+7q3mMhjYh6Ru5xM7HllxbZfwqC9PeNfdWG
jpaZYjRZdE2z6JrxVxhPYuefG+XFiwn0cmXdyD0mAvQ6lQc1JpE8A5l5fpdVWYs3
CNX3avbVRaNmKaebAQf/fnJUJlyuMxw/WJDg7Kjm2TpL2gHfAnYKmvPoUZyOZSse
NOThbdxCj+CIrxCspeuQODj2B4ZIjo0pLiuqGt9yoH37aJEuS0Y4xdHLPkNzdB0i
mk92jL6yhy9u3o+P8Pu9oZpRdwc6rjzSQksRHIlJ4udpxBAfuKwQwJvjeiOqyTmC
INFGmK0vh5EbGRmyjNhozTalFjx//iNQ+VO6YF1jY1TL24NRZb1/m/OUmlctRRcA
/Cy+uuRDhLunAvNGTkDDhLGtOC9Afrzrf4OT76w=
-----END CERTIFICATE-----
```

- Token
  - 最初に kube-system ネームスペースで cluster-admin 権限を持つサービスアカウントを作る必要があります。次のコマンドで [gitlab-admin.yaml](https://gitlab.com/yaegashi/gitlab-k8s/-/blob/master/resources/gitlab-admin.yaml) を読み込ませることで gitlab-admin という名前のサービスアカウントを作成できます。

```console:コマンド
kubectl create -f https://gitlab.com/yaegashi/gitlab-k8s/-/raw/master/resources/gitlab-admin.yaml
```
- 
  - 続いて次の 2 つのコマンドでトークンが表示されますので、それをフォームに入力します。

```console:コマンド
secret=$(kubectl -n kube-system get sa gitlab-admin -o jsonpath="{.secrets[0].name}")
kubectl -n kube-system get secret $secret -o jsonpath="{.data.token}" | base64 -D && echo
```
```text:出力例(1行のテキスト)
eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJnaXRsYWItYWRtaW4tdG9rZW4td2h3Y3giLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZ2l0bGFiLWFkbWluIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiNzc4M2ExMzktNTYzYi0xMWVhLTlmYzItY2U1ZThmZjBlYjE2Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmdpdGxhYi1hZG1pbiJ9.JCpGWQbJY4MAAYzYath8B0BEpZsOnqO_UyG5S3b-w6Sd_MbVhIdPjgEbDlFF96f6uNH49Omc9eh8-srlC2HElWzBesO8d-ue-cgtg9Xt_ALKiPK2HboBVEhRZ2uOVxGwjNIFZgJeR4NZU5IUOqygiT_I0LylVQ0MZ_F62ffHbUAn2BbjuJWIjPMBF7sllF1u7sVEqMQKsCjkU8YrekuTJ31JWPsglm0iBcG3U4igwxiFkjzzwFjkianh2cBIut-4AYk1UKF2L2NpORXhjKMVIqPFNldcFBZBH_A4YfLuUl4EUU5bvyM95CX5zL9EiaPlYSVTVyeOama1HyhWFdhbIhWlCfAJVgEsunZYE2zXizUQ0z9SMgqq9bgYB6VyPhV82nry-0CpxjrViwA6JafC7SPoXt8YnvnCWNNYrC_BpMDbP1OBWpC5djfofA4OAeCWNBtjNSf3Zbu9uAPUOQKST3ZLw4gR7anPNtE0rXgxtsTa4sgs0AZ-YrJNM4387tQeWePJ2vzg_97CLlZpOxuKlDh6k1PZLU-_wZmccjOi5-xpN1jqg6k0KH2yJUwn1X9Uq9uk1F1NiG8skjVwBDIogFvyERlaeYhGFkG_6NZe2e8_s9xa673LrIw8Bn-N93Sem38QhL-gfAbNhqgWxwL9A_nM4OXI_n6flTKEmSncM_8
```

- RBAC-enabled cluster
  - 有効な場合、クラスタの RBAC により権限管理します。
- GitLab-managed cluster
  - 有効な場合、GitLab がクラスタのネームスペースやサービスアカウントを作成します。
- Project namespace prefix (optional, unique)
  - GitLab がクラスタに作成するネームスペースのプレフィクスを指定します。

## Terraform による AKS クラスタの作成と GitLab プロジェクトへの追加

次の場所に Terraform スクリプト一式をまとめていますので参考にしてください。

https://gitlab.com/yaegashi/gitlab-k8s/-/tree/master/terraform/aks1

これを適用すると、次のリソースを自動的に作成します。

- Azure AD (sp.tf)
  - アプリケーション ([azuread_application](https://www.terraform.io/docs/providers/azuread/r/application.html))
  - サービスプリンシパル ([azuread_service_principal](https://www.terraform.io/docs/providers/azuread/r/service_principal.html))
  - サービスプリンシパルパスワード ([azuread_service_principal_password](https://www.terraform.io/docs/providers/azuread/r/service_principal_password.html))
- Azure RM (aks.tf)
  - リソースグループ ([azurerm_resource_group](https://www.terraform.io/docs/providers/azurerm/r/resource_group.html))
  - AKS クラスタ ([azurerm_kubernetes_cluster](https://www.terraform.io/docs/providers/azurerm/r/kubernetes_cluster.html))
- AKS クラスタ (k8s.tf)
  - gitlab-admin サービスアカウント ([kubernetes_service_account](https://www.terraform.io/docs/providers/kubernetes/r/service_account.html))
  - gitlab-admin ロールバインディング ([kubernetes_cluster_role_binding](https://www.terraform.io/docs/providers/kubernetes/r/cluster_role_binding.html))
  - gitlab-admin トークン ([kubernetes_secret](https://www.terraform.io/docs/providers/kubernetes/d/secret.html))
- GitLab (gitlab.tf)
  - 個人プロジェクト ([gitlab_project](https://www.terraform.io/docs/providers/gitlab/r/project.html))
  - 既存 Kubernetes クラスタのプロジェクト連携設定 ([gitlab_project_cluster](https://www.terraform.io/docs/providers/gitlab/r/project_cluster.html))

これを実際に使うには、次の手順のようにリポジトリをクローンし、 terraform.tfvars ファイルの変数定義を調整して terraform を実行します。

```console
$ git clone https://gitlab.com/yaegashi/gitlab-k8s
$ cd gitlab-k8s/terrafform/aks1
$ cp terraform.tfvars.sample terraform.tfvars
$ code terraform.tfvars
$ terraform init
$ terraform plan -var-file=terraform.tfvars
$ terraform apply  -var-file=terraform.tfvars
```

なお、事前に Azure CLI および GitLab に対して次のように認証しておく必要があります。

- AKS を作成するテナント・サブスクリプションに Azure CLI でログイン済みであること
  - `tenant_id` `subscription_id` に設定
- GitLab でスコープが `api` の Personal Access Token を発行していること
  - `gitlab_token` に設定

## まとめ

GitLab と既存 Kubernetes クラスタの連携において、わかりにくい操作を削減する改善された手順書と、環境の作成・後始末が簡単にできる Terraform スクリプトを紹介しました。

従来より、セルフホストの GitLab インスタンスと AKS を活用して様々な CI/CD やインフラ管理の自動化をやってきましたが、現在は内部ユーザー向けの小規模な Web サービスの DevOps を GitLab でやっていくことを検討しています。

実をいうと Terraform は今月からいじり始めたばかりの素人なのですが、今回の使い捨て可能な検証環境を構築する中で多くのメリットを感じており、これからも大いに活用していこうと思いました。