🍣

GitLab RunnerをKubernetesクラスタ上で動かす

2024/03/28に公開

今回はGitLabのCI/CD pipelineを回してくれるGitLab Runnerの、Kubernetesクラスタ上での動かし方を書いていきます。

  • GitOps (flux)、そして公式のhelm chartで導入
  • cacheはS3を利用する
  • amd64, arm64など混合構成のKubernetesクラスタで、node selectorを設定しamd64のノードだけで走らせる
  • pigzがノードにインストールされていないと大変なことになるんですというメッセージ

セットアップの前提

  • Kubernetes cluster bootstraped with flux for GitOps
  • Minio S3

クラスタの構築およびfluxを利用したGitOpsでのクラスタ管理のセットアップについては以前のポストでカバーできていると思います。よろしければ参照してください。Minio S3のflux + helm chartでの導入する手順については私のポストではまだカバーしていないと思います。ここ一、二か月でできればと思っています。

Kubernetesクラスタ構築 - おうちでGitOpsシリーズ投稿2/6

ただ、自分用に書いている公開ページでは一応カバーし終えているので、そちらを見ていただいても良いかもしれません。USB接続のSSDドライブを使ったDirectPVのセットアップ、およびDirectPVのディスクスペースを利用したMinio S3のセットアップがカバーされています。

building homelab cluster part 4

GitOps用レポジトリについて

https://fluxcd.io/flux/guides/repository-structure/

https://github.com/fluxcd/flux2-kustomize-helm-example

できるだけfluxのドキュメントにあるガイドに倣ってやってみようと考えておりますが、以下のようなディレクトリになっています。

./clusters/{クラスタ名}/宛にfluxでbootstrapしており、./clusters/{クラスタ名}/flux-systemがGitOpsセットアップ時に作られているほか、今回のポストで関係するところではnamespace作成などをここでやっています。

そしてクラスタから他をincludeするという表現が適切でしょうか、インフラ関連のマニフェストを格納するディレクトリとして./infrastructure/{クラスタ名}/controllers./infrastructure/{クラスタ名}/configsを見るようflux kustomizationを設定しています。

なお今回の作業とは関係ありませんが、アプリ関連のマニフェストは./apps/{クラスタ名}を見るよう./clusters/{クラスタ名}/apps.yamlにflux kustomizationのマニフェストをセットしています。

.
 |-apps
 | |-homelab
 | | |-kustomization.yaml
 | |-base
 | | |-dns-report-cloudflare
 | | | |-repo.yaml
 | | |-testbed
 | | | |-kustomization.yaml
 | | | |-debian12-deployment.yaml
 | | | |-dnsutil-pod.yaml
 |-clusters
 | |-homelab
 | | |-infrastructure.yaml
 | | |-apps.yaml
 | | |-network-addon
 | | | |-custom-resources.yaml
 | | | |-tigera-operator.yaml
 | | | |-version.txt
 | | |-monitoring.yaml
 | | |-flux-system
 | | | |-kustomization.yaml
 | | | |-gotk-sync.yaml
 | | | |-gotk-components.yaml
 | | |-sops.yaml
 | | |-nodes
 | | | |-{nodeにlabel付与などするためのマニフェストを格納している}
 | | | |-REDACTED
 | | |-namespace
 | | | |-metallb.yaml
 | | | |-keycloak.yaml
 | | | |-loki.yaml
 | | | |-cert-manager.yaml
 | | | |-runner.yaml             # namespace for GitLab Runner
 | | | |-bytebase.yaml
 | | | |-monitoring.yaml
 | | | |-minio-operator.yaml
 | | | |-gateway.yaml
 | | | |-dns-report-cloudflare.yaml
 | | | |-testbed.yaml
 | | | |-minio-tenant.yaml
 | | | |-postgres.yaml
 | | | |-memcached.yaml.bak
 | | | |-ngf.yaml
 |-infrastructure
 | |-homelab
 | | |-configs
 | | | |-kustomization.yaml
 | | | |-keycloak.yaml
 | | | |-metallb-config.yaml
 | | | |-issuer.yaml
 | | | |-monitoring.yaml
 | | | |-bytebase-db.yaml
 | | | |-gateway.yaml
 | | | |-minio-tenant.yaml
 | | | |-app-db-dns-report-cloudflare.yaml
 | | | |-directpv
 | | | | |-drives.yaml
 | | | |-keycloak-db.yaml
 | | | |-keycloak-cert.yaml
 | | | |-bytebase-routes.yaml
 | | | |-keycloak-routes.yaml
 | | | |-scrape
 | | | | |-monitoring.sh
 | | | | |-scrape.yaml
 | | |-controllers
 | | | |-kustomization.yaml
 | | | |-minio-tenant-values.yaml
 | | | |-metallb.yaml
 | | | |-gitlab-runner.yaml        # flux helmrepo and helmrelease manifest for GitLab Runner generated by gitlab-runner.sh
 | | | |-postgres.sh
 | | | |-loki.yaml
 | | | |-cert-manager-values.yaml
 | | | |-cert-manager.yaml
 | | | |-minio-tenant.sh
 | | | |-bytebase-values.yaml
 | | | |-results-memcached-values.yaml
 | | | |-keycloak-operator.yaml
 | | | |-bytebase.yaml
 | | | |-memcached.sh
 | | | |-postgres-operator-values.yaml
 | | | |-metallb.sh
 | | | |-promtail.sh
 | | | |-minio-operator.yaml
 | | | |-default-values
 | | | | |-{参照用に導入時のvalues.yamlファイルのコピーを格納している}
 | | | | |-gitlab-runner-values.yaml
 | | | |-ngf-values.yaml
 | | | |-gitlab-runner.sh        # script to generate flux helmrepo and helmrelease manifest yaml file including gitlab-runner-values.yaml file
 | | | |-bytebase.sh
 | | | |-postgres-operator-ui-values.yaml
 | | | |-metallb-values.yaml
 | | | |-cert-manager.sh
 | | | |-loki-values.yaml
 | | | |-crds
 | | | | |-cert-manager-v1.14.3.yaml
 | | | | |-gateway-v1.0.0.yaml
 | | | | |-directpv-v4.0.10.yaml
 | | | | |-directpv-v4.0.10.yaml.bak
 | | | | |-kube-prometheus-v0.13.yaml
 | | | | |-keycloakrealmimports-v1-24.0.1.yaml
 | | | | |-keycloaks-v1-24.0.1.yaml
 | | | |-postgres-operator.yaml
 | | | |-promtail.yaml
 | | | |-minio-tenant.yaml
 | | | |-promtail-values.yaml
 | | | |-gitlab-runner-values.yaml    # GitLab Runner helm chart values file edited to deploy on my cluster
 | | | |-memcached.yaml
 | | | |-ngf.yaml
 | | | |-loki.sh
 | | | |-chunk-memcached-values.yaml
 | | | |-ngf.sh
 | | | |-minio-operator.sh
 | | |-monitoring
 | | | |-{kube-promethuesのマニフェストが90個弱...省略}
 |-.git

GitLab Runnerとは

https://docs.gitlab.com/runner/

GitLab Runner is an application that works with GitLab CI/CD to run jobs in a pipeline

注意点 - pigzをクラスタの全ノードに入れておきましょう

https://zlib.net/pigz/

pigzというパッケージが必要です。parallel gzipということで圧縮、解凍するためのプログラムで、これがない場合、RunnerやHelperイメージをノードがpullし、利用しようとしても解凍できずImagePullErrを延々と繰り返すだけの状態になります。

私のクラスタのノードは5台中1台だけpigzがなぜか入っており、しかもRunnerを導入した最初のpodらは運悪くpigzが入っているノードで正常可動したので、問題の確認に手間がかかりました。

init-permissions コンテナ準備中にエラー終了する

GitLab上で確認できるジョブのログは以下の通りです。公式ドキュメント、shell-profile-loadingのところを参照してとリンクも出してくれるのですが全くkubenetes executorと関係ない内容で困りました。

pod側のログなどを探るとunpigzがなくてpullしたイメージを使えずどうこう...といったメッセージが見つかりました。各ノードでpigz入っているか確認、入っているノードいないノードそれぞれでnode selectorなど設定して試験、最終的に全ノードにpigzをインストールしてオールグリーンへ、という対応をしていました。

GitLab GUIで確認できるログが以下。

log_on_gitlab_job_gui
Running with gitlab-runner 16.9.1 (782c6ecb)
  on runner-gitlab-runner-gitlab-runner-74468ddc48-km6n9 2yUzDyeA-, system ID: r_2gX6yFG1W812
Preparing the "kubernetes" executor
00:00
Using Kubernetes namespace: runner
Using Kubernetes executor with image registry.blink-1x52.net/cache-gcrio/kaniko-project/executor:v1.22.0-debug ...
Using attach strategy to execute scripts...
Preparing environment
00:08
Using FF_USE_POD_ACTIVE_DEADLINE_SECONDS, the Pod activeDeadlineSeconds will be set to the job timeout: 1h0m0s...
Waiting for pod runner/runner-2yuzdyea--project-158-concurrent-0-niolypqc to be running, status is Pending
Waiting for pod runner/runner-2yuzdyea--project-158-concurrent-0-niolypqc to be running, status is Pending
	ContainersNotInitialized: "containers with incomplete status: [init-permissions]"
	ContainersNotReady: "containers with unready status: [build helper]"
	ContainersNotReady: "containers with unready status: [build helper]"
ERROR: Job failed (system failure): prepare environment: waiting for pod running: pod status is failed. Check https://docs.gitlab.com/runner/shells/index.html#shell-profile-loading for more information

Podのログが以下。

log_on_k8s_runner_pod
[0;33mWARNING: Job failed: prepare environment: waiting for pod running: pulling image "registry.gitlab.com/gitlab-org/gitlab-runner/gitlab-runner-helper:ubuntu-x86_64-v16.10.0": image pull failed: failed to pull and unpack image "registry.gitlab.com/gitlab-org/gitlab-runner/gitlab-runner-helper:ubuntu-x86_64-v16.10.0": failed to extract layer sha256:09c555e0ed6e70cd551f4d350f67dbbce8c9cb33a11a4f0c530b1da291d5dfb7: failed to get stream processor for application/vnd.docker.image.rootfs.diff.tar.gzip: fork/exec /usr/bin/unpigz: no such file or directory: unknown. Check https://docs.gitlab.com/runner/shells/index.html#shell-profile-loading for more information

Minioテナントでの事前準備

GitLab Runnerがcacheのために利用するbucketとユーザ、ユーザのaccess key、secret keyを作っておきましょう。

のちほどGitLab Runnerのhelm chart用values.yamlファイルに書き込むことになります。

GitLabでの事前準備

Admin Areas > CI/CD > Runnersとメニューを辿り、"New instance runner"をクリックして新たなRunnerを登録するためのトークンを取得します。

  • linux OS
  • tick "run untagged jobs" checkbox
  • create

とするとトークン文字列が取得できます。

こちらものちほどvalues.yamlファイルに書き込むことになります。

helm chartのvalues.yamlファイルの準備

helm chartはあるシステムをKubernetesクラスタ上で稼働させるために必要なものをまとめたレシピで、それに対する変更点、設定ファイルがvalues.yamlファイルになります。

https://docs.gitlab.com/runner/install/kubernetes.html

まずはhelmでレポジトリをローカルに追加し、バージョンを確認します。使うバージョンを指定してvaluesファイルを書き出します。

なお、いつでも指定のバージョンのvaluesファイルが取得できるのでレポジトリにコピーを取っておく必要はあまりないと思います。オプショナルです。

# add repository
helm repo add gitlab https://charts.gitlab.io

# update when necessary
helm repo update gitlab

# list versions
helm search repo -l gitlab/gitlab-runner | head

# move to the homelab repo and store default values file
cd {gitops repo}/infrastructure/homelab/controllers/default-values
helm show values gitlab/gitlab-runner --version 0.63.0 > gitlab-runner-values.yaml

# and of course, place the values file to actually edit and use
cp gitlab-runner-values.yaml ../.

values.yamlへの変更

長いのでコメント部分や変更を加えていない部分など省いた結果が以下です。

  • gitlabUrl: GitLabサーバ宛先
  • rbac.create: trueで作ってもらう
  • runners.config: GitLab Runnerに読ませるconfig.tomlです
    • clone_url - 私のGitLabはhttpで稼働していて、手前にリバースプロキシがTLS offloadしているので、アクセスするときはhttpsだよと改めて指示している
    • imageおよびhelper_image_flavor - デフォルトでalpineだが、名前解決に問題があるのでhelperはubuntuのイメージを使ってやってねと指示
    • runners.kubernetes.node_selector - executorはこのnode_selectorに従って動かしてねと指示
    • runners.cache.s3あたり - S3の宛先、bucket名、access keyなど指定
      • GitOpsなので暗号化されていないキーをマニフェストにベタ打ちしたくないのですが、secretでマウントできても使ってくれないという問題にぶち当たっており現在はベタ打ちのままです
      • SOPS用レポジトリに暗号化してプッシュ、fluxに複合化してもらってKubernetesにsecretとして載せてもらい、それをrunnerに使ってもらうというのが本当はやりたい方法
  • GitLab Runnerのregistration tokenは"gitlab-runner"という名前のsecretに格納してある情報を使ってねという指示

なお私のクラスタはamd64, arm64が混在しており、amd64でだけジョブを回してほしいということでnode selectorをセットしています。

./infrastructure/CLUSTERNAME/controllers/gitlab-runer-values.yaml...partial
gitlabUrl: https://cp.blink-1x52.net/

rbac:
  create: true

runners:
  config: |
    [[runners]]
      clone_url = "https://cp.blink-1x52.net"
      cache_dir = "/cache"
      [runners.kubernetes]
        namespace = "{{.Release.Namespace}}"
        image = "alpine"
        helper_image_flavor = "ubuntu"
        [runners.kubernetes.node_selector]
          "kubernetes.io/arch" = "amd64"
      [runners.cache]
        Type = "s3"
        [runners.cache.s3]
          ServerAddress = "s3.blink-1x52.net"
          BucketName = "runner"
          BucketLocation = ""
          Insecure = false
          AuthenticationType = "access-key"
          AccessKey = "REDACTED - access key here"
          SecretKey = "REDACTED - secret key here"

  secret: gitlab-runner

podSecurityContext:
  runAsUser: 100
  fsGroup: 65533

resources:
  limits:
    memory: 256Mi
    cpu: 200m
    ephemeral-storage: 512Mi
  requests:
    memory: 128Mi
    cpu: 100m
    ephemeral-storage: 256Mi

nodeSelector:
  kubernetes.io/arch: amd64

runner namespaceの作成

私の場合は./clusters/{クラスタ名}/namespace/runner.yamlというディレクトリ配置でファイルを用意しましたが、以下のようなマニフェストを用意して"runner" namespaceを作成しておきます。

./clusters/CLUSTERNAME/namespace/runner.yaml
---
kind: Namespace
apiVersion: v1
metadata:
  name: runner

GitLab Runnerのtokenをsecretとして作成

SOPS用レポジトリで次のようにしてsecretを作成、暗号化して、runner namespace上にsecretを用意しておきます。

SOPSに関してもおうちラボKubernetesクラスタ編など書いてカバーしたいのですが、取り急ぎ公式および私の個人メモページのリンクを貼っておきます。zennでポストできたら本ポストも更新します。

https://fluxcd.io/flux/guides/mozilla-sops/

https://beryl.blink-1x52.net/homelab/building-homelab-cluster-part-1/

create_gitlab_token_secret_on_sops_repo
kubectl create secret generic gitlab-runner \
  --from-literal=runner-registration-token="" \
  --from-literal=runner-token="your gitlab runner registration token here" \
  --dry-run=client \
  --namespace=runner \
  -o yaml > {export to appropriate directory on sops repo}/gitlab-runner.yaml

# and encrypt it, and then commit/push it to the origin for flux to decrypt/reconcile
sops -i --encrypt gitlab-runner.yaml

GitLab Runnerをhelm releaseでクラスタ上へ

namespace、runnerが自身をGitLabへ登録するためのトークン、helm release用設定ファイルであるvalues.yamlファイルが用意できたので、fluxのhelmrepo、helmreleaseマニフェストを用意します。

chartのバージョンなりvalues.yamlファイルなりを更新するたびにマニフェストを再生成するのが簡単になるよう、私はいつも以下のようなスクリプトを用意しています。

./infrastructure/CLUSTERNAME/controllers/gitlab-runner.sh
#!/bin/bash

# add flux helmrepo to the manifest
flux create source helm gitlab \
	--url=https://charts.gitlab.io \
	--interval=1h0m0s \
	--export >gitlab-runner.yaml

# add flux helm release to the manifest including the customized values.yaml file
flux create helmrelease gitlab-runner \
	--interval=10m \
	--target-namespace=runner \
	--source=HelmRepository/gitlab \
	--chart=gitlab-runner \
	--chart-version=0.63.0 \
	--values=gitlab-runner-values.yaml \
	--export >>gitlab-runner.yaml

上のスクリプトは./infrastructure/{クラスタ名}/controllers/gitlab-runner.shとして配置しており、生成されるマニフェストファイルは./infrastructure/{クラスタ名}/controllers/gitlab-runner.yamlとなります。このgitlab-runner.yamlを"infra-controller"kustomizationに追加します。

以下はもう他にいろいろ追加してある状態のファイルですが、flux kustomizationではなくk8s kustomizationとしてresourcesリストにインフラに載せたいシステムを一個ずつ追記して載せていくという形でクラスタに変更を加えられます。今回はもちろん"gitlab-runner.yaml"がresourcesリストに追記されることで、fluxがその追加マニフェストを確認し、GitLabのhelmレポジトリを自身に追加し、chartをダウンロードし、helm release内容をクラスタへ反映させていく、ということをしてくれます。

./infrastructure/CLUSTERNAME/controllers/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  ### CRDs
  - crds/gateway-v1.0.0.yaml
  - crds/directpv-v4.0.10.yaml
  - crds/cert-manager-v1.14.3.yaml
  - crds/kube-prometheus-v0.13.yaml
  - crds/keycloaks-v1-24.0.1.yaml
  - crds/keycloakrealmimports-v1-24.0.1.yaml
  ### infra-controllers
  - metallb.yaml
  - ngf.yaml
  - minio-operator.yaml
  - minio-tenant.yaml
  - cert-manager.yaml
  - gitlab-runner.yaml # ADD GITLAB RUNNER!
  - loki.yaml
  - promtail.yaml
  - keycloak-operator.yaml
  - postgres-operator.yaml
  - bytebase.yaml

結果

結果は次の通りです。

podがGitLab CI/CDのjobを常に出てこないか問い合わせ、新たなjobが発生すると別のexecutorとしてのpodを作りjobを実行させます。

$ kubectl get all -n runner
NAME                                                      READY   STATUS    RESTARTS   AGE
pod/runner-gitlab-runner-gitlab-runner-7f944ffb55-b4nlm   1/1     Running   0          23m

NAME                                                 READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/runner-gitlab-runner-gitlab-runner   1/1     1            1           23m

NAME                                                            DESIRED   CURRENT   READY   AGE
replicaset.apps/runner-gitlab-runner-gitlab-runner-7f944ffb55   1         1         1       23m

runner log

導入後二回目以降のジョブのログです。キャッシュが使われており、実行前にキャッシュ取得、実行後にキャッシュ格納しています。

gitlab_job_log
Running with gitlab-runner 16.9.1 (782c6ecb)
  on runner-gitlab-runner-gitlab-runner-7f944ffb55-b4nlm ZP57C1kd9, system ID: r_svxTIEr5WHJr
Preparing the "kubernetes" executor
00:00
Preparing environment
00:04
Getting source from Git repository
00:01
Restoring cache
00:00
Checking cache for build-deploy-20240305-plugin-protected...
Downloading cache from https://s3.blink-1x52.net/runner/runner/ZP57C1kd9/project/148/build-deploy-20240305-plugin-protected
Successfully extracted cache
Executing "step_script" stage of the job script
00:10
Running after_script
00:01
Saving cache for successful job
00:01
Creating cache build-deploy-20240305-plugin-protected...
/builds/pages/beryl/.cache/pip: found 568 matching artifact files and directories
Uploading cache.zip to https://s3.blink-1x52.net/runner/runner/ZP57C1kd9/project/148/build-deploy-20240305-plugin-protected
Created cache
Uploading artifacts for successful job
00:01
Uploading artifacts...
public/: found 175 matching artifact files and directories
Uploading artifacts as "archive" to coordinator... 201 Created  id=1022 responseStatus=201 Created token=glcbt-64
Cleaning up project directory and file based variables
00:01
Job succeeded

おわりに

以上です!

かなり前からKubernetesクラスタ上でrunnerを動かしていたのですが、時折ジョブを回してくれなくなるので、その問題が解消したらポストしたいと考えていました。原因は冒頭の方で触れた、pigzが入っているノードと入っていないノードが混在していたことです。

S3のaccess, secret keyをvalues.yamlファイルベタ打ち問題は残っているものの、これに関しては進展あれば更新します。

ポスト内でちょくちょく触れていますが、私のサイトではおうちラボKubernetes編がパート10くらいまでできています。英語ですが。以前のポストで触れた内容より訂正したいもの、更新したいものなどあるので、改めてzennでもシリーズ投稿したいと考えています。

内容としては次のようなものの導入についてカバーしています。興味あるものがありましたら見てみてください。

https://beryl.blink-1x52.net/homelab/building-homelab-cluster-part-1/

  • flux gitops
    • repository structure more aligned to what's described in the flux doc
  • sops repository
  • calico as network addon
    • will use network policy in later part
  • metallb # already covered in a previous post on zenn
  • gateway api and nginx gateway fabric # already covered in a previous zenn post
  • directpv using usb ssd drives
  • minio s3
  • cert-manager
    • use gateway annotations to automatically have certs ready
  • gitlab runner
    • use minio s3 cache
  • monitoring system using kube-prometheus (prometheus, grafana, exporters, alertmanager)
  • logging system using grafana loki and promtail as logging agent
  • bytebase to manage database and setup database gitops
  • postgres using postgres-operator
  • gitlab cicd to let kaniko build app image, push it on self-managed image registry running on harbor, and use it as k8s CronJob

Discussion