# Plan migracji na Kubernetes (mikroserwisy + CI/CD) ## 1) Co jest dziś w repo (stan wejściowy) Ten projekt już ma podział “mikroserwisowy” w Docker Compose: - **DB stack** (`devops/db/docker-compose.yml`) - `postgres` (TimescaleDB, port 5432) - `hasura` (GraphQL Engine, port 8080) - `pgadmin` (narzędzie dev, port 5050) - **App stack** (`devops/app/docker-compose.yml`) - `api` = **trade-api** (Node, port 8787, `devops/api/Dockerfile`) - `frontend` = **trade-frontend** (Node + statyczny build visualizera, port 8081, `devops/app/frontend/Dockerfile`) - `ingestor` = worker (Node, `devops/ingestor/Dockerfile`) – opcjonalnie, profil `ingest` - **One-shot bootstrap** (`devops/tools/bootstrap/docker-compose.yml`) - `db-init` (aplikuje SQL `devops/db/initdb/001_init.sql`) - `db-version` / `db-backfill` (migracje “wersji” ticków) - `hasura-bootstrap` (Node script `devops/db/hasura-bootstrap.mjs`: track tabel + permissions + funkcje) Konfiguracja i sekrety dziś są w `tokens/*.json` (gitignored) i są montowane jako pliki do kontenerów. ## 2) Docelowa architektura na K8s Minimalny sensowny podział na K8s (1 namespace lub 2): - **timescaledb** (stanowe) → `StatefulSet` + `PVC` + `Service` - **hasura** → `Deployment` + `Service` - **trade-api** → `Deployment` + `Service` - **trade-frontend** → `Deployment` + `Service` + `Ingress` - **trade-ingestor** → `Deployment` (1 replika) albo `CronJob` (jeśli chcesz uruchamiać okresowo) - **bootstrap / migracje** → `Job` (odpalane ręcznie albo jako część release’u) Opcjonalnie: - `pgadmin` tylko na dev/staging (nieprodukcyjnie). ## 3) Proponowane zasoby Kubernetes (mapowanie 1:1 z Compose) ### 3.1 timescaledb (Postgres) - `StatefulSet` z `volumeClaimTemplates` (dane w PVC) - `Service` typu `ClusterIP` (np. `timescaledb:5432`) - Init schematu: - *na pierwszy start* można nadal użyć mechanizmu `/docker-entrypoint-initdb.d` (ConfigMap z SQL), - *dla istniejących wolumenów* potrzebny jest osobny `Job` (odpowiednik `db-init` z compose). ### 3.2 Hasura - `Deployment` + `Service` (`hasura:8080`) - `Secret` na: - `HASURA_GRAPHQL_ADMIN_SECRET` - klucz JWT (`HASURA_GRAPHQL_JWT_SECRET` / `HASURA_JWT_KEY`) - connection string do Postgresa (albo osobno hasło) - Bootstrap metadanych: - `Job` uruchamiający `devops/db/hasura-bootstrap.mjs` (odpowiednik `hasura-bootstrap`). ### 3.3 trade-api - `Deployment` + `Service` (`trade-api:8787`) - `readinessProbe`/`livenessProbe`: `GET /healthz` (już istnieje) - Konfiguracja: - env: `HASURA_GRAPHQL_URL`, `TICKS_TABLE`, `CANDLES_FUNCTION`, `APP_VERSION`, `BUILD_TIMESTAMP` - sekret plikowy (jak dziś): `tokens/hasura.json` + `tokens/api.json` → `Secret` montowany do `/app/tokens/*` ### 3.4 trade-frontend - `Deployment` + `Service` (`trade-frontend:8081`) - `Ingress` wystawiający UI na zewnątrz (TLS opcjonalnie) - `readinessProbe`/`livenessProbe`: `GET /healthz` (już istnieje) - Sekrety plikowe: - `tokens/frontend.json` (basic auth do UI) - `tokens/read.json` (read token do proxy `/api/*`) - oba jako `Secret` montowany do `/tokens/*` - `API_UPSTREAM`: `http://trade-api:8787` (Service DNS) ### 3.5 trade-ingestor - `Deployment` (zwykle `replicas: 1`) - Sekrety: - RPC do Drift (np. `tokens/heliusN.json` / `rpcUrl` / `heliusApiKey`) - write token do API (`tokens/alg.json`) - Konfiguracja: - `MARKET_NAME`, `INTERVAL_MS`, `SOURCE`, `INGEST_API_URL=http://trade-api:8787` - Uwaga: worker wymaga egress do internetu (Helius RPC + Drift). ### 3.6 Jobs: db-init / db-version / db-backfill / hasura-bootstrap Bezpieczny pattern: - uruchamiane manualnie (kubectl) albo jako “hook” w Helm (pre/post-install) - odpalane w tym samym namespace co DB/Hasura (żeby DNS działał prosto) ## 4) Sekrety i konfiguracja (z `tokens/` → K8s) Rekomendacja: rozdziel na 2 typy: - **Secret** (nie commitować w git): - `tokens/hasura.json` (adminSecret, jwtKey) - `tokens/api.json` (api adminSecret) - `tokens/frontend.json` (basic auth) - `tokens/read.json` (read token) - `tokens/alg.json` (write token) - `tokens/heliusN.json` (RPC token / url) - **ConfigMap**: - wersja i parametry nie-wrażliwe: `APP_VERSION`, `BUILD_TIMESTAMP`, `TICKS_TABLE`, `CANDLES_FUNCTION`, `MARKET_NAME`, `INTERVAL_MS` Jeśli chcesz GitOps bez trzymania sekretów “na piechotę”, wybierz jedno: - **Sealed Secrets** (zaszyfrowane sekrety w repo), - **External Secrets Operator** (Vault / AWS / GCP / Azure), - “na start” manualne `kubectl create secret ...` per środowisko. ## 5) Wersjonowanie (v1, v2…) i cutover bez wyłączania DB W `scripts/ops/` jest workflow wersjonowania Compose: - nowa wersja `api+frontend(+ingestor)` działa równolegle - pisze do **nowej tabeli** (`drift_ticks_vN`) i używa **nowej funkcji** (`get_drift_candles_vN`) Na K8s najprościej odwzorować to tak: - Helm release name albo suffix w nazwach zasobów: `trade-v1`, `trade-v2`, … - wspólne DB/Hasura pozostają bez zmian - `Job` “version-init”: - odpala SQL migracji (odpowiednik `db-version`) - odpala `hasura-bootstrap` z `TICKS_TABLE` i `CANDLES_FUNCTION` ustawionymi na vN - “cutover”: - startujesz `trade-ingestor` vN - stopujesz `trade-ingestor` v1 - po czasie robisz `db-backfill` (opcjonalnie) i sprzątasz stare zasoby ## 6) CI/CD “na gita” (build → deploy po pushu) ### Opcja A (polecana): GitOps (Argo CD / Flux) 1) CI (GitHub Actions / GitLab CI) buduje obrazy: - `trade-api` - `trade-frontend` - `trade-ingestor` 2) CI publikuje je do registry (np. GHCR) 3) CD (ArgoCD/Flux) automatycznie synchronizuje manifesty/Helm z repo i robi rollout Tagowanie obrazów: - `:sha-` dla każdego commita - opcjonalnie `:vN` dla release’ów Aktualizacja tagów: - ArgoCD Image Updater / Flux Image Automation **albo** - CI robi commit do `k8s/` (np. podmienia tag w `values.yaml`) ### Opcja B (prostsza na start): “kubectl apply” z CI 1) CI buduje i pushuje obrazy 2) CI wykonuje `helm upgrade --install` albo `kubectl apply -k ...` 3) Dostęp do klastra przez sekret w repo (kubeconfig / token) ## 7) Proponowana sekwencja migracji (checklista) 1) **Decyzje**: gdzie stoi klaster (EKS/GKE/AKS/k3s), jakie registry, jaki Ingress, jak trzymamy sekrety. 2) **K8s “base”**: namespace, storage class, ingress controller, certy (jeśli TLS). 3) **DB**: wdroż Timescale (StatefulSet + PVC), odpal `db-init` job. 4) **Hasura**: wdroż Hasurę, odpal `hasura-bootstrap` job. 5) **API**: wdroż `trade-api`, sprawdź `/healthz`. 6) **Tokeny**: wygeneruj read/write tokeny (obecnym mechanizmem API) i wgraj je jako Secrets. 7) **Frontend**: wdroż `trade-frontend` + Ingress, sprawdź `/healthz` i UI. 8) **Ingestor**: wdroż `trade-ingestor` (1 replika), potwierdź że ticki wpadają. 9) **CI/CD**: dodaj workflow build+push i deploy (GitOps albo kubectl). 10) **Staging → Prod**: rollout na staging, potem prod. ## 8) Pytania, które domykają plan 1) Jaki “git”: **GitHub czy GitLab**? 2) Gdzie ma stać K8s: cloud (EKS/GKE/AKS) czy on-prem/k3s? 3) DB w klastrze (StatefulSet) czy zewnętrzny managed Postgres/Timescale? 4) Czy “po pushu” ma: - tylko robić rollout na `main`, - czy tworzyć **preview env per branch/PR**, - czy startować **nową wersję vN równolegle** (jak `scripts/ops/`)? 5) Jaki dostęp z zewnątrz: domena + TLS, czy wystarczy port-forward / internal?