DevCloudLab More courses at DevCloudLab.com →
FLUX CD · SECTION 2 · LECTURE 7

DevCloudLab

Implementing declarative infrastructure with Flux CD

Section 2, Lecture 7, from the Flux CD course by DevCloudLab.


What you’ll learn

Declarative vs. imperative

In an imperative model, you tell the system exactly what to do, step by step: run this command, then that one. In a declarative model, you describe the state you want, and something else is responsible for making it real and keeping it that way.

  Imperative Declarative
You specify The steps to reach a state The desired end state
Who acts You, one command at a time A controller, continuously
Example kubectl scale deployment/web --replicas=3 replicas: 3 committed to Git
Drift Nothing corrects it automatically Detected and reconciled automatically
Source of truth Whatever command ran last The Git repository

Flux CD is a declarative tool: you never tell it what to do. You tell it what you want, and its controllers figure out how to get there, and how to stay there.

How Flux reconciles state

Flux’s controllers run a continuous reconciliation loop: they watch the live state of your cluster and compare it against the state defined in your Git repository. When the two disagree, a controller acts to close the gap.

For example, say a Deployment manifest in Git specifies three replicas, but the running Pod count drops to two (a node was rescheduled, someone ran a manual kubectl scale, whatever the cause). Flux’s Source Controller notices the Git state hasn’t changed, but the Kustomize Controller notices the live cluster has drifted from it, and reapplies the manifest, bringing the replica count back to three. You never told Flux to “fix the replica count”. You told it, once, what the count should be, and it enforces that continuously.

Bootstrapping Flux

Before Flux can manage anything, it has to be installed on the cluster and pointed at a Git repository. This is bootstrapping:

flux bootstrap github \
  --owner=<your-github-user-or-org> \
  --repository=<your-repo-name> \
  --branch=main \
  --path=clusters/production \
  --personal

Bootstrapping installs Flux’s CRDs and controllers into the cluster, then commits the resulting configuration into the target repository and path. From that point on, anything you commit under that path is a candidate for Flux to apply: the repository becomes the cluster’s desired state.

Declaring infrastructure

Once Flux is bootstrapped, you describe your infrastructure in Git using one of three approaches, and Flux keeps the cluster in sync with whichever you choose.

Plain Kubernetes manifests. For something like an NGINX deployment, you write an ordinary Deployment manifest and commit it. Flux’s Source Controller detects the new commit; the Kustomize Controller applies the manifest. Change the image tag or the replica count in Git later, and Flux updates the live deployment to match, with no kubectl apply from your side.

Helm releases, managed through the HelmRelease custom resource. A HelmRelease installs a chart from a source you also declare, here a HelmRepository for the podinfo chart:

apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
  name: podinfo
  namespace: flux-system
spec:
  interval: 10m
  url: https://stefanprodan.github.io/podinfo
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: podinfo
  namespace: default
spec:
  interval: 5m
  chart:
    spec:
      chart: podinfo
      version: "6.x"
      sourceRef:
        kind: HelmRepository
        name: podinfo
        namespace: flux-system
  values:
    replicaCount: 3

Change a value or bump a chart version in this manifest, commit it, and the Helm Controller upgrades the release to match.

Kustomize overlays, for managing variations of the same manifests across environments. You keep a base configuration and layer environment-specific overlays (development, staging, production) on top of it. A Flux Kustomization then points Flux at the overlay directory for one environment:

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

This Flux Kustomization (a Flux object, not the kustomization.yaml file Kustomize reads) uses its own path, ./apps/production, which stays separate from the clusters/ path that bootstrap manages. The Kustomize Controller applies the resolved output of base plus overlay to the cluster, so the same base manifests can produce different results per environment without duplicating YAML.

Note: All three approaches share the same source of truth (the Git repository) and the same enforcement mechanism: Flux’s reconciliation loop. Which one you pick depends on how the software you’re deploying is packaged, not on any difference in how Flux treats them.

Keeping images fresh: Image Automation

Even with everything else declarative, someone still has to bump the image tag in the manifest whenever a new container image is built. Flux’s Image Automation feature closes this last gap: it watches a container registry, and when a new image matching your policy appears, it commits the updated tag to your Git repository itself. You still never touch the cluster directly. Flux just extends the same Git-as-source-of-truth model one step further back, into the build pipeline.

Further reading


DevCloudLab

Built by DevCloudLab
Hands-on cloud-native courses: Kubernetes, GitOps, CI/CD and the cloud.
Visit DevCloudLab.com →