Lesson 19 GitOps with Helm
Theme:

Lab Guide: GitOps with Helm

Three hands-on exercises covering ArgoCD Application setup, FluxCD HelmRelease configuration, and drift detection.

Prerequisites

A running Kubernetes cluster (minikube, kind, or cloud). kubectl, helm, and git installed. For Exercise 1-2, you need ArgoCD or FluxCD installed (instructions below). A GitHub/GitLab repo you can push to.

Exercise 1: Deploy a Helm Chart with ArgoCD

Create an ArgoCD Application that deploys an nginx chart from an OCI registry with environment-specific values.

Step 1 — Install ArgoCD (if not already running)

kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# Wait for pods to be ready
kubectl wait --for=condition=Ready pods --all -n argocd --timeout=120s

# Get the admin password
argocd admin initial-password -n argocd

Step 2 — Create a GitOps repository structure

Create a new Git repository (or a directory in an existing one) with this layout:

mkdir -p gitops-lab/apps/nginx
cd gitops-lab

Step 3 — Create the values file

Create apps/nginx/values-dev.yaml:

# apps/nginx/values-dev.yaml
replicaCount: 2
service:
  type: ClusterIP
  port: 80
resources:
  requests:
    cpu: 50m
    memory: 64Mi
  limits:
    cpu: 100m
    memory: 128Mi

Step 4 — Create the ArgoCD Application manifest

Create apps/nginx/application.yaml:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: nginx-dev
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://charts.bitnami.com/bitnami
    chart: nginx
    targetRevision: 15.*
    helm:
      releaseName: nginx-dev
      valueFiles:
        - $values/apps/nginx/values-dev.yaml
  # Reference to your GitOps repo for values files
  sources:
    - repoURL: https://charts.bitnami.com/bitnami
      chart: nginx
      targetRevision: 15.*
      helm:
        releaseName: nginx-dev
        valueFiles:
          - $values/apps/nginx/values-dev.yaml
    - repoURL: https://github.com/YOUR_USER/gitops-lab.git
      targetRevision: main
      ref: values
  destination:
    server: https://kubernetes.default.svc
    namespace: nginx-dev
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

Note

Replace YOUR_USER with your GitHub username. The $values ref allows ArgoCD to pull values files from a separate Git repo while using a Helm chart from a chart repository.

Step 5 — Apply and verify

# Push to your Git repo first
git add . && git commit -m "add nginx ArgoCD application" && git push

# Apply the Application
kubectl apply -f apps/nginx/application.yaml

# Check sync status
argocd app get nginx-dev
# Or via kubectl:
kubectl get application nginx-dev -n argocd -o jsonpath='{.status.sync.status}'

✓ Check: The Application shows Synced and Healthy. Two nginx pods are running in the nginx-dev namespace.

Exercise 2: Deploy with FluxCD HelmRelease

Set up a FluxCD HelmRepository and HelmRelease to deploy Redis with values from a ConfigMap.

Step 1 — Install Flux (if not already running)

# Install the Flux CLI
brew install fluxcd/tap/flux   # macOS
# or: curl -s https://fluxcd.io/install.sh | sudo bash

# Bootstrap Flux into your cluster
flux bootstrap github \
  --owner=YOUR_USER \
  --repository=gitops-lab \
  --branch=main \
  --path=clusters/dev \
  --personal

Step 2 — Create the HelmRepository source

Create clusters/dev/sources/bitnami.yaml:

apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
  name: bitnami
  namespace: flux-system
spec:
  type: oci
  url: oci://registry-1.docker.io/bitnamicharts
  interval: 10m

Step 3 — Create a ConfigMap for values

Create clusters/dev/redis/values-configmap.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-values
  namespace: caching
data:
  values.yaml: |
    architecture: replication
    replica:
      replicaCount: 2
    auth:
      enabled: true
    metrics:
      enabled: true
      serviceMonitor:
        enabled: false

Step 4 — Create the HelmRelease

Create clusters/dev/redis/helmrelease.yaml:

apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: redis
  namespace: caching
spec:
  interval: 30m
  chart:
    spec:
      chart: redis
      version: "18.x"
      sourceRef:
        kind: HelmRepository
        name: bitnami
        namespace: flux-system
      interval: 10m
  install:
    createNamespace: true
  valuesFrom:
    - kind: ConfigMap
      name: redis-values
      valuesKey: values.yaml
  values:
    commonLabels:
      managed-by: flux

Step 5 — Commit, push, and verify

git add . && git commit -m "add redis HelmRelease" && git push

# Force Flux to reconcile immediately
flux reconcile source git flux-system

# Check HelmRelease status
flux get helmrelease -n caching
kubectl get pods -n caching

✓ Check: flux get helmrelease -n caching shows Ready: True. Redis master and replica pods are running.

Exercise 3: Drift Detection and Self-Healing

Simulate manual cluster changes and observe how GitOps agents detect and correct drift.

Step 1 — Introduce drift manually

Pick whichever tool you set up in Exercise 1 or 2, then make a direct change:

# Scale the deployment manually (bypassing Git)
kubectl scale deployment nginx-dev --replicas=5 -n nginx-dev

# Or for the Redis exercise:
kubectl scale statefulset redis-master --replicas=0 -n caching

Step 2 — Observe drift detection

ArgoCD:

# Check if ArgoCD detects the drift
argocd app get nginx-dev

# Look for "OutOfSync" status
# With selfHeal enabled, watch it revert:
kubectl get pods -n nginx-dev -w

FluxCD:

# Check the HelmRelease status
flux get helmrelease redis -n caching

# Watch the reconciliation revert the change
kubectl get pods -n caching -w

Step 3 — Perform a GitOps rollback

Make a change via Git (e.g., update the replica count in your values file), push, wait for sync, then roll back:

# Edit values-dev.yaml: change replicaCount to 4
git add . && git commit -m "scale to 4 replicas" && git push

# Wait for sync (verify 4 pods running)
# Now rollback using git revert
git revert HEAD --no-edit
git push

# Watch the agent revert to the original replica count
kubectl get pods -n nginx-dev -w

Step 4 — Document your observations

Answer these questions:

✓ Check: You observed drift detection and automatic correction. You successfully performed a rollback via git revert and the cluster converged to the reverted state.

Cleanup

# Remove ArgoCD application
argocd app delete nginx-dev --cascade

# Remove Flux resources
flux delete helmrelease redis -n caching
flux delete source helm bitnami

# Or remove everything
kubectl delete namespace argocd nginx-dev caching