A cluster’s lifecycle, from creation to destruction, can be fully automated using Flux, turning your Kubernetes infrastructure into a declarative GitOps workflow.
Let’s see what that looks like in practice. Imagine we have a Git repository with a cluster-config directory. Inside, we have a clusters/dev directory, which contains manifests for a development cluster.
# clusters/dev/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: my-app
# clusters/dev/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-deployment
namespace: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app-container
image: nginx:latest
ports:
- containerPort: 80
Now, we configure Flux to watch this Git repository and apply these manifests to our dev cluster.
First, we install Flux into the cluster. This typically involves running a command like:
flux bootstrap git \
--url=ssh://git@github.com/your-org/your-repo.git \
--branch=main \
--path=./clusters/dev \
--personal
This command does a few key things:
- Installs Flux controllers: It deploys the necessary Flux components (Source Controller, Kustomize Controller, Helm Controller, Notification Controller) into your cluster.
- Configures Git access: It sets up authentication (SSH in this case) for Flux to pull from your Git repository.
- Defines a
GitRepositoryresource: This tells Flux where to look for manifests. - Defines a
Kustomizationresource: This tells Flux what to apply from the repository and where (which path, which namespace).
The GitRepository resource Flux creates might look something like this internally:
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
name: cluster-config
namespace: flux-system # Flux's namespace
spec:
interval: 1m0s
url: ssh://git@github.com/your-org/your-repo.git
ref:
branch: main
secretRef:
name: flux-system-git-auth # The secret Flux created for SSH keys
And the Kustomization resource, which points to the manifests:
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: dev-cluster-apps
namespace: flux-system
spec:
interval: 5m0s
path: ./clusters/dev # Path within the Git repository
prune: true # Automatically delete resources removed from Git
sourceRef:
kind: GitRepository
name: cluster-config # Refers to the GitRepository above
targetNamespace: dev # Apply resources in this namespace on the cluster (optional, if not specified, defaults to Git path)
Once these are in place, Flux’s Source Controller continuously watches the specified Git repository. When it detects a change (a new commit), it pulls the latest manifests. The Kustomize Controller then compares these desired states with the actual state in the cluster and applies any necessary changes (creation, updates, or deletions).
This system elegantly solves the problem of drift. Because Flux is constantly reconciling the cluster’s state with Git, any manual changes made directly to the cluster (e.g., via kubectl apply) will be automatically reverted by Flux on its next reconciliation cycle.
You can also manage more complex scenarios. For instance, you might have a base set of manifests in ./clusters/base and then create specific Kustomization resources for different environments that overlay or extend the base.
# clusters/prod/kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: prod-cluster-apps
namespace: flux-system
spec:
interval: 5m0s
path: ./clusters/prod # Path for production specific manifests
prune: true
sourceRef:
kind: GitRepository
name: cluster-config
patches: # Apply patches to the base configuration
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-deployment
spec:
replicas: 10 # Scale up for production
target:
kind: Deployment
name: my-app-deployment
namespace: my-app
The truly surprising part is how seamlessly Flux integrates with Git hooks and CI/CD pipelines. You can set up a pipeline that, upon merging to your Git repository’s main branch, triggers a commit to your cluster-config repository. Flux then picks up this commit, and your entire cluster infrastructure is updated without any direct human intervention on the cluster itself.
The core components of Flux (Source, Kustomize, Helm, Notification Controllers) work in concert. The Source Controller fetches artifacts from external sources (Git, OCI registries, Helm repositories). The Kustomize Controller applies Kubernetes manifests defined in Kustomizations. The Helm Controller manages Helm releases. The Notification Controller handles events and alerts. Together, they provide a robust, event-driven GitOps engine for your cluster.
When you’re managing multiple clusters, you’ll often create a top-level GitRepository pointing to your main configuration repo, and then have individual Kustomization resources for each cluster (clusters/dev, clusters/staging, clusters/prod). Each of these Kustomization resources will then reference the appropriate path within that single GitRepository. This allows for a single source of truth for all your cluster configurations, with environment-specific variations managed through path segregation or Kustomize overlays.
Managing secrets across environments requires careful consideration. While you can store secrets directly in Git, it’s generally discouraged for anything sensitive. Flux integrates well with tools like External Secrets Operator or Vault to fetch secrets from external stores at runtime, rather than committing them.
The next logical step is to explore how to integrate Flux with CI/CD pipelines to automate the Git commit process itself.