GitOps with Flux¶
Duration: 50 minutes (25 minutes theory + 25 minutes lab)
Introduction¶
GitOps is a way of implementing Continuous Deployment for cloud-native applications. You describe your entire system declaratively in Git, and updates are automatically deployed.
Key Principles:
- Declarative - entire system described declaratively in Git
- Versioned - Git as single source of truth
- Automatically pulled - agents pull changes from Git
- Continuously reconciled - actual state matches desired state
Flux is a CNCF project that implements GitOps for Kubernetes.
Why GitOps?¶
Traditional CI/CD:
- CI has cluster credentials (security risk)
- Push-based (manual or triggered)
- No drift detection
GitOps with Flux:
- Cluster pulls from Git (no external access needed)
- Continuous reconciliation
- Automatic drift detection and correction
- Git history = deployment audit log
Flux Architecture¶
Core Controllers:
- Source Controller - Fetches artifacts (Git repos, Helm repos, S3 buckets)
- Kustomize Controller - Applies Kustomize overlays
- Helm Controller - Manages Helm releases
- Notification Controller - Sends alerts (Slack, Discord, etc.)
- Image Automation Controllers - Automates image updates
Installing Flux¶
# Install Flux CLI
curl -s https://fluxcd.io/install.sh | sudo bash
# Verify installation
flux --version
# Bootstrap Flux on your cluster
flux bootstrap github \
--owner=your-username \
--repository=fleet-infra \
--path=clusters/my-cluster \
--personal
GitOps Repository Structure¶
Typical GitOps repo layout:
fleet-infra/
├── clusters/
│ ├── production/
│ │ ├── infrastructure.yaml
│ │ └── apps.yaml
│ └── staging/
│ ├── infrastructure.yaml
│ └── apps.yaml
├── infrastructure/
│ ├── nginx-ingress/
│ ├── cert-manager/
│ └── monitoring/
└── apps/
├── backend/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── kustomization.yaml
└── frontend/
├── deployment.yaml
├── service.yaml
└── kustomization.yaml
GitOps Source¶
Define where Flux should pull manifests from:
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: fleet-infra
namespace: flux-system
spec:
interval: 1m
url: https://github.com/user/fleet-infra
ref:
branch: main
Kustomization¶
Tell Flux what to apply from the repository:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: apps
namespace: flux-system
spec:
interval: 5m
sourceRef:
kind: GitRepository
name: fleet-infra
path: ./apps
prune: true
wait: true
timeout: 2m
Helm Releases with Flux¶
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bitnami
namespace: flux-system
spec:
interval: 1h
url: https://charts.bitnami.com/bitnami
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: nginx
namespace: default
spec:
interval: 5m
chart:
spec:
chart: nginx
version: '13.x.x'
sourceRef:
kind: HelmRepository
name: bitnami
namespace: flux-system
values:
replicaCount: 3
service:
type: LoadBalancer
Image Automation¶
Automatically update images when new versions are pushed:
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageRepository
metadata:
name: myapp
namespace: flux-system
spec:
image: docker.io/myorg/myapp
interval: 1m
---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImagePolicy
metadata:
name: myapp
namespace: flux-system
spec:
imageRepositoryRef:
name: myapp
policy:
semver:
range: '>=1.0.0 <2.0.0'
---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
name: myapp
namespace: flux-system
spec:
interval: 1m
sourceRef:
kind: GitRepository
name: fleet-infra
git:
checkout:
ref:
branch: main
commit:
author:
name: flux
email: flux@example.com
messageTemplate: 'Update image to {{range .Updated.Images}}{{println .}}{{end}}'
push:
branch: main
update:
path: ./apps
strategy: Setters
Notifications¶
Alert on deployments and failures:
apiVersion: notification.toolkit.fluxcd.io/v1beta1
kind: Alert
metadata:
name: slack-alert
namespace: flux-system
spec:
providerRef:
name: slack
eventSeverity: info
eventSources:
- kind: Kustomization
name: '*'
- kind: HelmRelease
name: '*'
---
apiVersion: notification.toolkit.fluxcd.io/v1beta1
kind: Provider
metadata:
name: slack
namespace: flux-system
spec:
type: slack
channel: deployments
secretRef:
name: slack-webhook
Multi-Tenancy¶
Isolate teams with separate namespaces and permissions:
apiVersion: v1
kind: Namespace
metadata:
name: team-a
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: team-a-apps
namespace: flux-system
spec:
serviceAccountName: team-a-reconciler
interval: 5m
sourceRef:
kind: GitRepository
name: fleet-infra
path: ./teams/team-a
prune: true
targetNamespace: team-a
Best Practices¶
- Separate repos - Infrastructure vs applications
- Environment branches - dev/staging/prod branches or directories
- Encrypted secrets - Use Sealed Secrets or SOPS
- Structured commits - Meaningful commit messages
- Health checks - Configure proper health assessment
- Notifications - Alert on failures
- Progressive delivery - Use Flagger for canary deployments
- RBAC - Least privilege for Flux service accounts
Flux CLI Commands¶
# Check Flux status
flux check
# Get resources
flux get sources git
flux get kustomizations
flux get helmreleases
# Suspend/resume reconciliation
flux suspend kustomization apps
flux resume kustomization apps
# Trigger immediate reconciliation
flux reconcile source git fleet-infra
flux reconcile kustomization apps
# Export resources
flux export source git fleet-infra
flux export kustomization apps
# View logs
flux logs --all-namespaces
flux logs --kind=Kustomization --name=apps
Handling Secrets¶
Option 1: Sealed Secrets¶
# Install Sealed Secrets controller
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.18.0/controller.yaml
# Seal a secret
kubectl create secret generic mysecret --from-literal=password=secret123 --dry-run=client -o yaml | \
kubeseal -o yaml > mysealedsecret.yaml
# Commit sealed secret to Git
git add mysealedsecret.yaml
git commit -m "Add sealed secret"
Option 2: Mozilla SOPS¶
# .sops.yaml
creation_rules:
- path_regex: .*.yaml
encrypted_regex: ^(data|stringData)$
age: age1...publickey...
# Encrypt secret
sops -e secret.yaml > secret.enc.yaml
# Flux decrypts automatically with decryption key
Complete GitOps Workflow¶
- Developer pushes code to application repo
- CI builds and pushes container image
- Image automation detects new tag
- Flux updates manifest in GitOps repo
- Flux reconciles and deploys to cluster
- Notification sent to team
- Monitoring detects issues (if any)
- Automatic rollback if health checks fail
Key takeaways¶
- GitOps treats Git as the single source of truth — the cluster state is always derived from a repository
- Flux pulls changes from Git rather than having CI push to the cluster, reducing the attack surface
- Continuous reconciliation automatically corrects any drift between the desired state in Git and the actual cluster state
- Secrets must be encrypted before committing — use Sealed Secrets or SOPS to keep sensitive data safe in Git
- Image automation allows Flux to update manifests automatically when new container images are pushed
Check your understanding¶
- What is the key difference between a push-based and a pull-based deployment model?
- Which Flux controller is responsible for applying Kustomize overlays?
- How does GitOps help with audit trails?
- What is the purpose of the
prune: trueoption in a Kustomization? - Why should secrets be encrypted before storing them in a GitOps repository?
Solution
- In push-based deployments, a CI system sends changes to the cluster (requiring cluster credentials in CI); in pull-based, an in-cluster agent fetches and applies changes from Git
- The Kustomize Controller
- Every change is a Git commit, providing a complete, timestamped history of who changed what and when
- It tells Flux to delete resources from the cluster that are no longer present in the Git repository
- Anyone with read access to the repository would be able to see plain-text credentials; encryption ensures secrets are safe even in public repos
Hands-on¶
Apply the concepts from this section in the lab exercises.
Next section¶
Once you've reviewed the content and completed the lab, proceed to the next section.