Argo CD¶
Argo CD is the only thing that writes to the cluster. Every namespace, every Helm release, every Deployment, every Secret is the output of an Application sync — auto-sync, auto-prune, and self-heal are on by default, so anything not in this repo gets reverted within minutes.
One GKE cluster (gke-tomoda), three logical tiers wired up by Argo CD: platform (cluster-wide system services), dev (the dev tenant data plane), prod (the prod tenant data plane). See Environments for the full tenancy model and tier sizing.
For the underlying GCP service account, IAM bindings, and how the Argo CD Helm release itself gets installed, see Infrastructure → GCP → Argo CD. That layer is Terraform-managed (infrastructure/gcp/argocd.tf); this page covers the GitOps layer on top.
Bootstrap¶
One file, one command, applied once after terraform apply brings up the cluster + Argo CD:
gcloud container clusters get-credentials gke-tomoda \
--zone us-central1-a --project development-485000
kubectl apply -f k8s/envs/bootstrap.yaml
k8s/envs/bootstrap.yaml creates three top-level Applications: platform, dev, prod. Each recurses through its respective directory under k8s/envs/ and picks up the sub-Applications it finds.
Reconciliation takes 5–10 minutes the first time. Watch progress:
kubectl get applications -n argocd -w
To skip a tier (rare — e.g. scratch cluster with no prod tenant), apply the bootstrap and then delete the unwanted top-level app:
kubectl delete application dev -n argocd # or: prod
platform is required — uninstalling it takes down ingress, TLS, monitoring, and secret syncing for every tenant.
See Operations → Bootstrap for the full cluster-rebuild walkthrough.
The three apps¶
Application CRsdirectory.recurse: true — each app discovers sub-Applicationskubectl apply'd by hand — Argo CD owns reconciliation.| App | Source path | Scope | Skippable? |
|---|---|---|---|
platform |
k8s/envs/platform/ |
Cluster-wide system services — required by both tenants | No |
dev |
k8s/envs/dev/ |
Dev tenant data plane (1 Gi PVCs, no backups) | Yes — kubectl delete application dev -n argocd |
prod |
k8s/envs/prod/ |
Prod tenant data plane (10 Gi PVCs, daily backups) | Yes — rarely useful |
What each contains¶
platform brings up cert-manager, External Secrets Operator, Traefik, External-DNS, oauth2-proxy, the Prometheus / Grafana / Alertmanager stack, Loki, Tempo, Pyrra (SLO controller), blackbox-exporter, cloudflared, Photon + Photon-indexer, the ARC controller + runner pool, and Trivy Operator (runtime image CVE scanning). Each subdirectory under k8s/envs/platform/ carries its own Application CR. See System services for per-service docs.
dev brings up postgres-dev (CNPG), redis-dev (Bitnami), pgAdmin, RedisInsight, and the tomoda dev overlay. Sized to validate changes cheaply — see Environments → Tier sizing.
prod brings up postgres-prod, redis-prod, pgAdmin, RedisInsight, and the tomoda prod overlay. Real PVCs, daily Postgres backups, HPA-driven app scale-out.
Recurse + exclude¶
Each top-level app uses directory.recurse: true plus an exclude pattern so sub-Applications don't double-apply manifests their child apps already own:
| App | exclude pattern |
Why |
|---|---|---|
platform |
manifests/** |
The manifests/ subtree (alerting rules, ESO configs, dashboards, image-updater config, SLOs) is owned by the sys-resources Application — applied separately so Helm-installed CRDs land first |
dev |
**/manifests.yaml |
Sub-apps (postgres, redis, etc.) own their own manifests.yaml via their own Application |
prod |
**/manifests.yaml |
Same as dev |
Image Updater¶
argocd-image-updater runs in the argocd namespace (manifest at k8s/envs/platform/manifests/image-updater.yaml) and:
- Authenticates to Artifact Registry via Workload Identity (KSA
argocd-image-updater→ GSAargocd-image-updater-sa@development-485000.iam.gserviceaccount.com). - Watches the dev tomoda images in
tomoda-dev-repo. - When a new tag matches the configured constraint, writes a Kustomize image-tag update back into this repo. Argo CD then reconciles the dev tomoda Application normally.
Prod's Application/tomoda-prod is intentionally not annotated with argocd-image-updater.argoproj.io/*. Prod tag bumps require a manual scripts/release.sh invocation — the explicit gate between dev and prod. See Operations → Deploy for the promotion workflow.
OAuth gating on the UI¶
The Argo CD UI is exposed at argo-app.tomoda.life, fronted by Traefik with two layers of auth:
- oauth2-proxy (Traefik middlewares on the Ingress at
k8s/envs/platform/argocd-ingress/ingress.yaml) — gates the host at the edge. Only@tomoda.lifeGoogle accounts get past it. - Argo CD Dex — once past oauth2-proxy, Dex maps the Google identity into an Argo CD RBAC role.
Sign in once with Google; oauth2-proxy plants a .tomoda.life cookie and Dex picks up the identity. See oauth2-proxy for proxy-side details.
RBAC and access hardening¶
Two Helm values in infrastructure/gcp/argocd.tf (configs.cm / configs.rbac) lock down what an authenticated user can do:
exec.enabled = "false"— the browser Web Terminal (exec into pods from the Argo UI) is disabled. No shell into workloads through Argo CD.policy.default = "role:readonly"— the default SSO policy is read-only. Every mapped Google identity can view but not mutate. Write and admin actions require an explicit grant inpolicy.csv.
Sync workflow¶
- Edit and push to
main. - Argo CD polls (default ~3 min) and detects the diff. Click Refresh in the UI to force an immediate poll.
- Sync runs automatically — pruned resources deleted, new resources created, drifted resources patched.
- Self-heal pins the cluster to whatever is in Git.
Manual sync is still available — argocd app sync <name> or the UI's Sync button — useful when you don't want to wait for the next poll. No kubectl apply is needed after the initial bootstrap.