Flux: installing Flux on a Kubernetes cluster with bootstrap command

Flux is a GitOps continuous delivery tool that provides a framework for keeping a Kubernetes cluster in-sync with source git repositories, OCI registries, and published Helm charts [1].

The recommended way to install Flux on a Kubernetes cluster is to bootstrap using the Flux CLI, so I will go through those details in this article.

Installing the Flux CLI

Below are instructions for install the Flux CLI tool on Debian/Ubuntu.

# ensure json parser is installed
sudo apt install jq -y

# discover latest version of flux available, then fetch/extract
latest_flux_linux=$(curl -sL https://api.github.com/repos/fluxcd/flux2/releases/latest | jq -r ".assets[].browser_download_url" | grep linux_amd64.tar.gz)
wget $latest_flux_linux
tar xvfz $(basename $(echo $latest_flux_linux))
sudo mv flux /usr/local/bin/.

# validate installation
flux --version

Validate Flux connection to Kubernetes cluster

The Flux bootstrap process will need to connect to the Kubernetes cluster to install custom CRD and Flux-related workloads.  Make sure your kubeconfig is setup and kubectl can properly make the connection to the Kubernetes API.

# test basic connection to cluster
$ kubectl get pods -A

# test connection to cluster via flux CLI
$ flux check --pre
► checking prerequisites
✔ Kubernetes 1.32.1 >=1.30.0-0
✔ prerequisites checks passed

As stated in the documentation, cluster admin rights are required to bootstrap Flux.

Create GitHub repository used for Flux bootstrap

The Flux “bootstrap” procedure installs custom CRD and a minimal Flux footprint onto the Kubernetes cluster, then pushes a set of yaml manifests representing the full Flux-related workload to a git repository.

Create new Git Repository

Using the GitHub web UI, create a new git repository.  I am using the name “flux-bootstrap” for this article.

Then create a file on the ‘main’ branch as a placeholder.  The Flux bootstrap procedure will place content into this repo in a later step.

This could be done from the web UI, but here is an example of the CLI commands.

mkdir flux-bootstrap && cd $_
git init
echo "# flux bootstrap repo" > README.md
git add README.md 
git commit -am "init"
git branch -M main
git remote set-url origin git@github.com:fabianlee/flux-bootstrap.git
git push -u origin main

Create Personal Access Token for this git repo

Create a new fine-grained Personal Access Token with privileges only to this repository that the Flux CLI tool will use to add the yaml bootstrap files to this git repository.

From the GitHub web UI, go to Settings > Personal Access Tokens > Fine Grained Tokens and press “Generate new token”.

Choose any name for the token, I am using “forflux”, and under Repository Access, choose the radio button “Only select repositories”, and pick the flux-bootstrap repo created in the last step.  This ensure this token is limited to operations on just this single git repository.

Under the Permissions section, modify “Contents” to “Read and write”.  This is the only access needed.

Go to the bottom of the page and press “Generate token”.  Immediately copy the returned token, this is the only time it will ever be shown.

From the command line, export this token value in preparation for running the Flux CLI bootstrap.

export GITHUB_TOKEN=github_pat_xxxxxxx

Run Flux bootstrap

With the connection to the Kubernetes cluster proven out, and the GITHUB_TOKEN environment variable now defined, we should be able to invoke the Flux bootstrap process.

$ flux bootstrap github \
  --token-auth \
  --owner=fabianlee \
  --repository=flux-bootstrap \
  --branch=main \
  --path=clusters/my-cluster \
  --personal=true --private=false \
  --log-level debug --verbose

► connecting to github.com
► cloning branch "main" from Git repository "https://github.com/fabianlee/flux-bootstrap.git"
✔ cloned repository
► generating component manifests
✔ generated component manifests
✔ committed component manifests to "main" ("5e7bbe63af9e8d3dadd1587301d0d3753ab82d0e")
► pushing component manifests to "https://github.com/fabianlee/flux-bootstrap.git"
► installing components in "flux-system" namespace
✔ installed components
✔ reconciled components
► determining if source secret "flux-system/flux-system" exists
► generating source secret
► applying source secret "flux-system/flux-system"
✔ reconciled source secret
► generating sync manifests
✔ generated sync manifests
✔ committed sync manifests to "main" ("7a1339f49cfffbcc282b12ba6334a8300790c12c")
► pushing sync manifests to "https://github.com/fabianlee/flux-bootstrap.git"
► applying sync manifests
✔ reconciled sync configuration
◎ waiting for GitRepository "flux-system/flux-system" to be reconciled
✔ GitRepository reconciled successfully
◎ waiting for Kustomization "flux-system/flux-system" to be reconciled
✔ Kustomization reconciled successfully
► confirming components are healthy
✔ helm-controller: deployment ready
✔ kustomize-controller: deployment ready
✔ notification-controller: deployment ready
✔ source-controller: deployment ready
✔ all components are healthy

Validate Flux bootstrap results

Kubernetes cluster after bootstrap

There will now be a ‘flux-system’ namespace in the Kubernetes cluster, and we can check for the expected Flux deployments and the secret which contains the PAT used to authenticate against the bootstrap git repository.

# list CRD created
$ kubectl api-resources | grep flux

# list Flux workloads
$ kubectl get deployment,services -n flux-system
NAME                                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/helm-controller           1/1     1            1           2m7s
deployment.apps/kustomize-controller      1/1     1            1           2m7s
deployment.apps/notification-controller   1/1     1            1           2m7s
deployment.apps/source-controller         1/1     1            1           2m7s

NAME                              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
service/notification-controller   ClusterIP   10.152.183.224           80/TCP    2m7s
service/source-controller         ClusterIP   10.152.183.173           80/TCP    2m7s
service/webhook-receiver          ClusterIP   10.152.183.180           80/TCP    2m7s

# githut PAT used for repo access
$ kubectl get secrets -n flux-system
NAME          TYPE     DATA   AGE
flux-system   Opaque   2      2m41s

Git repository after bootstrap

Per the path used during bootstrap, “clusters/my-cluster“, if we look at the git repository it will now be populated with a set of Kustomization files that represent the remaining Flux-related workload.

# fetch latest files from git repo
$ git pull -r

# files added to repo by flux bootstrap
$ find clusters/my-cluster
clusters/my-cluster
clusters/my-cluster/flux-system
clusters/my-cluster/flux-system/gotk-sync.yaml
clusters/my-cluster/flux-system/gotk-components.yaml
clusters/my-cluster/flux-system/kustomization.yaml

Note that any additional files placed directly into the ‘flux-system’ folder (or subfolder) will also be deployed by Flux.

Test Flux deployment

I have a completely separate GitHub repository, google-hello-app-logging-multiarch, that builds a small GoLang web server into a Docker/OCI image and has a ‘/kustomize/‘ directory containing a kustomization.yaml that can deploy this simple workload to a Kubernetes cluster as a deployment+service.

If we want the cluster-level Flux integration to deploy this simple workload for us, we need to instantiate two Flux custom resource definitions into a new “github-hello” namespace:

  • GitRepository.source.toolkit.fluxcd.io – defines the source git repository and branch to monitor. For this example, the main branch of google-hello-app-logging-multiarch is watched for changes
  • Kustomize.kustomize.toolkit.fluxcd.io – based on events from the GitRepository above, runs Kustomize at a certain folder path (‘/kustomize/‘) in the target namespace, github-hello.
# create target k8s namespace
kubectl get ns github-hello || kubectl create ns github-hello

# create Flux GitRepository object that represents source repo
$ flux create source git github-hello \
  --url=https://github.com/fabianlee/google-hello-app-logging-multiarch \
  --branch=main \
  --interval=1m \
  --namespace=github-hello \
  --export | tee /dev/tty | kubectl apply -f -


# check readiness of new Flux source repo, status should be Ready=True
$ kubectl get GitRepository -n github-hello
NAME    URL                                                               AGE     READY   STATUS
hello   https://github.com/fabianlee/google-hello-app-logging-multiarch   3m47s   True    stored artifact for revision 'main@sha1:36d821e0b645b000e6f6d1bc1d0e13a16b50d37a'

# create Flux Kustomization object that invokes kustomize at specific folder path
$ flux create kustomization github-hello \
  --namespace=github-hello --target-namespace=github-hello \
  --source=github-hello \
  --path="/kustomize/" \
  --prune=true \
  --wait=true \
  --interval=1m \
  --retry-interval=2m \
  --health-check-timeout=3m \
  --export | tee /dev/tty | kubectl apply -f -


# readiness of Flux Kustomization object, status should eventually go to Ready=True
$ kubectl get kustomization -n github-hello
NAME AGE READY STATUS
github-hello 22m True Applied revision: main@sha1:3c518e65dc7c1a4f4327a674c1b443d47cc121bf

# once Kustomization is ready and reconciled, the workload will be deployed
$ kubectl get deployment,service -n github-hello
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/golang-hello-world-web-logging 1/1 1 1 94s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/golang-hello-world-web-logging-service ClusterIP 10.152.183.34 8080/TCP 94s

 

REFERENCES

Flux site

Flux, bootstrap conceptual diagram

Flux integration examples

Flux community Helm charts for installation (instead of bootstrap)

bootstrap github repository for this article, flux-bootstrap

Flux docs, configuring a web proxy and NO_PROXY for cluster egress

Flux2 issues, need to add DNS server to flux NO_PROXY

Flux docs, using podinfo as example for Flux deployment

Flux, managing Helm releases

GitHub example repo used for test deployment, google-hello-app-logging-multiarch