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, bootstrap conceptual diagram
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
GitHub example repo used for test deployment, google-hello-app-logging-multiarch