Skip to content

Muhammad-Imad/argocd-gitops-platform

Repository files navigation

argocd-gitops-platform

Argo CD Kubernetes Helm License: MIT

A production-style GitOps repository that manages multiple Kubernetes clusters across environments and regions using Argo CD App-of-Apps + ApplicationSets — standardized platform addons, self-healing deployments, and per-cluster overlays, all driven from one Git repo.


GitOps Flow

flowchart TD
    Dev[Engineer] -->|git push / PR| Repo[(Git: argocd-gitops-platform)]
    Repo -->|polls & reconciles| ArgoCD[Argo CD]

    subgraph Control["Argo CD (management cluster)"]
        ArgoCD --> Root[root app-of-apps]
        Root --> Projects[AppProjects]
        Root --> AddonsApp[addons Application]
        Root --> WorkloadsApp[workloads Application]
        Root --> PlatformCfg[platform-config Application]
        AddonsApp --> AddonsAS{{addons ApplicationSet}}
        WorkloadsApp --> WorkloadsAS{{sample-api ApplicationSet}}
    end

    AddonsAS -->|matrix: clusters x addons| C1
    AddonsAS --> C2
    AddonsAS --> C3
    WorkloadsAS -->|cluster generator| C1
    WorkloadsAS --> C2
    WorkloadsAS --> C3

    subgraph Fleet["Managed clusters"]
        C1[dev-eu]
        C2[stg-eu]
        C3[prod-eu]
    end
Loading

The root Application is the only thing applied by hand. Everything below it — projects, platform addons, and workloads — is rendered declaratively and kept in sync (and self-healed) by Argo CD. Adding a cluster or an addon is a pull request.


✨ Features

  • App-of-Apps bootstrap — a single root Application owns the entire platform tree.
  • ApplicationSets for fan-out — a matrix generator crosses clusters with addons; a cluster generator rolls workloads onto every environment automatically.
  • Multi-cluster, multi-regiondev-eu, stg-eu, prod-eu managed from one repo, selected by cluster-secret labels.
  • Standardized platform addons — ingress-nginx, cert-manager, external-secrets, metrics-server, and kube-prometheus-stack, each with per-cluster value overlays.
  • Both packaging models demonstratedsample-api shipped as a Helm chart and as a Kustomize base + overlays.
  • Sync waves — deterministic ordering: projects → addon CRDs/charts → platform config → workloads.
  • Progressive deliveryRollingSync promotes addon changes dev → stg → prod.
  • Secret-safe — zero plaintext secrets; everything sensitive flows through External Secrets.
  • CI validation — yamllint, helm lint/template, kustomize build, and kubeconform schema checks on every PR.

🗂️ Repository Structure

argocd-gitops-platform/
├── bootstrap/                     # Install Argo CD + apply the root app
│   ├── argocd-values.yaml         # Helm values for the argo-cd chart (HA)
│   └── root-app-of-apps.yaml      # The one Application you apply by hand
├── apps/                          # App-of-apps children (one per concern)
│   ├── 00-projects.yaml
│   ├── 10-addons-appset.yaml
│   ├── 20-workloads-appset.yaml
│   └── 30-platform-config.yaml
├── applicationsets/               # Fan-out generators
│   ├── addons-appset.yaml         # matrix: clusters x addons
│   └── workloads-appset.yaml      # cluster generator -> sample-api
├── addons/                        # Platform addons (Helm + per-cluster values)
│   ├── ingress-nginx/values/{common,dev-eu,stg-eu,prod-eu}.yaml
│   ├── cert-manager/{clusterissuers.yaml,values/...}
│   ├── external-secrets/{clustersecretstore.yaml,values/...}
│   ├── metrics-server/values/...
│   └── kube-prometheus-stack/values/...
├── workloads/
│   └── sample-api/
│       ├── chart/                 # Helm packaging
│       └── kustomize/             # base + overlays/{dev,stg,prod}
├── clusters/                      # Cluster registration placeholders (no secrets)
│   ├── dev-eu/cluster.yaml
│   ├── stg-eu/cluster.yaml
│   └── prod-eu/cluster.yaml
├── projects/                      # AppProjects (RBAC + allowed sources/dests)
│   ├── platform.yaml
│   └── workloads.yaml
└── .github/workflows/ci.yml       # Validation pipeline

🚀 Bootstrap

# 1. Install Argo CD into the management cluster (HA values).
helm repo add argo https://argoproj.github.io/argo-helm
helm upgrade --install argocd argo/argo-cd \
  --namespace argocd --create-namespace \
  --version 7.7.x \
  -f bootstrap/argocd-values.yaml

# 2. Register the managed clusters (creates labelled cluster secrets).
#    Prefer `argocd cluster add`; never commit real credentials.
argocd cluster add dev-eu  --label env=dev  --label addons=enabled --label workloads=enabled
argocd cluster add stg-eu  --label env=stg  --label addons=enabled --label workloads=enabled
argocd cluster add prod-eu --label env=prod --label addons=enabled --label workloads=enabled

# 3. Apply the ONE root Application — everything else flows from Git.
kubectl apply -f bootstrap/root-app-of-apps.yaml

# 4. Watch it converge.
argocd app list
argocd app get root

From here on, all changes are pull requests against this repo.


🔄 How It Works

App-of-Apps

bootstrap/root-app-of-apps.yaml points Argo CD at apps/, which holds one child Application per platform concern (projects, the addons ApplicationSet wrapper, the workloads ApplicationSet wrapper, and standalone platform config). The root owns the whole tree, so the platform is reproducible from an empty cluster + this repo.

ApplicationSets

  • addons-appset.yaml uses a matrix generator of clusters × addons. The cluster generator selects clusters labelled addons=enabled; the list generator carries each addon's upstream chart coordinates. The result is one Application per (cluster, addon) — e.g. prod-eu-ingress-nginx. Per-cluster values are layered as addons/<addon>/values/common.yaml + addons/<addon>/values/<cluster>.yaml.
  • workloads-appset.yaml uses a cluster generator and selects the Kustomize overlay from the cluster's env label, so the same base manifests promote across dev → stg → prod without copy/paste.

Sync Waves

Ordering is enforced with argocd.argoproj.io/sync-wave:

Wave What
-10 root app-of-apps
-5 AppProjects
-3 core addons (ingress, cert-manager, ESO, metrics)
-1 platform config (ClusterIssuers, SecretStore) + monitoring
0 workloads ApplicationSet wrapper
1 sample-api workloads

Self-Healing

Every Application sets syncPolicy.automated with prune: true and selfHeal: true, plus exponential retry. Manual drift on a cluster is reverted to the Git state automatically; deleted-from-Git resources are pruned.


🔐 Secrets

No plaintext secrets are ever committed. Sensitive material flows through the External Secrets Operator:

  1. A ClusterSecretStore (addons/external-secrets/clustersecretstore.yaml) connects ESO to the backing secrets manager (AWS Secrets Manager shown; swap for Vault, GCP Secret Manager, or Azure Key Vault). Authentication uses IRSA / Workload Identity — no static credentials.
  2. Workloads declare an ExternalSecret (see the Helm chart and the Kustomize base) referencing remote keys like sample-api/database-url.
  3. ESO materializes a Kubernetes Secret in-cluster, consumed via envFrom.

cert-manager ACME account keys, Grafana admin credentials, and Argo CD SSO secrets follow the same pattern. .gitignore additionally blocks *.pem, *-secret.yaml, .env, kubeconfigs, and similar. For air-gapped setups, the same manifests work with SealedSecrets instead of ESO.


🧭 Engineering Case Study

Problem. Operating a fleet of Kubernetes clusters (EKS / ROSA / RKE2-style) across multiple environments and regions tends toward snowflakes: addon versions drift between clusters, secrets get pasted into manifests, and "what is actually running where?" becomes unanswerable. Manual kubectl/helm runs don't scale and aren't auditable.

Approach. Treat the fleet as one declarative system in Git:

  • One source of truth. The root app-of-apps means a cluster's entire platform is reconstructable from this repo. Reviews and history give a full audit trail.
  • Fan-out, not copy-paste. ApplicationSets generate per-cluster Applications from generators, so onboarding a new cluster is labelling its secret — the full addon stack and workloads roll out automatically.
  • Standardized addons, local overrides. Each addon has a common.yaml baseline with thin per-cluster overlays (replica counts, retention, HPA bounds, ACME issuer), keeping clusters consistent while honoring environment differences.
  • Safe rollouts. RollingSync promotes addon changes dev → stg → prod; sync waves guarantee CRDs and issuers exist before the resources that depend on them.
  • Self-healing & drift control. Automated sync with prune + selfHeal converges any cluster back to Git, eliminating configuration drift.
  • Secret hygiene. External Secrets keeps credentials out of Git entirely while still letting workloads declare exactly which secrets they need.

Outcome. Adding a cluster or bumping an addon is a reviewable pull request, every cluster's state is knowable from Git, and production changes are gated behind earlier environments — the operational properties expected of a senior SRE/platform setup.

(Generic by design — no employer, client, or proprietary names. Placeholders only: example.com, acme, dev-eu/stg-eu/prod-eu.)


📄 License

Released under the MIT License. Copyright © 2026 Muhammad Imad.

About

Production-style multi-cluster GitOps with ArgoCD App-of-Apps + ApplicationSets (Helm & Kustomize)

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors