The system we build: "Orbit"
Orbit is a small but realistic order-processing platform. It is deliberately shaped like the systems you run at work: a synchronous HTTP path, an asynchronous queue path, a relational database, a cache, and background workers that have to scale on queue depth rather than CPU.
┌──────────────────────── Kubernetes cluster ───────────────────────────┐
│ │
client ──HTTPS──► Gateway API ───┼──► catalog (Go, 3+ pods) ──► Redis (cache) │
(k6 / curl) (Envoy Gateway) │ │ │
+ cert-manager │ └──────────────► Postgres (CloudNativePG, 3 instances) │
│ ▲ │
├──► orders (Go, 3+ pods) ─────┘ │
│ │ XADD orders.created │
│ ▼ │
│ Redis Stream ◄── XREADGROUP ── worker (Go, 0..N pods, KEDA-scaled) │
│ │
│ every pod ──OTLP──► OTel Collector (agent DS) ──► OTel gateway ──┐ │
│ │ │
│ Prometheus ◄─metrics──────────────────────────────────────────┤ │
│ Loki ◄─logs─────────────────────────────────────────────┤ │
│ Tempo ◄─traces───────────────────────────────────────────┘ │
│ Grafana (dashboards, SLOs) Alertmanager ──► Slack / PagerDuty │
│ │
│ Argo CD (GitOps) Argo Rollouts (canary) KEDA Kyverno Velero │
└─────────────────────────────────────────────────────────────────────────┘
| Component | What it does | What it teaches |
|---|---|---|
catalog |
GET /products, GET /products/{id}; read-heavy, Redis cache-aside |
stateless scaling, HPA on CPU, cache patterns |
orders |
POST /orders writes Postgres, publishes an event to a Redis Stream |
transactions, idempotency, canary releases, RPS-based scaling |
worker |
consumes orders.created, "charges payment", marks order paid |
queue-driven autoscaling (KEDA, scale-to-zero), retries, poison messages |
| Postgres | system of record (CloudNativePG operator) | StatefulSets, operators, backups, failover |
| Redis | cache + stream | StatefulSet basics, persistence trade-offs |
| Observability | OTel Collector, Prometheus, Loki, Tempo, Grafana | the three signals, correlation, SLOs |
| Platform | Envoy Gateway, cert-manager, Argo CD, Argo Rollouts, KEDA, Kyverno, Velero | the "platform layer" every real cluster grows |
Final repository layout (you build it up chapter by chapter):
orbit/
├── go.mod
├── cmd/
│ ├── catalog/main.go
│ ├── orders/main.go
│ └── worker/main.go
├── internal/
│ ├── platform/ # shared: config, logging, telemetry, http server, health
│ ├── catalog/
│ ├── orders/
│ └── worker/
├── migrations/ # SQL migrations (goose)
├── Dockerfile # one multi-stage Dockerfile, SERVICE build-arg
├── .dockerignore
├── compose.yaml # local dev stack
├── load/orders.js # k6 load test
├── lab/ # Lima VM: create-vm.sh, bootstrap.sh, extract-blocks.py (§0)
├── deploy/
│ ├── base/ # Kustomize base: one folder per service
│ └── overlays/{dev,staging,prod}
├── platform/ # cluster add-ons as Argo CD Applications (Helm values)
└── .github/workflows/ci.yaml