GitOps on AKS with Flux: The Cluster That Configures Itself

A week of advanced AKS begins with the deployment model question. The pipelines from the DevOps posts push manifests to clusters; GitOps inverts it: the cluster runs an agent that pulls desired state from Git and reconciles continuously. The inversion sounds cosmetic and is not, it changes drift, security, and fleet scale fundamentally, and AKS ships it as the managed Flux extension.

Why Pull Beats Push at Scale

Three properties. Drift heals: a kubectl edit in production survives until Flux’s next reconciliation, minutes, then reality returns to Git, which is the deny assignment philosophy from the deployment stacks post implemented as convergence instead of prevention. Credentials invert: no pipeline holds cluster admin anymore, the cluster reaches out to Git and ACR with its own identity, so the CI system compromise scenario stops including “and then production kubeconfig”. And fleets flatten: cluster number twelve bootstraps by pointing the extension at the same repo, inheriting the platform stack (ingress, monitoring agents, policies, the September registry cache rules as manifests) with zero pipeline changes. The push pipelines do not disappear, they shrink to their honest job: build, test, scan, and commit a manifest change; delivery belongs to the cluster.

resource "azurerm_kubernetes_flux_configuration" "platform" {
  name       = "platform"
  cluster_id = azurerm_kubernetes_cluster.this.id
  namespace  = "flux-system"

  git_repository {
    url             = "https://github.com/contoso/aks-fleet-config"
    reference_type  = "branch"
    reference_value = "main"
  }

  kustomizations {
    name = "infra"
    path = "./infrastructure/production"
    sync_interval_in_seconds = 300
  }

  kustomizations {
    name                     = "apps"
    path                     = "./apps/production"
    depends_on               = ["infra"]
    sync_interval_in_seconds = 300
  }
}

Repo Structure and Layering

The structure that survives growth separates three concerns: infrastructure (the platform stack every cluster runs), apps (workload deployments), and clusters (the per cluster composition choosing which of the above and with what overlays). Kustomize does the layering, base manifests with environment overlays patching replicas, resources, and hostnames, and the depends_on between Kustomizations sequences platform before apps so CRDs exist before their consumers. Multi tenancy draws the boundary at namespaces: platform team owns the infrastructure Kustomizations cluster wide, application teams own Flux Kustomizations scoped to their namespaces pointing at their own repos, and the Azure Policy for Kubernetes from the hardening post keeps everyone inside their lines. Image automation closes the inner loop: Flux watches ACR for new tags matching a policy (semver ranges, not latest, per the registry post), commits the bump to Git itself, and the audit trail of what ran when becomes git log, which is the operational payoff people underestimate until their first incident review consists of reading commits.

Secrets and the Honest Caveats

Secrets do not go in Git, and GitOps does not change that: the Secrets Store CSI driver mounting Key Vault via workload identity (from the hardening post) remains the pattern, with Git holding only the SecretProviderClass references. The caveats worth stating: reconciliation intervals mean deployments are eventually consistent, so urgent rollbacks go through Git revert plus an immediate reconcile trigger, rehearse it; Helm releases under Flux want explicit values files in Git rather than inline mutations; and observability of the GitOps layer itself matters, alert on Kustomizations failing to reconcile, because a cluster silently three days behind Git is drift wearing a compliance badge. Adopt it platform stack first, one workload team second, fleet wide when the first two are boring, the same graduation logic as everything else in this series.

Cheers
Osama

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.