🎉

KubernetesクラスタのGitOps用レポジトリの紹介

2025/03/26に公開

このポストでは最近構築していじっているおうちラボのKubernetesクラスタの、GitOpsのレポジトリについて紹介します。このセットアップで使用しているバージョン管理システムは、おうちラボ上で動かしているGitLabです。

最初に触れておかなくてはいけないのですが、fluxcdの公式ドキュメントより次のリンクで紹介しているようなものほぼそのままです。更には参考用にパブリックレポジトリのリンクもこのドキュメントにあります。それらを参考に私のレポジトリはこうなっていますという紹介が今回のポストの内容になります。

Ways of structuring your repositories

またついでにと言えばよいのか、以前のポストAnsible で作るおうちラボ HA Kubernetes クラスタの流れでGitOpsのセットアップについてもカバーし、デモも一つやります。

お題に到達するまでが長いので、セットアップの流れが不要な方は目次より一番下まで飛んでくださいませ。

流れ

  • gitレポジトリを用意する
  • owner ロールと api スコープを持つプロジェクトアクセストークンを作成する
    • flux bootstrap コマンドで使用する必要な変数をエクスポートする
  • kubectl を使用してクラスタにアクセスできるホストに flux cli をインストールする
  • flux bootstrap を実行する
  • リポジトリとクラスタの変更を観察する
  • デモ
  • 私の GitOps リポジトリ構造の紹介

プロジェクトの作成

"gitops" というグループ内に "homelab" という名前のプロジェクトを作成しました。

プロジェクトアクセストークン

fluxcdが最初のbootstrap時に利用するアクセストークンを用意します。

プロジェクトの"settings" > "access tokens" と移動し、プロジェクト専用のアクセストークンを一つ払い出します。有効期限については心配しないでください。このトークンは最初に一回使用されるだけです。

  • 名前を付ける
  • owner ロールと api スコープを設定する

https://YOUR_GITLAB_HOST/gitops/homelab/-/settings/access_tokens

最近、owner ロールと api スコープを設定した "hlv3" および "lab-hlv3" という名前の新しいアクセストークンを作成しました。これらは、外部 etcd クラスタを持つ HA クラスタを構築するために私の ansible プロジェクトを使用して構築された最新の Kubernetes クラスタ用です。

トークンを取得したら、fluxのbootstrapを実行するホストでexportしておきましょう。

export GITLAB_TOKEN={access_token_string_here}
export GITLAB_SERVER={YOUR_GITLAB_HOST}

flux のインストール

https://fluxcd.io/flux/installation/

fluxのインストールです。普段kubectlなどでクラスタを管理しているホストにインストールしましょう。kubectl同様、fluxコマンドも~/.kube/configなりKUBECONFIG変数を参照してクラスタとやり取りします。

curl -s https://fluxcd.io/install.sh | sudo bash

補完が用意されているのでシェルに設定しておくと便利です。私の場合は~/.bashrcに以下を書き込みました。

. <(flux completion bash)

ブートストラップ

https://fluxcd.io/flux/installation/bootstrap/gitlab/

以下はfluxでself-hostのGitLabと"lab-hlv3"と名付けていじっているクラスタとをbootstrapした時のコマンドです。上のリンクと少し異なっている部分があるので説明します。

  • --owner=gitops および repository=homelab で "gitops/homelab" レポジトリを指している
  • --hostname="$GITLAB_SERVER" も合わせ、レポジトリの宛先は "https://GITLAB_SERVER/gitops/homelab.git" となる
  • --branch=main はそのままの通り、対象ブランチはmainとしている
  • --path=./cluster/lab-hlv3 はfluxインストールおよびGitOps対象とするパスを指定している
    • インストールされるfluxのコンポーネントも宣言的で、このディレクトリにマニフェストが書かれpushされる
  • --cluster-domain=lab.example.net はクラスタのドメインを指定している
    • デフォルトではkubernetesクラスタのドメインは"cluster.local"でありflux bootstrapコマンドのデフォルトも同様
# do not forget to have these variables set/exported
#export GITLAB_TOKEN={token_string_here}
#export GITLAB_SERVER={YOUR_GITLAB_HOST}

flux bootstrap gitlab \
  --deploy-token-auth \
  --hostname="$GITLAB_SERVER" \
  --owner=gitops \
  --repository=homelab \
  --path=./clusters/lab-hlv3 \
  --branch=main \
  --cluster-domain=lab.example.net

# if you mess up anything and want to start over, run uninstall, and you will be ready to run another flux bootstrap
#
# flux uninstall

bootstrap後のクラスタとレポジトリについて

クラスタの状態

まずはクラスタを見ていきます。つくられたものは以下の通りです。

$ kubectl get all -n flux-system
NAME                                           READY   STATUS    RESTARTS   AGE
pod/helm-controller-654c4c4c64-2wtbp           1/1     Running   0          19h
pod/kustomize-controller-55ff9444cd-kkkxm      1/1     Running   0          19h
pod/notification-controller-58ffd586f7-94vkp   1/1     Running   0          19h
pod/source-controller-5b6b6d555c-5dpv5         1/1     Running   0          19h

NAME                              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/notification-controller   ClusterIP   10.96.22.37    <none>        80/TCP    19h
service/source-controller         ClusterIP   10.96.166.23   <none>        80/TCP    19h
service/webhook-receiver          ClusterIP   10.96.197.23   <none>        80/TCP    19h

NAME                                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/helm-controller           1/1     1            1           19h
deployment.apps/kustomize-controller      1/1     1            1           19h
deployment.apps/notification-controller   1/1     1            1           19h
deployment.apps/source-controller         1/1     1            1           19h

NAME                                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/helm-controller-654c4c4c64           1         1         1       19h
replicaset.apps/kustomize-controller-55ff9444cd      1         1         1       19h
replicaset.apps/notification-controller-58ffd586f7   1         1         1       19h
replicaset.apps/source-controller-5b6b6d555c         1         1         1       19h

api-resourcesはこの通りです。

$ kubectl api-resources --namespaced=true | grep flux
helmreleases                hr             helm.toolkit.fluxcd.io/v2                true         HelmRelease
kustomizations              ks             kustomize.toolkit.fluxcd.io/v1           true         Kustomization
alerts                                     notification.toolkit.fluxcd.io/v1beta3   true         Alert
providers                                  notification.toolkit.fluxcd.io/v1beta3   true         Provider
receivers                                  notification.toolkit.fluxcd.io/v1        true         Receiver
buckets                                    source.toolkit.fluxcd.io/v1              true         Bucket
gitrepositories             gitrepo        source.toolkit.fluxcd.io/v1              true         GitRepository
helmcharts                  hc             source.toolkit.fluxcd.io/v1              true         HelmChart
helmrepositories            helmrepo       source.toolkit.fluxcd.io/v1              true         HelmRepository
ocirepositories             ocirepo        source.toolkit.fluxcd.io/v1beta2         true         OCIRepository

新しいfluxのバージョンでは開発中のコマンドとしてflux treeがあり、fluxが処理しているもののリストが見られるようです。

$ flux tree ks flux-system
Kustomization/flux-system/flux-system
├── CustomResourceDefinition/alerts.notification.toolkit.fluxcd.io
├── CustomResourceDefinition/buckets.source.toolkit.fluxcd.io
├── CustomResourceDefinition/gitrepositories.source.toolkit.fluxcd.io
├── CustomResourceDefinition/helmcharts.source.toolkit.fluxcd.io
├── CustomResourceDefinition/helmreleases.helm.toolkit.fluxcd.io
├── CustomResourceDefinition/helmrepositories.source.toolkit.fluxcd.io
├── CustomResourceDefinition/kustomizations.kustomize.toolkit.fluxcd.io
├── CustomResourceDefinition/ocirepositories.source.toolkit.fluxcd.io
├── CustomResourceDefinition/providers.notification.toolkit.fluxcd.io
├── CustomResourceDefinition/receivers.notification.toolkit.fluxcd.io
├── Namespace/flux-system
├── ResourceQuota/flux-system/critical-pods-flux-system
├── ServiceAccount/flux-system/helm-controller
├── ServiceAccount/flux-system/kustomize-controller
├── ServiceAccount/flux-system/notification-controller
├── ServiceAccount/flux-system/source-controller
├── ClusterRole/crd-controller-flux-system
├── ClusterRole/flux-edit-flux-system
├── ClusterRole/flux-view-flux-system
├── ClusterRoleBinding/cluster-reconciler-flux-system
├── ClusterRoleBinding/crd-controller-flux-system
├── Service/flux-system/notification-controller
├── Service/flux-system/source-controller
├── Service/flux-system/webhook-receiver
├── Deployment/flux-system/helm-controller
├── Deployment/flux-system/kustomize-controller
├── Deployment/flux-system/notification-controller
├── Deployment/flux-system/source-controller
├── NetworkPolicy/flux-system/allow-egress
├── NetworkPolicy/flux-system/allow-scraping
├── NetworkPolicy/flux-system/allow-webhooks
└── GitRepository/flux-system/flux-system

レポジトリの状態

パッとディレクトリとファイルだけ見ると、fluxがbootstrap時に加えた変更が確認できます。APIスコープをセットしたアクセストークンを用いてコミットされました。

$ find . -not -path "*/\.git/*" | sed -e "s/[^-][^\/]*\// |/g" -e "s/|\([^ ]\)/|-\1/"
|-clusters
 | |-lab-hlv3
 | | |-flux-system
 | | | |-kustomization.yaml
 | | | |-gotk-sync.yaml
 | | | |-gotk-components.yaml

GitLab上で "settings" > "repository" > "deploy tokens" を確認してみるとread-onlyのdeploy tokenが作られていることが確認できます。bootstrap以後、fluxはこのトークンでレポジトリを監視して変更に応じてGitOpsを回していきます。

デモ

デモとして一つpodを動かします。

podのマニフェストファイルをそのまま ./clusters/lab-hlv3/demo-pod.yamlに配置するのではなく、今回このあと紹介するレポジトリ構造に近づけていきます。

namespace

mkdir ./clusters/lab-hlv3/namespaces

cat <<'EOF' > ./clusters/lab-hlv3/namespaces/testbed.yaml
---
apiVersion: v1
kind: Namespace
metadata:
  name: testbed
EOF

# commit and push

これでcommit/pushすればfluxが内容を拾い、新たなnamespace "testbed"を作ってくれます。

placeholders

続けて必要なものを一気に作っていきます。大体形を整えるためのダミーです。最後の方にデモとして動かすpodの分があります。

cat <<'EOF' > ./clusters/lab-hlv3/namespaces/placeholder.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: placeholder
EOF


mkdir -p ./infrastructure/lab-hlv3/configs
mkdir -p ./infrastructure/lab-hlv3/controllers
mkdir -p ./apps/base/testbed
mkdir -p ./apps/lab-hlv3

cat <<'EOF' > infrastructure/lab-hlv3/controllers/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - cm-placeholder.yaml
EOF


cat <<'EOF' > infrastructure/lab-hlv3/controllers/cm-placeholder.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: controllers-placeholder
  namespace: placeholder
data:
  thisis: placeholder
EOF


cat <<'EOF' > infrastructure/lab-hlv3/configs/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - cm-placeholder.yaml
EOF


cat <<'EOF' > infrastructure/lab-hlv3/configs/cm-placeholder.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: configs-placeholder
  namespace: placeholder
data:
  thisis: placeholder
EOF


cat <<'EOF' > apps/lab-hlv3/kustomization.yaml
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../base/testbed
EOF


cat <<'EOF' > apps/base/testbed/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deploy-tools.yaml
EOF


cat <<'EOF' > apps/base/testbed/deploy-tools.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tools
  namespace: testbed
  labels:
    app: tools
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tools
  template:
    metadata:
      labels:
        app: tools
    spec:
      hostname: tools
      containers:
        - name: tools
          image: registry.k8s.io/e2e-test-images/agnhost:2.39
          imagePullPolicy: IfNotPresent
          command: ["tail"]
          args: ["-f", "/dev/null"]
EOF

インフラとアプリ用のflux kustomization

次の二つは紹介のところで触れる特に重要なものです。

cat <<'EOF' > clusters/lab-hlv3/infrastructure.yaml
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: infra-controllers
  namespace: flux-system
spec:
  interval: 1m0s
  path: ./infrastructure/lab-hlv3/controllers
  prune: true
  wait: true
  sourceRef:
    kind: GitRepository
    name: flux-system
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: infra-configs
  namespace: flux-system
spec:
  dependsOn:
    - name: infra-controllers
  interval: 1h
  retryInterval: 1m
  timeout: 5m
  sourceRef:
    kind: GitRepository
    name: flux-system
  path: ./infrastructure/lab-hlv3/configs
  prune: true
EOF


cat <<'EOF' > clusters/lab-hlv3/apps.yaml
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: apps
  namespace: flux-system
spec:
  interval: 10m0s
  dependsOn:
    - name: infra-configs
  sourceRef:
    kind: GitRepository
    name: flux-system
  path: ./apps/lab-hlv3
  prune: true
  wait: true
  timeout: 5m0s
EOF

tools podでのデモ

docker execなどに親しみのある方は、kubernetesでも同じようなことができるのだと分かるかと思います。

以下の出力は、クラスタ内のpodでnslookupを実行した例です。このポストではkubernetes公式ドキュメントでも使われているイメージを指定していますが実環境ではdig, drill, openssl, curl, ncなどなど、ちょっとした便利コマンドをあれこれ用意したカスタムイメージを使っています。

$ kubectl exec deploy/tools -n testbed -- nslookup zenn.dev.
Server:         10.96.0.10
Address:        10.96.0.10#53

Non-authoritative answer:
Name:   zenn.dev
Address: 104.26.14.203
Name:   zenn.dev
Address: 172.67.72.220
Name:   zenn.dev
Address: 104.26.15.203
Name:   zenn.dev
Address: 2606:4700:20::681a:fcb
Name:   zenn.dev
Address: 2606:4700:20::681a:ecb
Name:   zenn.dev
Address: 2606:4700:20::ac43:48dc

$ kubectl exec deploy/tools -n testbed -- nslookup kubernetes.default.svc.lab.example.net.
;; Got recursion not available from 10.96.0.10
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   kubernetes.default.svc.lab.example.net
Address: 10.96.0.1
;; Got recursion not available from 10.96.0.10

GitOps レポジトリ構造の紹介

デモは以上で、GitOps用レポジトリの形についてカバーしていきます。

fluxcdのGitOpsの根っこは"flux-system"と名前付けられているflux kustomizationです。その役割は./clusters/lab-hlv3ディレクトリ配下に配置されているkubernetesクラスタに関するマニフェストと実際のクラスタの状態を同期させるということです。

デモの冒頭で触れたとおりそこに直にpodのマニフェストを配置しても良いのですが、何でもかんでもそこに配置してしまうとすぐ手に負えなくなってしまうと思います。

そういうわけで、fluxcd公式のドキュメントとしてそもそもどのようなディレクトリ構造でレポジトリを用意したらよいかについてガイドがあります。

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

今回のポストで紹介するものも違いはありません。公式のガイドに沿って次のようにレイヤーごとのflux kustomizationの作成および依存関係の設定をしています。

  • apps ks (flux kustomization) は ./apps/lab-hlv3 ディレクトリ内のマニフェストに従ってクラスタを更新する
    • そして infra-configs ks に依存する
  • infra-configs ks は ./infrastructure/lab-hlv3/configs ディレクトリ内のマニフェストに従ってクラスタを更新する
    • そして infra-controllers ks に依存する
  • infra-controllers ks は ./infrastructure/lab-hlv3/controllers ディレクトリ内のマニフェストに従ってクラスタを更新する
$ flux get ks
NAME                    REVISION                SUSPENDED       READY   MESSAGE
apps                    main@sha1:b1a6b397      False           True    Applied revision: main@sha1:b1a6b397
flux-system             main@sha1:b1a6b397      False           True    Applied revision: main@sha1:b1a6b397
infra-configs           main@sha1:b1a6b397      False           True    Applied revision: main@sha1:b1a6b397
infra-controllers       main@sha1:b1a6b397      False           True    Applied revision: main@sha1:b1a6b397

つまり(1)インフラのコンポーネントを用意し、(2)それらをコンフィグし、(3)それらを基にアプリケーション、ワークロードを立ち上げるという形に整えます。

もう少し例を加えて説明

デモではpodを一つ動かしただけでしたので、cilium gateway実装状態のディレクトリを想定してもう少し説明させてください。

階層、ファイルのリストはこのセクションの最後にあります。また細かい内容については別ポストとして用意ができたらと考えています。

flux-system

Gateway API実装用のnamespaceとして"gateway" namespaceを./clusters/lab-hlv3/namespaces/gateway.yamlとして用意しました。またGateway経由で既存のtestbed namespaceに用意するサービスへのアクセスを設けるためにtestbed.yamlにも変更を加えています。

変更をcommit/pushすればfluxがgateway namespaceの作成およびtestbed namespaceの更新を実施してくれます。

infra-controllers

fluxのGitOpsで処理されたものではないものの、ここで最初に紹介しなければいけないのはciliumのhelm chartインストール時に用いたvalues.yamlファイル、./infrastructure/lab-hlv3/controllers/values/cilium-values.yamlです。network add-onをインストールしないとそもそもkubernetesクラスタは動作しないので、クラスタ構築後真っ先に入れましたが、クラスタに関するあらゆる情報はこのレポジトリで管理したいのでこちらに配置してあります。

この先さらにhelm chartを利用してインフラのコンポーネントを何かインストールするとなれば、こちらにvaluesファイルを配置してflux helmrepo, helmreleaseマニフェストを作成してfluxにインストールしてもらうことになります。

Cilium gateway実装に限っては、ここではGateway APIのCRDs v1.2.0を配置し、infra-controllersのkustomizationに追加しています。

infra-configs

Cilium network add-onインストール同様、GitOpsセットアップ以前にcorednsのコンフィグマップも更新していました。従ってそのマニフェストファイルも./infrastructure/lab-hlv3/configs/corednsに格納しておきます。

ちなみに今後さらにcorednsの設定を更新するといった場合は、変更後のマニフェストを配置しinfra-configsのkustomizationに追加することになります。

そしてcilium gateway実装関連では、ここではgatewayのコンフィグ、IPAMの設定、L2広報の設定を加えています。

apps

Cilium gatewayの実装、ウェブアクセスのデモということでtraefik/whoamiイメージのdeploymentおよびserviceマニフェストを、既存のtestbed namespaceに追加しています。

Cilium gatewayとwhoamiサービスを繋ぐHTTPRoutesマニフェストは./apps/lab-hlv3/httproutes/whoami.yamlに配置しています。(ちなみにここは./apps/base/testbedに一緒に入れてしまって良いと思います)

アプリを別レポジトリに分ける例

最後にcilium gateway実装と無関係なところなのですが、アプリに関してはアプリ担当チームの別レポジトリで個別に管理するという形も取れるという紹介もしたく。

GitOpsのレポジトリはこういう形にもできるという紹介として./apps/base/news-logger./apps/base/gitlab-reportをレポジトリツリー出力に含めてあります。これら二つに関しては「別レポジトリ用のトークンを使って」「別レポジトリのどこそこのpathを参照すること」といった内容のfluxcdのマニフェストが配置してあり、コードに加えkubernetesクラスタへどうdeployするかといったことが別レポジトリ内で管理されています。

./apps/base/news-logger/repo.yamlの例は以下の通りです。

---
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: news-logger
  namespace: flux-system
spec:
  interval: 1m0s
  ref:
    branch: main
  secretRef:
    name: NAME_OF_SECRET_CONTAINING_RO_DEPLOY_TOKEN
  url: URL_OF_THE_REPO
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: news-logger
  namespace: flux-system
spec:
  interval: 1m0s
  path: ./deploy/lab-hlv3
  prune: true
  sourceRef:
    kind: GitRepository
    name: news-logger
  targetNamespace: WHICHEVER_NAMESPACE_ASSIGNED_FOR_THIS_APP

tree output

$ tree
.
 |-clusters
 | |-lab-hlv3
 | | |-flux-system
 | | | |-kustomization.yaml
 | | | |-gotk-sync.yaml
 | | | |-gotk-components.yaml
 | | |-app.yaml
 | | |-namespaces
 | | | |-gateway.yaml
 | | | |-placeholder.yaml
 | | | |-testbed.yaml
 | | |-infrastructure.yaml
 |-readme.md
 |-sops
 |-.git
 |-infrastructure
 | |-lab-hlv3
 | | |-configs
 | | | |-kustomization.yaml
 | | | |-coredns
 | | | | |-cm-coredns.yaml
 | | | | |-coredns-modified.yaml
 | | | |-cm-placeholder.yaml
 | | | |-cilium
 | | | | |-ippools-cilium-gateway.yaml
 | | | | |-gateway.yaml
 | | | | |-l2announcement-cilium-gateway.yaml
 | | |-controllers
 | | | |-crds
 | | | | |-gateway-api
 | | | | | |-experimental
 | | | | | | |-gateway.networking.k8s.io_tlsroutes-v1.2.0.yaml
 | | | | | |-standard
 | | | | | | |-standard-install-v1.2.0.yaml
 | | | | | |-readme.md
 | | | |-kustomization.yaml
 | | | |-values
 | | | | |-cilium-values.yaml
 | | | |-cm-placeholder.yaml
 | | | |-default-values
 | | | | |-cilium-1.17.2-values.yaml
 |-apps
 | |-lab-hlv3
 | | |-httproutes
 | | | |-whoami.yaml
 | | |-kustomization.yaml
 | |-base
 | | |-testbed
 | | | |-kustomization.yaml
 | | | |-whoami.yaml
 | | | |-readme.md
 | | | |-tools-deployment.yaml
 | | |-news-logger
 | | | |-repo.yaml
 | | |-gitlab-report
 | | | |-repo.yaml

Discussion