メモ - RyuSA

技術的なメモ書きとか試してみたこと

GitLab OperatorはGitLab運用の自動化の夢を見るか

注意 : GitLab Operatorは10月9日現在まだv0.1.0のリリースがあったばかりです。本番環境への適用にはまーだ早いのでご注意してください🤔

–– さぁ、GitLab OperatorでGitLab運用の自動化をはじめよう!!

gitlab.com

背景

GitLabはおそらく多くのエンジニア、組織で利用されているGitリポジトリ管理ツールです。しかし、GitLabには多様な機能が日々追加されており今やGitLabはただのGitリポジトリツールではなくっている……と個人的に思っています。

GitLabのIssue/Milestoneをはじめ、MavenやコンテナイメージのリポジトリKubernetes Integrationなど多様なコミュニケーション機能や開発者向けの機能が追加されています。GitLabに求められる可用性は日々上がっている一方、ますます複雑になるGitLab運用は人間の手でやりたくないですよね。

そこで使えるのがGitlab Operator、Helmに次ぐ新しい"Cloud Native"なGitlabホスト方法です。

全体像

GitLab Operatorは様々なコンポーネントと接続してGitLabを起動する

検証環境

AWSのEKS上で実施します。ただEKS専用の機能を使う部分は限定的なため、他のKubernetes環境でも同じように起動することができるはずです。

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: ryusa-gitlab
  version: "1.21"

# 後述のExternalDNSとcert-managerをRoute53で管理するためのIRSAを定義
iam:
  withOIDC: true
  serviceAccounts:
  - metadata:
      name: external-dns
      namespace: external-dns
      labels: {}
    wellKnownPolicies:
      externalDNS: true
    roleName: eksctl-ryusa-external-dns-role
  - metadata:
      name: cert-manager
      namespace: cert-manager
      labels: {}
    wellKnownPolicies:
      certManager: true
    roleName: eksctl-ryusa-cert-manager

managedNodeGroups:
  - name: workers
    minSize: 1
    maxSize: 6
    desiredCapacity: 3
    spot: true
    instanceTypes: 
          - t3.medium
          - t3.large
    volumeSize: 60
    volumeType: "gp3"

Getting Startedする

依存関係のセットアップ

GitLab Operatorは

  • NGINX Ingress Controller
  • cert-manager
  • DNSを変更できるツール(今回はExternalDNSを採用)

を利用してGitLabを起動します。まずはこれらを適当にKubernetesにデプロイしておきましょう。

NGINX Ingress Controller

GitLabをKubernetesの外からアクセスできるようにするためのIngress ControllerとしてNGINX Ingress Controllerが推奨されています。実際にはIngress Controllerとして

  • Ingressのプロビジョニングができる
  • TCP:22でProxyができる(SSH用)

の2点が必要な機能だと思います。

kubernetes.github.io

推奨に従ってNGINX Ingress ControllerをKubernetesにHelmでデプロイします。使うHelmチャートはingress-nginx/ingress-nginxValueファイルは下記のYAMLで実装します。

# Helmチャート ingress-nginx/ingress-nginx 

controller:
  ingressClassResource:
    name: nginx
    enabled: true
    default: true
 
# NGINX Ingress ControllerにSSH用のTCPプロキシーを作成する
# 22: ${GITLAB_NAMESPACE}/${GITLAB_NAME}-gitlab-shell:22
tcp:
  22: "gitlab-system/vulture-gitlab-shell:22"

cert-manager

GitLabのTLS証明書を発行するためにインストールが必須です、GitLabのシステム内部で利用する自己証明書の作成もcert-managerのIssuerを使って実装されています。

cert-manager.io

推奨に従ってcert-managerをKubernetesにHelmでデプロイします。使うHelmチャートはjetstack/cert-managerValueファイルは下記のYAMLで実装します。一部項目にEKSからRoute53を変更するためのIRSAの設定が入ってますが、不要な方はスルーしてください。

# Helmチャート jetstack/cert-manager

installCRDs: true
 
# EKSのIRSAを利用するための設定
serviceAccount:
  create: false
  name: cert-manager
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::XXX:role/eksctl-ryusa-cert-manager
 
securityContext:
  enabled: true
  fsGroup: 1001

今回はIssuerとしてLet's Encryptを利用するため、Let's EncryptのACMEを持つClusterIssuerを先に作成しておきます。

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt
  namespace: cert-manager
spec:
  acme:
    email: YOUR_EMAIL
    privateKeySecretRef:
      name: letsencrypt-privatekey
    server: https://acme-v02.api.letsencrypt.org/directory
    solvers:
    - dns01:
        route53:
          region: YOUR_REGION
          hostedZoneID: HOST_ZONE_ID

External DNS

Ingressに割り振るドメイン名を自動的にDNSに設定するツールのインストールが推奨されています。GitLabをデプロイ後、デフォルトの状態で「GitLab」「MinIO」「Repository」の3つのIngressが作成され、それぞれ適切なドメイン名で名前解決できることが期待された状態でコンポーネントが起動してきます。

github.com

もちろん、Ingress作成後に手動でDNSを操作しても良いですが……素直にExternalDNSを利用した方が良いでしょう。今回はExternalDNSの公式リポジトリでホストされているマニフェストを利用してデプロイすることにします。

# 公式のRBACの定義は省略
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
  namespace: external-dns
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
      annotations:
        # IRSAのための設定
        iam.amazonaws.com/role: arn:aws:iam::XXX:role/eksctl-ryusa-external-dns-role
    spec:
      serviceAccountName: external-dns
      containers:
      - name: external-dns
        image: k8s.gcr.io/external-dns/external-dns:v0.7.6
        args:
        - --source=service
        - --source=ingress
        - --domain-filter=ryusa.example.com
        - --provider=aws
        - --policy=upsert-only
        - --aws-zone-type=public
        - --registry=txt
        - --txt-owner-id=some-identifier
      securityContext:
        fsGroup: 65534

GitLab Operatorインストール

公式ドキュメントに沿ってインストールしていきます。

docs.gitlab.com

❯ GL_OPERATOR_VERSION=0.1.0
❯ PLATFORM=kubernetes                
❯ kubectl create namespace gitlab-system
namespace/gitlab-system created
❯ kubectl apply -f https://gitlab.com/api/v4/projects/18899486/packages/generic/gitlab-operator/${GL_OPERATOR_VERSION}/gitlab-operator-${PLATFORM}-${GL_OPERATOR_VERSION}.yaml
Warning: apiextensions.k8s.io/v1beta1 CustomResourceDefinition is deprecated in v1.16+, unavailable in v1.22+; use apiextensions.k8s.io/v1 CustomResourceDefinition
customresourcedefinition.apiextensions.k8s.io/gitlabs.apps.gitlab.com created
serviceaccount/gitlab-app created
serviceaccount/gitlab-manager created
serviceaccount/gitlab-nginx-ingress created
role.rbac.authorization.k8s.io/gitlab-leader-election-role created
role.rbac.authorization.k8s.io/gitlab-nginx-ingress created
clusterrole.rbac.authorization.k8s.io/gitlab-app-role created
clusterrole.rbac.authorization.k8s.io/gitlab-manager-role created
clusterrole.rbac.authorization.k8s.io/gitlab-proxy-role created
Warning: rbac.authorization.k8s.io/v1beta1 ClusterRole is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 ClusterRole
clusterrole.rbac.authorization.k8s.io/gitlab-metrics-reader created
rolebinding.rbac.authorization.k8s.io/gitlab-leader-election-rolebinding created
rolebinding.rbac.authorization.k8s.io/gitlab-nginx-ingress created
clusterrolebinding.rbac.authorization.k8s.io/gitlab-gitlab-app-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/gitlab-gitlab-manager-rolebinding created
clusterrolebinding.rbac.authorization.k8s.io/gitlab-gitlab-proxy-rolebinding created
service/gitlab-controller-manager-metrics-service created
service/gitlab-webhook-service created
deployment.apps/gitlab-controller-manager created
Warning: cert-manager.io/v1alpha2 Certificate is deprecated in v1.4+, unavailable in v1.6+; use cert-manager.io/v1 Certificate
certificate.cert-manager.io/gitlab-serving-cert created
Warning: cert-manager.io/v1alpha2 Issuer is deprecated in v1.4+, unavailable in v1.6+; use cert-manager.io/v1 Issuer
issuer.cert-manager.io/gitlab-selfsigned-issuer created
Warning: admissionregistration.k8s.io/v1beta1 ValidatingWebhookConfiguration is deprecated in v1.16+, unavailable in v1.22+; use admissionregistration.k8s.io/v1 ValidatingWebhookConfiguration
validatingwebhookconfiguration.admissionregistration.k8s.io/gitlab-gitlab-validating-webhook-configuration created
 
❯ kubectl get all -n gitlab-system
NAME                                             READY   STATUS    RESTARTS   AGE
pod/gitlab-controller-manager-645466b464-jxn5l   2/2     Running   0          2m5s
 
NAME                                                TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/gitlab-controller-manager-metrics-service   ClusterIP   10.100.68.88     <none>        8443/TCP   2m10s
service/gitlab-webhook-service                      ClusterIP   10.100.163.107   <none>        443/TCP    2m9s
 
NAME                                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/gitlab-controller-manager   1/1     1            1           2m10s
 
NAME                                                   DESIRED   CURRENT   READY   AGE
replicaset.apps/gitlab-controller-manager-645466b464   1         1         1       2m11s

いい感じに起動しますね、ヨシ!これからGitLab OperatorがWatchしているGitLabリソースをKubernetesにデプロイして、GitLab OperatorにGitLab本体をデプロイしてもらいましょう。

GitLabをデプロイする

GitLab Operatorをデプロイした際にGitLabリソースのCustom Resource Definition(CRD)もデプロイされています。

❯ kubectl api-resources --api-group apps.gitlab.com        
NAME      SHORTNAMES   APIVERSION                NAMESPACED   KIND
gitlabs   gl           apps.gitlab.com/v1beta1   true         GitLab

GitLab OperatorはOperator SDKのHelm Operatorで実装されています。つまりGitlabリソースのスキーマはHelmチャート(gitlab/gitlab)と同一になっています。

sdk.operatorframework.io

GitLabのHelmチャートを参考にしつつ、cert-managerとExternalDNS、NGINX Ingress Controllerと連携するGitLabリソースを作成していきます。

docs.gitlab.com

docs.gitlab.com

まずは*.ryusa.example.comワイルドカードの証明書のためCertificateリソースを作成しておきます。このリソースをあとでGitLabリソースに連携し、すべてのIngressがこのCertificateリソースを参照できるようにします。

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: gitlab-tls
  namespace: gitlab-system
spec:
  dnsNames:
  - "*.ryusa.example.com"
  issuerRef:
    group: cert-manager.io
    kind: ClusterIssuer
    name: letsencrypt
  secretName: gitlab-tls

Certificateリソースが作成され、TLS証明書が発行されたらGitLabをデプロイしましょう。

apiVersion: apps.gitlab.com/v1beta1
kind: GitLab
metadata:
  name: vulture
  namespace: gitlab-system
spec:
  chart:
    version: "5.2.4"
    values:
      global:
        hosts:
          # `gitlab` `registory` `minio`のサブドメインが生えてくる
          domain: ryusa.example.com
        ingress:
          # NGINX Ingress Controllerを使うように指定
          class: nginx
          configureCertmanager: false
          # *.ryusa.example.com ワイルドカード証明書を指定
          tls:
            secretName: gitlab-tls
      nginx-ingress:
        enabled: false
      certmanager:
        install: false

これでGitLabをKubernetes上に展開できました。(長い……)

サクッと運用してみる

それではGitLabで運用作業をやってみましょう!とはいえGitLab OperatorはDay 2 Operationを効率化するためのツールなので、バックアップやバージョンアップなどは非常にシンプルに行うことができます。

実際にコマンドを叩いて動かしてみます。

バックアップ

GitLabリソースを作成するとデフォルトでハウスキーピング用のTask-Runnerが起動します。このTask-Runnerにバックアップ含め様々な作業をお願いすることができます(らしい)。Task-RunnerのPodにはデフォルトでMinIOへの接続情報が保存されているため、Podの内部でbackup-utilityコマンドをキックするだけでバックアップを保存することができます。

docs.gitlab.com

❯ kubectl exec -it \
     -n gitlab-system task-runner-b854bfbd7-zrfgw -- backup-utility
Defaulted container "task-runner" out of: task-runner, certificates (init), configure (init)
Dumping database ... 
Dumping PostgreSQL database gitlabhq_production ... [DONE]
done
...
Dumping pages ...
empty
Packing up backup tar
[DONE] Backup can be found at s3://gitlab-backups/XX_gitlab_backup.tar

すごく簡単、これでMinIOのオブジェクトストレージにバックアップが保存されました。

MinIOのDashboard上でバックアップされたオブジェクトを確認できます

なお、GitLabリソースのTask-Runnerのサブチャートにバックアップのcron設定を追加することで定期的にバックアップを取ってくれるようになるらしいです。(未確認

apiVersion: apps.gitlab.com/v1beta1
kind: GitLab
metadata:
  name: vulture
  namespace: gitlab-system
spec:
  chart:
    version: "5.2.4"
    values:
      # 省略
      gitlab:
        task-runner:
          enabled: true
          backups:
            cron:
              enabled: true
              # 毎日朝3時にバックアップを取得
              schedule: '0 3 * * *'   

リストア

リストア……を完全自動化することはあまりないと思いますが、手動バックアップと同じようにデータをリストアすることができます。バックアップと同じようにTask-Runner Podのコンテナ上でbackup-utilityコマンドをキックするだけです。

docs.gitlab.com

❯ kubectl exec -it -n gitlab-system vulture-task-runner-7dd4d9c4bd-dgs6l \
     -- backup-utility --restore -f 'https://minio.ryusa.example.com/gitlab-backups/XXX_gitlab_backup.tar?...'
...
[DONE]
done

ただし上記コマンドでリストアできるのはデータのみであり、鍵などの秘密情報はリストアしてくれません。GitLabの再インストールなどする場合は、Secretリソースを自分でリストアする必要があります。(ドキュメントに記載あり)

GitLabアップグレード

Gitlabのバージョンをアップグレードするには、対応するHelmチャートのバージョンを変更してapplyすればOKです。あとの処理はOperatorが実施してくれます。

上記でインストールしたGitLabリソースのHelmチャートバージョンは5.2.4ですので、これを5.3.0にアップグレードにします。これによりインストールされるGitLabのバージョンが14.2.4から14.3.0に上がります。

docs.gitlab.com

apiVersion: apps.gitlab.com/v1beta1
kind: GitLab
metadata:
  name: vulture
  namespace: gitlab-system
spec:
  chart:
    # 5.2.4 -> 5.3.0
    version: "5.3.0"
    # あとは省略

kubectl apply でリソース変更後に

まとめ

GitLab Operatorを利用することでKubernetes上にGitLabを展開し、Kubernetesの言葉でGitLabを管理できるようになるところを確認しました。日時のバックアップや機能の拡張、アップデートなど日々の作業の多くをkubectlコマンドだけで管理できるようになるのは、少なくとも個人的には嬉しいですね。

今回はcert-managerやNGINX Ingress ControllerをGitLab Operator内蔵のものではないExternalなものと連携しましたが、他にもいくつかのコンポーネントをExternalに変更できるみたいです。特にPostgreSQLやObject Storageなどの状態を持つものはマネージドサービスで使いたいところですね。

docs.gitlab.com

このGitLab Operatorはまだまだ開発中とのことで、Issueを見ている限り面白そうなIssueがいくつか上がってます。(ダウンタイムなしアップグレードとか、アツい……!)

Operator: enable true Zero downtime upgrades (#59) · Issues · GitLab.org / Cloud Native / GitLab Operator · GitLab

現時点ではまだHelm版とOperator版のGitLabの間のクリティカルなギャップがあるわけではない(と思う)ですが、Operatorによる管理はHelmより柔軟な運用が可能になるのでこれからのロードマップに期待ですね。