Command Palette

Search for a command to run...

Hectal

Mission 2.4 · Stage 2 — GitOps at Scale: Secrets, Rollouts, and Many Clusters

Flux, Promotion Pipelines, and Running GitOps in Production

Goal: Know Flux well enough to work in a Flux shop, automate environment promotion, and harden Argo CD for production: SSO, RBAC, HA, notifications, metrics, and disaster recovery.

50 min Free 5 steps 1 break-it drills

By the end of this mission

  • Map Flux's controllers and CRDs to Argo CD's concepts
  • Automate image updates and promotion safely
  • Secure Argo CD with SSO, RBAC, and least-privilege projects
  • Monitor GitOps itself and rebuild a cluster from Git

Part 1

Understand it first

Flux in one page

Flux is the other CNCF-graduated GitOps engine, built as a set of small controllers with no central UI by default. SOURCE CONTROLLER fetches GitRepository, OCIRepository, and HelmRepository sources. KUSTOMIZE CONTROLLER applies a Kustomization (Flux's CRD, not to be confused with kustomization.yaml) from a source, with prune and health checks. HELM CONTROLLER reconciles HelmRelease objects (real Helm releases, visible in helm list). IMAGE AUTOMATION controllers scan registries and commit new tags back to Git. NOTIFICATION CONTROLLER sends alerts and receives webhooks.

Rough mapping: Argo Application ≈ Flux Kustomization or HelmRelease; Argo app-of-apps ≈ Flux Kustomizations with dependsOn; Argo Image Updater ≈ Flux image automation. Choose Argo CD for its UI, multi-cluster hub, and ApplicationSets; choose Flux for a lightweight, fully CRD-driven, per-cluster setup with native SOPS support. Both do the job.

Promotion

Manual promotion PRs (Mission 1.4) are safe and auditable but slow at scale. Options, from simplest: CI opens the promotion PR automatically after dev's rollout is healthy (Argo notification → webhook → workflow); a promotion tool such as Kargo models stages (dev → staging → prod) and promotes verified 'freight' (image + config versions) through them with gates; or image automation per environment with semver policies. Whatever the tool, the principles stay: promote immutable artifacts, never rebuild per environment, and record every promotion in Git.

Hardening Argo CD

SSO (OIDC via Dex or direct) with groups mapped in argocd-rbac-cm: developers can view and sync their project's apps, only the platform team can edit projects and clusters, and the local admin is disabled. PROJECTS restrict repos, destinations, and kinds per team. HA manifests run multiple replicas of the API server and repo server, Redis HA, and a sharded application controller for large fleets. The repo server executes rendering tools, so keep config-management plugins minimal and resource-limited.

Treat Argo CD as production software: scrape its metrics (argocd_app_info for sync and health per app, argocd_app_sync_total, reconcile duration, repo-server Git request latency), alert on apps Degraded or OutOfSync for too long, and send notifications to the owning team's channel.

Disaster recovery

Because desired state is in Git, rebuilding a cluster is: provision it (Terraform), install Argo CD, apply the root Application, and wait. What Git does NOT contain: secret values (they're in the secret manager, via ESO), the sealed-secrets key (backed up), persistent data (volume snapshots and database backups, see Velero and the Stateful Systems course), and Argo's own cluster credentials. Practise the rebuild; a DR plan that's never been run is a hope, not a plan (SRE course).

Part 2

Your project after this mission · 4 files change

shoplite-gitops/
  • shoplite-gitops/
    • flux-example/
      • clusters/
        • dev/
          • web.yamlnew
    • platform/
      • argocd/
        • argocd-notifications-cm.yamlnew
        • argocd-rbac-cm.yamlnew
      • monitoring/
        • argocd-alerts.yamlnew

Part 3

Build it, step by step

  1. 1

    The same app, the Flux way

    For comparison, here's what ShopLite web in dev looks like in a Flux repo. flux bootstrap github installs Flux and commits its own manifests into the repo, so Flux manages itself from the start.

    shoplite-gitops/flux-example/clusters/dev/web.yamlwhole fileyaml
    apiVersion: source.toolkit.fluxcd.io/v1
    kind: GitRepository
    metadata:
      name: shoplite-gitops
      namespace: flux-system
    spec:
      interval: 1m
      url: https://github.com/<you>/shoplite-gitops
      ref: { branch: main }
    ---
    apiVersion: kustomize.toolkit.fluxcd.io/v1
    kind: Kustomization
    metadata:
      name: web-dev
      namespace: flux-system
    spec:
      interval: 10m
      sourceRef: { kind: GitRepository, name: shoplite-gitops }
      path: ./apps/web/overlays/dev
      prune: true
      wait: true
      timeout: 3m
      dependsOn:
        - name: external-secrets
    terminal
    $ flux bootstrap github --owner=<you> --repository=shoplite-flux --path=clusters/dev
    flux get kustomizations
    ── expected output ──
    NAME REVISION SUSPENDED READY MESSAGE
    flux-system main@sha1:4e5f6a7b False True Applied revision: main@sha1:4e5f6a7b
    web-dev main@sha1:4e5f6a7b False True Applied revision: main@sha1:4e5f6a7b
  2. 2

    SSO groups → Argo CD roles

    Policies are Casbin CSV lines: p, <role>, <resource>, <action>, <project>/<object>, allow. Developers can see and sync ShopLite apps but can't delete them or touch clusters; the local admin account gets disabled in argocd-cm (admin.enabled: "false") once SSO works.

    shoplite-gitops/platform/argocd/argocd-rbac-cm.yamlwhole fileyaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: argocd-rbac-cm
      namespace: argocd
    data:
      policy.default: role:readonly
      policy.csv: |
        p, role:shoplite-dev, applications, get,  shoplite/*, allow
        p, role:shoplite-dev, applications, sync, shoplite/*, allow
        p, role:shoplite-dev, logs,         get,  shoplite/*, allow
        g, shoplite-developers, role:shoplite-dev
        g, platform-team, role:admin
      scopes: "[groups]"
  3. 3

    Tell the team when things go wrong

    Argo CD notifications ship with a trigger/template catalog. Subscribe an app (or all apps via defaults) to degraded and failed-sync triggers.

    shoplite-gitops/platform/argocd/argocd-notifications-cm.yamlwhole fileyaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: argocd-notifications-cm
      namespace: argocd
    data:
      service.slack: |
        token: $slack-token
      defaultTriggers: |
        - on-health-degraded
        - on-sync-failed
      template.app-degraded: |
        message: ":red_circle: {{.app.metadata.name}} is {{.app.status.health.status}} at {{.app.status.sync.revision | trunc 7}}"
      trigger.on-health-degraded: |
        - when: app.status.health.status == 'Degraded'
          send: [app-degraded]
  4. 4

    Alert on GitOps itself

    A PrometheusRule (Observability course) for apps stuck unhealthy or out of sync. 'For 15 minutes' avoids paging on normal rollouts.

    shoplite-gitops/platform/monitoring/argocd-alerts.yamlwhole fileyaml
    apiVersion: monitoring.coreos.com/v1
    kind: PrometheusRule
    metadata:
      name: argocd
      namespace: monitoring
    spec:
      groups:
        - name: argocd
          rules:
            - alert: ArgoAppDegraded
              expr: argocd_app_info{health_status="Degraded"} == 1
              for: 15m
              labels: { severity: page }
              annotations:
                summary: "{{ $labels.name }} degraded for 15m"
            - alert: ArgoAppOutOfSync
              expr: argocd_app_info{sync_status="OutOfSync"} == 1
              for: 30m
              labels: { severity: ticket }
  5. 5

    Rehearse a rebuild

    The DR drill: delete the dev cluster, recreate it, and time how long until everything is Healthy. Anything you had to do by hand that isn't in these four commands is a gap to close.

    terminal
    $ kind delete cluster --name shoplite && kind create cluster --name shoplite
    helm install argocd argo/argo-cd -n argocd --create-namespace --version 8.5.0
    kubectl apply -f argocd/root.yaml
    argocd app wait -l env=dev --health --timeout 900
    ── expected output ──
    ...
    Name: argocd/web-in-cluster
    Sync Status: Synced
    Health Status: Healthy
    (total time from empty cluster: 6m41s)

Checkpoint — you should now have

  • ✓You can explain Flux's source/kustomize/helm controllers and map them to Argo CD.
  • ✓Argo CD uses SSO groups, and the local admin is disabled.
  • ✓Degraded apps notify Slack and page after 15 minutes.
  • ✓You've rebuilt a cluster from Git and written down what wasn't automatic.

Part 4

Break it on purpose

Make each change, run the command, and read the error before revealing the diagnosis. Recognising these messages on sight is what makes you fast on a real team. Undo the change afterwards.

Break #1

Suspend and forget

During an incident, disable auto-sync on web-prod (argocd app set web-prod --sync-policy none) to hotfix by hand, then never re-enable it.

terminal
$ argocd app list | grep web-prod
── what you'll see ──
web-prod-mumbai ... OutOfSync Healthy Manual <none>

Part 5

Interview questions from this mission

01

Argo CD vs Flux?

02

What does it take to recover a cluster in a GitOps setup, and what isn't in Git?

03

How do you secure Argo CD?

Before you stop

Clean up

terminal
$ kind delete cluster --name shoplite
kind delete cluster --name shoplite-prod
0/4 · 0%