How Flux works: GitRepository, Kustomization and Receiver

Flux is a set of Kubernetes controllers that keeps your cluster in sync, based on configuration changes you do in a Git repository instead of running kubectl by hand.

A simple Flux setup can be explained with three resources, each one answering a different question:

Let us start with the where.

GitRepository: where

The GitRepository resource tells Flux which repository to watch and how often to check it. It does not apply anything. It only fetches and makes the content available to other resources as a source.

apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: my-config
  namespace: flux-system # A K8s namespace
spec:
  interval: 1m
  url: https://github.com/my-org/my-config
  ref:
    branch: main

Kustomization: what

The Kustomization resource points at a source and says which part of it to apply. Plain manifests in a folder work fine.

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: my-app
  namespace: flux-system
spec:
  interval: 10m
  sourceRef:
    kind: GitRepository
    name: my-config
  path: "./clusters/production"
  prune: true

Receiver: when

The two resources above are enough for a working GitOps setup. Without a webhook, Flux only notices a new commit at the next GitRepository poll. A Receiver exposes a webhook, so GitHub or GitLab can tell Flux about a push the moment it happens, instead of waiting for that poll.

The Receiver needs a Secret holding a random token, which you also give to the webhook on the provider side:

TOKEN="$(openssl rand -hex 32)"

kubectl -n flux-system create secret generic receiver-token \
  --from-literal=token="$TOKEN"

echo "$TOKEN"
apiVersion: notification.toolkit.fluxcd.io/v1
kind: Receiver
metadata:
  name: my-config-receiver
  namespace: flux-system
spec:
  type: github
  events:
    - push
  secretRef:
    name: receiver-token
  resources:
    - kind: GitRepository
      name: my-config

To get the webhook path run the following, when the Receiver is ready:

kubectl -n flux-system get receiver my-config-receiver -o jsonpath='{.status.webhookPath}'

Combine that path with the hostname of an ingress that exposes the notification controller’s webhook-receiver Service, and that full URL is what goes into the webhook settings in GitHub or GitLab.

Putting it together

Three resources, three questions:

Resource Question Key field
GitRepository Where does configuration come from? spec.url
Kustomization What gets applied? spec.path
Receiver When should we check immediately? spec.resources

Start with the first two and let it poll. Add the Receiver as an optimisation, not a requirement. A cluster that only polls is still a correct GitOps setup.

GitRepository is one kind of source, and HelmRepository and OCIRepository are others.

Kustomization is one kind of applier, and HelmRelease is another.

Not every source fits every applier, though: a Kustomization cannot read from a HelmRepository, because Helm charts are handled by HelmRelease.

Feel free to drop me a comment, if you found this article helpful.

comments powered by Disqus