Flux導入とMetalLB実装 - おうちでGitOpsシリーズ投稿3/6

2023/06/28に公開

こちらはおうちでGitOpsシリーズの3つ目のポストです。

前回はKubernetesクラスタが出来上がりましたので、今回はようやくGitOpsができるようレポジトリやツールのセットアップをしていきます。また出来上がったGitOps環境でmetallbという、サービスをKubernetesクラスタ外からアクセスできるようにするシステムの導入もやっていきます。

本シリーズのお約束

  • Kubernetes Cluster on 3 nodes (2 raspberry pi4 - arm64, and 1 amd64 fanless mini PC)
    • flannel for networking
    • flux as a GitOps tool
    • metallb for LoadBalancer service type implementation
  • 1 or more machine running Docker
    • serving GitLab (Used for GitOps central repository)
    • serving Nginx (Reverse proxy to provide access to GitLab and other web services to be created on Kubernetes Cluster)
    • serving Unbound (DNS resolver for machines on LAN)
  • public DNS domain + SSL/TLS certification (for example, Let's Encrypt) recommended
    • they are all mydomain.net in the series
    • replace mydomain.net with your own DNS domain to follow through

More on Docker - Series Top: Dockerで作るおうちLAN遊び場 シリーズ1/7

Interested in getting your own DNS domain?

現状の再確認

まずは本ポスト開始時点で、前回までに用意したGitLabサーバと、構築したてのKubernetesクラスタがあります。

Kubernetesクラスタにはkube-systemというnamespace上に、Kubernetesクラスタとして機能するために必要な数々のpodが動作しています。ちなみにcontrol-planeでkubectl get pods -n kube-systemと実行するとpodのリストが見られます。またオプションを加えてkubectl get pods -n kube-system -o wideとすると各podがどのnode上で動作しているかなども見られます。

今回追加されるもの

今回追加されるものリストは次の通りです。

  • "flux-config"というレポジトリをGitLabに追加
  • control-planeにfluxツールをインストール
  • fluxツールでKubernetesクラスタと"flux-config"というレポジトリを関連付ける
    • この時、"flux-config"レポジトリにflux関連のマニフェストファイルが書き加えられる
    • 同時にクラスタ上にflux-systemというnamespaceと、fluxがGitOpsツールとして動作するのに必要なpodが作られる
    • 以後、flux-system内のpodらがレポジトリの変更を監視し、レポジトリ上の変更内容に応じてfluxのpodがKubernetesクラスタへ変更命令を出すという形になる
  • "flux-config"レポジトリにmetallb追加用のファイルを追加する
    • fluxがKubernetesクラスタ上にmetallbを追加するよう手配してくれる

セットアップ開始

それではセットアップを進めていきます。まずはGitLabでの準備からです。

GitLab上の準備開始

実は今回やっていくことは、大体GitLab公式のドキュメントで紹介されている通りになります。

https://docs.gitlab.com/ee/user/clusters/agent/gitops/flux_tutorial.html

gitopsグループ作成

まずはgitopsというグループを作りましょう。そしてそのグループのページへ移動しましょう。https://gitlab.mydomain.net/gitopsといったURLになると思います。

グループアクセストークン発行

"Settings > Access Tokens"と移動し、グループアクセストークンを発行します。以下のような設定で発行し、トークン・文字列を控えておきましょう。

  • Token name: "gitops_token"など、自由に決めましょう
  • Expiration date: 私は10年後としました
  • Select a role: 私はMaintainerにしました (developerでもよかったでしょうか)
  • Select scopes: apiのみ有効にします

flux-configレポジトリを作成

gitopsグループでflux-configという名称のレポジトリを作成しましょう。

control-planeにfluxツールをインストール

一旦GitLabから離れ、Kubernetesクラスタのcontrol-planeとして動いているノード、本シリーズではrpi4bp、で作業します。

先に紹介したGitLab上のドキュメントにもリンクがありますが、flux公式のドキュメントがこちらです。

https://fluxcd.io/flux/installation/#install-the-flux-cli

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

# check
❯ which flux
/usr/local/bin/flux

❯ flux --version
flux version 2.0.0-rc.2

bootstrap

続いてfluxでbootstrapします。これはつまりKubernetesクラスタ構築時にkubeadm initしたのと同様に、fluxをKubernetesクラスタおよびGitLabレポジトリに導入する初期セットアッププロセスです。

まず先程取得したグループアクセストークンをGITLAB_TOKENとしてexportします。続くflux bootstrap gitlabコマンドでbootstrapを実行させます。

コマンドは以下の通りとなります。なおこのポストを書いている時点で、GitLab上のドキュメントではトークンに関するオプションが--deploy-token-authとなっていますが、flux側のバージョンアップの結果でしょうか、--token-authが有効なオプションとなっています。

# export access token as GITLAB_TOKEN
export GITLAB_TOKEN={gitlab group access token string}

# run flux bootstrap
flux bootstrap gitlab \
  --hostname=gitlab.mydomain.net \
  --owner=gitops \
  --repository=flux-config \
  --branch=main \
  --path=clusters/my-cluster \
  --token-auth

GitOpsを試そう!!

これでGitOps環境は完成です!以後、flux-configレポジトリ内、mainブランチのclusters/my-cluster配下に配置したマニフェストはfluxによってKubernetesクラスタへ反映されていきます。これがGitOpsの肝で、reconcilationというプロセスによって成されるのですが、「ソースとして記載されている状態とKubernetesクラスタのリソースの状態とを合致させる」といったイメージのプロセスとなります。

では早速gitでpushするだけでKubernetesクラスタにモノをデプロイできるのか試してみましょう。

先のGitLab公式ドキュメントにもbootstrapの続きがあり、それについても後でカバーしたいのですが、まずはmetallbをクラスタに導入させてください。

metallb

https://metallb.universe.tf/

Kubernetesはサービスへアクセスできるようにするのが難しい

Dockerで初めて遊ぶ時、コンテナを実行して比較的すぐそのコンテナのサービスにDockerのホスト端末および他の端末からでもアクセスできるようにできます。例えばdocker run hello-worldでローカルでコンテナを実行・確認でき、docker run -p 80:80 nginxでリモートからもdockerコンテナを走らせている端末のポート80へのアクセスでサービスへのアクセスが確認できます。

ところがKubernetesの場合、kubeadm init/joinでクラスタを構築できたと思いきや、ネットワークプラグインを導入するまでpodを実行してみることはできず、更にはマニフェストを流し込んでpodを実行できてkubectl get podsなどで実行していることを確認できたと思いきや、今度はそのpodのサービスにアクセスする方法がなくて困るといったことに直面します。クラスタのcontrol-planeなどからはcurl {IP address of pod running nginx}などでアクセスできるのですが、クラスタ外からはDockerのコンテナの例のように簡単にはアクセスがセットアップできません。クラウドサービスの場合はもちろん他からクラスタ上のサービスへアクセスするための仕組み・サービスが備わっていますが、オンプレに自前で構築したKubernetesクラスタにはそういったものがありません。

前置きが長くなってしまいましたが、要するにmetallbを導入すればKubernetesクラスタ上に走らせるサービスへのクラスタ外からのアクセスを容易に構築できます。

metallbに使わせるLANのIPアドレスレンジ

このシリーズでは192.168.1.0/24が実ネットワークとなっており、192.168.1.55-56あたりのノードでGitLabや他のサービスをDockerコンテナとして走らせています。Kubernetesクラスタ上のサービスも同じサブネット上のアドレスでアクセスできるようにするのですが、それをやってくれるmetallbには192.168.1.200-250を設定することにします。

マニフェスト

ではfluxに拾ってもらい、Kubernetesクラスタへ当ててもらうマニフェストファイルを用意していきます。

最初にディレクトリの用意です。/clusters/my-cluster以下に、/clusters/my-cluster/metallb-systemとしてディレクトリを用意しましょう。

このmetallb-systemディレクトリに3つファイルを用意します。

1つ目はkustomization.yamlです。大本のシステムに関するマニフェストはマニュアルに倣いgithub上にあるものを直接参照します。更に追加で、このディレクトリに配置するconfig.yamll2adv.yamlも使うということを指示しているファイルとなります。

なおバージョンについてはその時の最新のものを参照すればよいと思います。ここではv0.13.9となっています。

https://metallb.universe.tf/installation/#installation-with-kustomize

namespace: metallb-system

resources:
  - github.com/metallb/metallb/config/native?ref=v0.13.9
  - config.yaml
  - l2adv.yaml

2つ目のファイルはconfig.yamlです。先程触れた、metallbに使わせるIPアドレスレンジを指定しています。

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: lan-addr-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.1.200-192.168.1.250

最後にl2adv.yamlです。OSI参照レイヤ2の仕組みでなんとかしてサービスをネットワーク上に広報しますよ、つまりARPやMACアドレスでなんとかしますよという場合、L2Advertisementを選択します。このファイルではどのIPAddressPoolを使うのかという指定をしていません。その場合、あるものすべて使いなさいということになり、今回は唯一config.yaml内で作っているプールだけ使われるということになります。

apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: l2adv-addr-pool
  namespace: metallb-system

このシリーズではカバーしませんが、LAN環境にBGPルータがいる場合は、metallbのBGPAdvertisementで構築するのもよいと思います。大きなKubernetesクラスタ、大きなサービス用サブネットで豪勢に遊べると思います。BFDやlocal pref、community、vrfなども利用できます。

さて、以上の3ファイルをレポジトリのmainブランチにpushすると、ほどなくKubernetesクラスタに反映されます。kubectl get nsmetallb-systemができていることが確認でき、kubectl get pods -n metallb-system -o wideでcontrollerのpodが一つ、speakerのpodが各ノードで走っていることが確認できます。

metallb導入が完了しました!

❯ tree
.
 |-.git
 |-clusters
 | |-my-cluster
 | | |-flux-system
 | | | |-gotk-components.yaml
 | | | |-kustomization.yaml
 | | | |-gotk-sync.yaml
 | | |-metallb-system
 | | | |-config.yaml
 | | | |-l2adv.yaml
 | | | |-kustomization.yaml
 |-README.md

nginx deployment

それでは続いてNginxを走らせてみましょう。大体先のGitLab公式のドキュメントの続きをなぞっていく形になりますが、metallbを活用してLAN上の他の端末からアクセスできる状態にまで持っていきます。

別レポジトリの用意

まずはgitopsグループ下に"web-app-manifests"という新しいレポジトリを作りましょう。

deploymentマニフェストの用意

そしてそのレポジトリにnginx-deployment.yamlというマニフェストファイルを用意しましょう。内容は以下の通りです。GitLab公式のドキュメントからの変更点は、replicasを3から1にしているところです。3のままでも全く問題ありません。

apiVersion: apps/v1

kind: Deployment

metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

デプロイトークンの発行

次にこのレポジトリのデプロイトークンを発行します。"Settings > Repository settings > Deploy tokens"に行き、NameとScopesを入力して"Create deploy token"をクリックすれば発行できます。例えば次のようにしましょう。

  • name: web-app-manifests-deploy-token
  • scopes: read_repository
  • expiration dateとusernameはoptionalなので空欄のまま

Usernameとpasswordが生成されますので一時的にどこかに記録しておきましょう。

デプロイトークンをクラスタ上にsecretとして作成

Kubernetesクラスタのcontrol-planeにfluxをインストールしたかと思いますが、このflux cliをbootstrapぶりに使います。ドキュメント通りの命名となりますが、"flux-deployment-authentication"という名称のsecretをKubernetesクラスタ上に作成します。

flux create secret git flux-deploy-authentication \
  --url=https://gitlab.mydomain.net/gitops/web-app-manifests \
  --namespace=default \
  --username={deploy token username} \
  --password={deploy token password}

kubectl -n default get secrets flux-deploy-authentication -o yamlを実行し、secretができていることを確認しましょう。

flux-configに参照の追加

Kubernetesクラスタと連携させてあるflux-configレポジトリで、参照するソースの追加をしましょう。

例によってYAML形式のマニフェストファイルをレポジトリのclusters/my-clusterに配置するのですが、内容はflux cliを使って生成することができます。もしローカルにレポジトリがある状態であれば、以下で直接ファイルを用意できます。

flux create source git web-app-manifests \
  --url=https://gitlab.mydomain.net/gitops/web-app-manifests.git \
  --branch=main \
  --secret-ref=flux-deploy-authentication \
  --export > clusters/mycluster/web-app-manifests-source.yaml

ちなみに生成される内容は以下です。

---
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: web-app-manifests
  namespace: flux-system
spec:
  interval: 1m0s
  ref:
    branch: main
  secretRef:
    name: flux-deploy-authentication
  url: https://gitlab.mydomain.net/gitops/web-app-manifests.git

kustomizationの追加

参照すべき別のgitレポジトリの追加はこれでできました。最後にkustomizationの追加です。これもflux cliで生成できます。先に用意した"web-app-manifests"というgit sourceを対象に、そのレポジトリの"./"にあるマニフェストを対象とするkustomizationファイルになります。

つまり先程"web-app-manifests"レポジトリ上に用意したnginx-deployment.yamlの内容がKubernetesクラスタに反映されるわけです。

flux create kustomization nginx-source-kustomization \
  --namespace=default \
  --source=GitRepository/web-app-manifests \
  --path="./" \
  --prune=true \
  --interval=1m \
  --target-namespace=default \
  --export > clusters/mycluster/web-app-manifests-kustomization.yaml

生成される内容は以下です。

---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: nginx-source-kustomization
  namespace: default
spec:
  interval: 1m0s
  path: ./
  prune: true
  sourceRef:
    kind: GitRepository
    name: web-app-manifests
  targetNamespace: default

nginx-deployment完成

少し待つと、podがクラスタ上で実行されていることが確認できます。

# check the available deployment
❯ kubectl get deploy
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   1/1     1            1           61s

# check the pod
❯ kubectl get pod -o wide
NAME                               READY   STATUS    RESTARTS   AGE   IP            NODE     NOMINATED NODE   READINESS GATES
nginx-deployment-cbdccf466-8rvpf   1/1     Running   0          4s    10.244.2.13   rpi4   <none>           <none>

# check the servicecurl 10.244.2.13 -I
HTTP/1.1 200 OK
Server: nginx/1.14.2
......

metallbの利用

最後にmetallbも利用してこのNginxにLAN、192.168.1.0/24上のアドレスでアクセスできるようにしましょう。

"web-app-manifests"レポジトリで、追加のファイルとして用意しても良いですし、nginx-deployment.yamlファイルに追記する形でもよいです。Serviceを追加しましょう。以下の内容となります。

---
apiVersion: v1
kind: Service
metadata:
  name: web-app
  labels:
    app: nginx
spec:
  type: LoadBalancer
  selector:
    app: nginx
  ports:
    - name: http-svc
      protocol: TCP
      port: 80

https://metallb.universe.tf/usage/

After MetalLB is installed and configured, to expose a service externally, simply create it with spec.type set to LoadBalancer, and MetalLB will do the rest.

metallbが動作しているKubernetesクラスタでは、LoadBalancerタイプのServiceを用意するとそれだけでIPアドレスプールよりIPアドレスを選んで勝手にLAN上で見えるようになります。なんて便利なのでしょう。

それではserviceを確認してみましょう。以下の出力はcontrol-plane上で実行したcurlですが、LAN上の他のPCやスマホのブラウザなどからもアクセスできるようになっています!

# check service
❯ kubectl get svc
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)          AGE
kubernetes   ClusterIP      10.96.0.1       <none>          443/TCP          20d
web-app      LoadBalancer   10.99.119.177   192.168.1.201   80:31224/TCP     6s

# access 192.168.1.201:80 to verify access
❯ curl -I 192.168.1.201
HTTP/1.1 200 OK
Server: nginx/1.14.2
......

出来上がったもの

前回まででGitLabとまっさらなKubernetesクラスタを作りました。今回はそれらの上にいろいろ導入しました。

  • GitLabに"gitops"グループ作成
  • API利用権限を付与したグループアクセストークン発行
  • GitLabの"gitops"グループ下に新規プロジェクト・レポジトリ"flux-config"を作成
  • Kubernetesクラスタのcontrol-planeにfluxツールをインストール
  • flux bootstrapで"flux-config"レポジトリおよびKubernetesクラスタにfluxを導入
    • GitOps環境完成!!
    • "flux-config"の/clusters/my-cluster以下に配置したマニフェストなどはGitOpsで処理されるようになった
  • metallbのkustomizationや他のマニフェストを/clusters/my-cluster/metallb-systemに配置し、metallbをKubernetesクラスタに導入
  • GitLabに新規レポジトリ"web-app-manifests"を作成し、Nginxのdeploymentマニフェストファイルを配置
    • またこのレポジトリより、read-onlyのデプロイトークンを発行
  • flux cliを使い、トークンを"flux-deploy-authentication"という名称のKubernetesのsecretとして作成
  • "flux-config"の/clusters/my-cluster以下に"web-app-manifests"を外部git sourceとして登録するマニフェストを配置
    • またこのマニフェストにはデプロイトークン・secretを使うよう記載されている
  • 更に"flux-config"の/clusters/my-cluster以下に"web-app-manifests"レポジトリの内容を参照してkustomizationを走らせるというマニフェストファイルも配置
  • Nginx用にLoadBalancerタイプのServiceを作るよう、"web-app-manifests"上のマニフェストファイルに追記
    • Kubernetesクラスタ上にServiceが作られる
    • 同時に、metallbによって実ネットワークのIPアドレスでサービスにアクセスできる入り口が作られる

Discussion