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.
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/
- 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
The same app, the Flux way
For comparison, here's what ShopLite web in dev looks like in a Flux repo.
flux bootstrap githubinstalls 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-secretsterminal$ flux bootstrap github --owner=<you> --repository=shoplite-flux --path=clusters/devflux get kustomizations── expected output ──NAME REVISION SUSPENDED READY MESSAGEflux-system main@sha1:4e5f6a7b False True Applied revision: main@sha1:4e5f6a7bweb-dev main@sha1:4e5f6a7b False True Applied revision: main@sha1:4e5f6a7b - 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 inargocd-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
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
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
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 shoplitehelm install argocd argo/argo-cd -n argocd --create-namespace --version 8.5.0kubectl apply -f argocd/root.yamlargocd app wait -l env=dev --health --timeout 900── expected output ──...Name: argocd/web-in-clusterSync Status: SyncedHealth 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.
Part 5
Interview questions from this mission
Argo CD vs Flux?
What does it take to recover a cluster in a GitOps setup, and what isn't in Git?
How do you secure Argo CD?
Before you stop