The GitOps "how it runs" repo for t2s: a single-node k3s cluster, deployed and kept in sync entirely through Argo CD. It owns namespaces, data-layer infrastructure, observability, and the deploy config for every application service — nothing here is code that gets built into an image; it's manifests, Helm values, and sealed secrets that describe the running system.
Principle: app repos (fastapi_ms/, training arena/)
own what runs — code, Dockerfiles, CI that builds and pushes GHCR images. This repo owns
how it runs — k8s manifests, Helm charts/values, Argo CD Applications, secrets. Docker Compose
in the app repos is local-dev only; the cluster is the one real deployment target.
| Namespace | Tier | Holds |
|---|---|---|
argocd |
control-plane | Argo CD itself |
observability |
observability | Prometheus, Grafana, Loki, Tempo, Alloy, exporters |
data |
data | Postgres (CloudNativePG), Kafka (Strimzi), Kafka Connect (Debezium) |
apps |
app | UserService, SubscriptionService, DBService, ChatService |
ml (planned) |
app | training-arena's serving + MLOps stack (MinIO/MLflow/Prefect/pruner) |
cluster/ namespaces, container-registry pull secrets
argocd/ Argo CD install notes + the app-of-apps bootstrap (argocd/bootstrap/)
charts/microservice/ one parameterized Helm chart every app service deploys with
apps/<svc>/ per-service values.yaml + sealed-secret.yaml (one dir per microservice)
data/ Postgres / Kafka / Debezium manifests + runbook
observability/ kube-prometheus-stack, Loki, Tempo, Alloy, dashboards, alert rules
Each subdirectory that needs operational steps has its own README with copy-pasteable commands — this file is the map, not a replacement for them:
argocd/README.md— installing Argo CD, the app-of-apps bootstrap, sync-wave ordering, repo credentials.data/README.md— bringing up CloudNativePG, Strimzi Kafka, and Debezium Kafka Connect, in order, plus the outbox/publication gotchas.apps/README.md— sealing per-service secrets and deploying the fourfastapi_msservices via the microservicesApplicationSet.
Everything syncs from a single app-of-apps root (argocd/root-application.yaml →
argocd/bootstrap/). Child Applications carry sync-wave annotations so infra converges before
anything that depends on it (namespaces → data/observability CRDs → services), and all carry
syncPolicy.automated with prune: true + selfHeal: true: a git push deploys, and manual
cluster drift gets reverted back to match git. New application services are onboarded by adding
one apps/<svc>/ directory (values.yaml + sealed-secret.yaml) — the microservices
ApplicationSet picks it up automatically, no manual kubectl apply.
Secrets are Sealed Secrets, always — never an imperative kubectl create secret. Every
secret in this repo exists as a gitignored plaintext *.unsealed.yaml (never committed) and a
committed, encrypted sealed-secret-*.yaml that only the in-cluster controller can decrypt.
See any of the READMEs above for the kubeseal invocation.
kube-prometheus-stack (Prometheus + Grafana + Alertmanager) plus Loki (logs) and Tempo
(traces), fed by an Alloy DaemonSet that auto-discovers pod logs and receives OTLP. Application
ServiceMonitors (rendered by charts/microservice) opt in via additionalLabels: release: observability so Prometheus picks them up without a manual scrape-config change.
Single node, 6 vCPU / 15 GiB RAM — RAM is the binding constraint. Bring the stack up in waves
(data layer → observability → apps) rather than all at once, and don't run this cluster and any
app repo's docker compose up at the same time.