blog.maisumvictor.dev — zsh

GitOps with FluxCD: A Practical Guide

GitOps with FluxCD: A Practical Guide
blog.maisumvictor.dev — zsh

GitOps with FluxCD: A Practical Guide

GitOps changed how we deploy to Kubernetes. Instead of pushing changes, we pull them from Git. FluxCD is the engine that makes this happen.

What is GitOps?

GitOps has four key principles:

  1. Declarative: System state defined in Git
  2. Versioned: All changes are versioned and auditable
  3. Pulled: Agents pull changes automatically
  4. Continuously Reconciled: Drift is automatically corrected

Installing FluxCD

# Install Flux CLI
curl -s https://fluxcd.io/install.sh | sudo bash

# Verify installation
flux --version

# Check cluster compatibility
flux check --pre

Bootstrapping Flux

# Set your GitHub credentials
export GITHUB_TOKEN=<your-token>
export GITHUB_USER=maisumvictor

# Bootstrap Flux in your cluster
flux bootstrap github \
  --owner=$GITHUB_USER \
  --repository=flux-infra \
  --branch=main \
  --path=./clusters/production \
  --personal

This creates a flux-infra repository with the following structure:

flux-infra/
├── clusters/
│   └── production/
│       ├── flux-system/           # Core Flux components
│       ├── infrastructure.yaml    # Infrastructure apps
│       └── apps.yaml              # Application deployments
├── infrastructure/
│   ├── nginx-ingress/
│   ├── cert-manager/
│   └── monitoring/
└── apps/
    ├── base/
    └── production/

The GitOps Workflow

1. Source Configuration

# clusters/production/flux-system/gotk-sync.yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: flux-system
  namespace: flux-system
spec:
  interval: 1m0s
  ref:
    branch: main
  secretRef:
    name: flux-system
  url: https://github.com/maisumvictor/flux-infra

2. Kustomization for Infrastructure

# clusters/production/infrastructure.yaml
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: infrastructure
  namespace: flux-system
spec:
  interval: 1h
  retryInterval: 1m
  path: ./infrastructure/overlays/production
  prune: true
  sourceRef:
    kind: GitRepository
    name: flux-system
  healthChecks:
    - apiVersion: apps/v1
      kind: Deployment
      name: cert-manager
      namespace: cert-manager
  timeout: 5m

3. Application Deployment

# clusters/production/apps.yaml
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: apps
  namespace: flux-system
spec:
  interval: 5m
  path: ./apps/production
  prune: true
  sourceRef:
    kind: GitRepository
    name: flux-system
  dependsOn:
    - name: infrastructure

Helm Releases with Flux

# infrastructure/nginx-ingress/release.yaml
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: nginx-ingress
  namespace: ingress-nginx
spec:
  interval: 5m
  chart:
    spec:
      chart: ingress-nginx
      version: "4.8.x"
      sourceRef:
        kind: HelmRepository
        name: ingress-nginx
        namespace: flux-system
      interval: 1m
  values:
    controller:
      replicaCount: 2
      service:
        type: LoadBalancer
        annotations:
          service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
      metrics:
        enabled: true
        serviceMonitor:
          enabled: true
      podAnnotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "10254"

Image Automation

Automatically deploy new container images:

# apps/production/image-policy.yaml
---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
  name: myapp
  namespace: flux-system
spec:
  image: ghcr.io/maisumvictor/myapp
  interval: 1m
  secretRef:
    name: ghcr-auth
---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
  name: myapp
  namespace: flux-system
spec:
  imageRepositoryRef:
    name: myapp
  policy:
    semver:
      range: "1.x.x"
---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
  name: myapp
  namespace: flux-system
spec:
  interval: 1m
  sourceRef:
    kind: GitRepository
    name: flux-system
  git:
    checkout:
      ref:
        branch: main
    commit:
      author:
        name: Flux Bot
        email: [email protected]
      messageTemplate: |
        Automated image update
        
        Images:
        {{ range .Updated.Images -}}
        - {{.}}
        {{ end }}
      signingKey:
        secretRef:
          name: flux-gpg-signing-key
    push:
      branch: main
  policy:
    automerge: true

Monitoring with Prometheus

# infrastructure/monitoring/prometheus.yaml
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: kube-prometheus-stack
  namespace: monitoring
spec:
  interval: 5m
  chart:
    spec:
      chart: kube-prometheus-stack
      version: "55.x.x"
      sourceRef:
        kind: HelmRepository
        name: prometheus-community
        namespace: flux-system
  values:
    prometheus:
      prometheusSpec:
        retention: 30d
        storageSpec:
          volumeClaimTemplate:
            spec:
              storageClassName: gp3
              resources:
                requests:
                  storage: 50Gi
    grafana:
      enabled: true
      adminPassword: "changeme"
      ingress:
        enabled: true
        hosts:
          - grafana.maisumvictor.dev

Alerting with Slack

# infrastructure/monitoring/alerts.yaml
---
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Provider
metadata:
  name: slack
  namespace: flux-system
spec:
  type: slack
  channel: alerts
  secretRef:
    name: slack-webhook
---
apiVersion: notification.toolkit.fluxcd.io/v1beta3
kind: Alert
metadata:
  name: flux-alerts
  namespace: flux-system
spec:
  providerRef:
    name: slack
  eventSeverity: error
  eventSources:
    - kind: GitRepository
      name: '*'
    - kind: Kustomization
      name: '*'
    - kind: HelmRelease
      name: '*'

Common Commands

# Check Flux status
flux get all

# Check specific kustomization
flux get kustomizations --watch

# Suspend/Resume reconciliation
flux suspend kustomization apps
flux resume kustomization apps

# Force reconciliation
flux reconcile kustomization apps --with-source

# Check logs
flux logs --level=error

# Export current state
flux export all > backup.yaml

Key Takeaways

  1. Git is the single source of truth — All changes go through PRs
  2. Flux handles the complexity — You define what, Flux handles how
  3. Drift detection is automatic — Manual changes are reverted
  4. Rollback is just a git revert — Simple and familiar

The complete Flux configuration is in my flux-infra repository.