Files
trade-doc/k8s-migracja.md
2026-01-06 12:33:47 +01:00

168 lines
7.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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ęść releaseu)
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-<shortsha>` 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?