Operacje
Wdrożenie i udostępnianie
DataFlow AI ma dwie odrębne rzeczywistości wdrożeniowe: udokumentowaną ścieżkę CI/CD, która buduje obrazy w GitHub Actions i udostępnia je na GKE Autopilot z etapami canary i automatycznym wycofywaniem, oraz rzeczywisty działający system produkcyjny — pojedynczy VPS z systemem Debian uruchamiający Docker Compose. Ta strona dokumentuje oba podejścia, uczciwie, aby operatorzy wiedzieli, które z nich obsługuje użytkowników.
Dwa modele wdrożenia
Repozytorium zawiera dwa odrębne, niespójne sposoby wdrożenia platformy. Zanim dotkniesz produkcji, musisz wiedzieć, który z nich jest rzeczywisty.
| Model | Cel | Udostępnia | Udostępnianie | Migracje | Czy obsługuje produkcję dzisiaj? |
|---|---|---|---|---|---|
| (a) Udokumentowany CI/CD | Klastry GKE Autopilot (dev / staging / prod) | GitHub Actions → GCP Artifact Registry → Kustomize → kubectl | Canary 10% → 50% → 100% z automatycznym wycofywaniem | Binarium Go /app/migrate przez kubectl exec | Nie — aspiracyjny |
| (b) Rzeczywista produkcja | Pojedynczy VPS Debian etl.exai.cloud (57.129.120.73) | Ręcznie napisane skrypty Python (deploy/deploy_to_vps.py) przez SSH | Brak — pełna przebudowa + uporządkowany restart | Spring Boot Flyway przy starcie usługi | Tak |
Wiedz, która ścieżka jest aktywna
Przepływy pracy GKE odwołują się do dataflow.polkomtel.internal, regionów GCP oraz narzędzia migracji Go. Żaden z tych elementów nie obsługuje produkcji. Produkcją jest VPS pod adresem etl.exai.cloud, wdrożony za pomocą Docker Compose. Traktuj ścieżkę GKE jako architekturę docelową, a nie stan bieżący.
Obie ścieżki różnią się w fundamentalny sposób:
- GKE używa nakładek Kustomize na środowisko; VPS używa pojedynczego pliku
docker-compose.yml. - GKE uruchamia dedykowane narzędzie Go
/app/migrate; VPS uruchamia Flyway przy starcie z agresywnymi flagami naprawy. - GKE ma etapy canary, testy dymne, testy integracyjne i automatyczne wycofywanie; ścieżka VPS nie ma żadnego z tych elementów.
- GKE zakłada wieloregionowy Cloud SQL; VPS ma pojedynczy kontener PostgreSQL.
Wybór topologii docelowej
Ta strona opisuje sposób, w jaki platforma jest budowana i dostarczana. Nie omawia tego, na jaką topologię infrastruktury wdrażać — lokalnie / on-premises, pełną chmurę GCP czy zalecaną hybrydową — ani wymiarowania i kosztu każdej z nich.
Wybór topologii, wymagania infrastrukturalne, wymiarowanie pojemności, pełny rozkład kosztów usług GCP oraz przewodnik decyzyjny znajdziesz na stronie Scenariusze wdrożenia i wymiarowanie. W skrócie: dokumenty źródłowe zalecają topologię hybrydową dla Polkomtela (bazy danych źródłowe pozostają on-premises, platforma działa na GCP), z 3-letnim TCO na poziomie około 606 000–741 000 USD.
Topologia zamierzona a stan bieżący
Strona scenariuszy wdrożenia opisuje zamierzoną architekturę GCP opartą na GKE. Mechanika budowy i udostępniania udokumentowana na tej stronie odzwierciedla to, co obsługuje produkcję dzisiaj — pojedynczy VPS z Docker Compose. Przeczytaj obie strony razem, aby zrozumieć różnicę między stanem docelowym a bieżącym.
Inwentarz usług
Pełne wdrożenie tworzy osiem wdrażalnych usług plus warstwa infrastruktury.
Usługi aplikacyjne
| Usługa | Stos | Build | Port kontenera | Port hosta VPS |
|---|---|---|---|---|
api-gateway | Kotlin / Spring Cloud Gateway | platform/Dockerfile BUILD_MODULE=api-gateway | 8080 | 8085 |
metadata-service | Kotlin / Spring Boot | BUILD_MODULE=metadata-service | 8080 | 8181 |
pipeline-engine | Kotlin / Spring Boot | BUILD_MODULE=pipeline-engine | 8080 | 8082 |
lineage-service | Kotlin / Spring Boot | BUILD_MODULE=lineage-service | 8080 | 8083 |
monitor-service | Kotlin / Spring Boot | BUILD_MODULE=monitor-service | 8080 | 8084 |
copilot | Python 3.12 / FastAPI | ai-services/copilot/Dockerfile | 8000 | 8090 |
migration-engine | Python 3.12 / FastAPI | ai-services/migration-engine/Dockerfile | 8000 | 8091 |
frontend | React / Vite + nginx | frontend/Dockerfile | 80 | 3006 |
Interfejs Go CLI (backend/cli) jest dostarczany jako binaria wydania, a nie jako wdrożona usługa. Moduły Gradle pushdown-sql i connector-sdk są przywoływane przez build oraz konfigurację Prometheus, ale nie są samodzielnymi usługami Compose.
Usługi infrastrukturalne
Działają obok kontenerów aplikacyjnych za pośrednictwem Docker Compose:
- PostgreSQL 15 —
pgvector/pgvector:pg15 - Keycloak 24 — dostawca tożsamości OIDC
- Zookeeper + Kafka —
confluentinc/cp-* 7.7.1(pojedynczy broker, RF=1 — nie HA) - Prometheus —
v2.54.1 - Grafana —
11.2.2 - MinIO — magazyn obiektów zgodny z S3
- Redis 7-alpine — niewdrożony na VPS; brama przechodzi na ograniczanie liczby żądań w pamięci
Architektura budowy
Usługi Kotlin
Wszystkie pięć usług Kotlin buduje się z jednego wieloetapowego pliku backend/platform/Dockerfile, wybieranego przez argument budowy BUILD_MODULE.
- Etap 1 —
gradle:8.5-jdk21. Pliki budowy są kopiowane jako pierwsze dla buforowania warstw, zależności są wstępnie pobierane, następniegradle :${BUILD_MODULE}:bootJar -x test --no-daemonuruchamia wewnątrzprocesowy kompilator Kotlin zGRADLE_OPTS=-Xmx2g. - Etap 2 —
eclipse-temurin:21-jre-alpine. Użytkownikdataflowbez uprawnień roota, JVM świadoma kontenera (MaxRAMPercentage=75) oraz kontrola stanu Spring Boot Actuator na/actuator/health.
Dockerfile.local to wariant wyłącznie środowiska uruchomieniowego — oczekuje plików JAR wstępnie zbudowanych na hoście (./gradlew bootJar -x test --parallel) i omija problemy DNS Dockera podczas budowy.
Usługi Python
copilot i migration-engine mają każda własny plik Dockerfile, budowany z własnego kontekstu. Copilot ładuje sekrety LLM z ignorowanego przez git pliku .env.local (required: false), więc jego brak nie psuje budowy.
Frontend
frontend/Dockerfile jest wieloetapowy: node:22-alpine uruchamia vite build, a środowiskiem uruchomieniowym jest nginx:alpine obsługujący /app/dist z niestandardowym nginx.conf do routingu SPA.
Potok CI
W .github/workflows/ znajduje się osiem przepływów pracy GitHub Actions. Najważniejsze dla wdrożenia opisano poniżej.
ci.yml — Continuous Integration
Wyzwalany przy push do main/develop oraz PR do main. Używa dorny/paths-filter do wykrycia, które części monorepo uległy zmianie (kotlin / python / go / frontend / terraform / docker / openapi), i warunkowo uruchamia zadania zmatrycowane wg języka:
| Zadanie | Co uruchamia |
|---|---|
kotlin-build | JDK 21 + Gradle: kompilacja → lint Detekt → testy jednostkowe → integrationTest → build; przesyła raporty + pliki JAR |
python-test | Macierz [copilot, migration-engine], Python 3.12 + Poetry 1.8.4: lint/format Ruff, mypy (nieblokujący), pytest z pokryciem. Bramka pokrycia PR thresholdAll 0.70 / thresholdNew 0.80 |
go-build | Go 1.22: build / vet / test z -race, golangci-lint |
frontend-build | Node 20: ESLint, tsc -b --noEmit, build, pokrycie vitest |
terraform-validate | terraform fmt/validate na środowisko + moduł, TFLint |
docker-compose-validate | docker compose config, hadolint |
openapi-validate | Lint Redocly dla 5 specyfikacji OpenAPI + kontrola zmian przełomowych oasdiff |
ci-status | Zagregowana bramka dla ochrony gałęzi |
zero-tolerance.yml — Bramka zgodności
Przy PR/push do main/develop uruchamia scripts/detect-fake-code.js na zmienionych plikach .ts/.tsx/.js/.jsx, egzekwując regułę „brak fałszywego kodu”, plus typecheck/lint/test/build frontendu.
security-scan.yml — Bezpieczeństwo
Cotygodniowy cron (pon. 06:00 UTC) plus push/PR do main. Zadania:
- gitleaks — wykrywanie sekretów
- Trivy — skan kontenerów na usługę,
CRITICAL,HIGH,exit-code 1 - Snyk — skan zależności
- OWASP dependency-check — zależności JVM
- Python
safety— podatności zależności Python - Checkov — skan błędnych konfiguracji Terraform
- license-check — przerywa build przy licencjach GPL/AGPL
codeql-analysis.yml również uruchamia analizę statyczną.
Przepływy pracy wdrożenia i wydania
deploy-dev.yml— przy push dodevelop: buduje i przesyła wszystkie 8 obrazów (dev-<sha>,dev-latest), uruchamiakustomize edit set imagena nakładcedev,kubectl apply --prune, czeka na udostępnianie, uruchamia testy dymne, powiadamia przez Slack.deploy-staging.yml— przy push domain/ taguv*.*.*-rc*/ ręcznie: buduje + przesyła 8 obrazów, tworzy kopię zapasową Cloud SQL przed wdrożeniem, stosuje nakładkęstaging, uruchamia migracje bazy danych przezkubectl exec deployment/api-gateway -- /app/migrate --direction=up --environment=staging, a następnie testy dymne i integracyjne.deploy-production.yml— wyłącznieworkflow_dispatch; udostępnianie canary (zobacz poniżej).release.yml— przy tagachv*.*.*: buduje obrazy wydania otagowane wg semver, buduje binaria Go CLI dla 5 kombinacji OS/architektura, generuje dziennik zmian w formacie conventional-commit oraz tworzy GitHub Release zSHA256SUMS.txt.
Uwierzytelnianie do GCP używa Workload Identity Federation (google-github-actions/auth@v2, id-token: write) — w repozytorium nie ma statycznych kluczy GCP.
Udostępnianie canary (ścieżka produkcyjna GKE)
Udokumentowane wdrożenie produkcyjne (deploy-production.yml) to ręczny workflow_dispatch z wejściami: image_tag (musi być przetestowanym tagiem staging), canary_weight_step1 (domyślnie 10%), canary_weight_step2 (domyślnie 50%) oraz skip_canary.
┌──────────────────────────────────────────────────────────────────┐
│ 1. pre-deploy-validation │
│ verify images in Artifact Registry · verify Trivy scan · │
│ check current prod pod health │
└────────────────────────────┬─────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────────┐
│ 2. database-backup gcloud sql backups create dataflow-prod-db │
└────────────────────────────┬─────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────────┐
│ 3. canary-stage-1 (10%) │
│ clone each deployment as ${svc}-canary · scale by weight · │
│ 5-minute health observation │
│ ABORT if >10 failures OR >2 pod restarts ──────────┐ │
└────────────────────────────┬──────────────────────────────┼───────┘
▼ │
┌──────────────────────────────────────────────────────────┐│
│ 4. canary-stage-2 (50%) ││
│ scale canary to 50% · 10-minute observation ││
└────────────────────────────┬──────────────────────────────┼───────┘
▼ │
┌──────────────────────────────────────────────────────────┐│
│ 5. full-deploy (100%) ││
│ kustomize edit set image · DB migrations (dry-run then ││
│ real) · kubectl apply --prune · wait rollout · delete ││
│ canary deployments ││
└────────────────────────────┬──────────────────────────────┼───────┘
▼ │
┌──────────────────────────────────────────────────────────┐│
│ 6. post-deploy-verify ││
│ all pods healthy · smoke tests vs ingress IP · ││
│ replica counts match ││
└────────────────────────────┬──────────────────────────────┘│
▼ on failure() ▼
┌──────────────────────────────────────────────────────────────────┐
│ 7. rollback kubectl rollout undo all deployments · remove │
│ canaries · verify │
└────────────────────────────┬─────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────────┐
│ 8. notify Slack on every outcome · PagerDuty critical if │
│ deploy failed AND rollback also failed │
└──────────────────────────────────────────────────────────────────┘
Etap canary 1 klonuje każde wdrożenie jako ${svc}-canary, skaluje je do skonfigurowanej wagi i obserwuje stan przez 5 minut — przerywając, jeśli wystąpi więcej niż 10 niepowodzeń lub więcej niż 2 ponowne uruchomienia poda. Etap 2 podnosi canary do 50% i obserwuje przez 10 minut. Dopiero wtedy full-deploy przełącza na 100% i usuwa wdrożenia canary.
Canary dotyczy wyłącznie GKE
Canary 10% → 50% → 100%, bramki testów dymnych/integracyjnych oraz automatyczne kubectl rollout undo istnieją wyłącznie w przepływach pracy GKE. Ścieżka produkcyjna VPS nie ma canary ani automatycznego wycofywania.
Wdrożenie produkcyjne na VPS — krok po kroku
To jest ścieżka, która faktycznie dostarcza produkcję. Jest sterowana przez deploy/deploy_to_vps.py, 7-krokowy skrypt Python/paramiko.
Topologia
Internet (HTTPS :443)
│
▼
┌───────────────────────────────────────────────────────┐
│ Debian VPS etl.exai.cloud (57.129.120.73) │
│ │
│ System nginx ── TLS termination (Plesk cert) ──┐ │
│ /auth/ → 127.0.0.1:4180 (Keycloak) │ │
│ /api/ → 127.0.0.1:8085 (api-gateway) │ │
│ / → 127.0.0.1:3006 (frontend) │ │
│ │ │ │
│ ┌───────────────────▼────────────────────────┐ │ │
│ │ docker compose — network dataflow-network │ │ │
│ │ 8 app services + postgres · keycloak · │ │ │
│ │ kafka · zookeeper · prometheus · grafana · │ │ │
│ │ minio │ │ │
│ └─────────────────────────────────────────────┘ │ │
│ (shared host: itsm.exai.cloud, openmeet.exai.cloud)│ │
└───────────────────────────────────────────────────────┘
7 kroków deploy_to_vps.py
- Archiwizacja —
git archive HEADtworzydeploy/dataflow-source.tar.gz(~17,4 MB). - Połączenie — SSH/SFTP do VPS.
- Przesłanie — przesłanie archiwum tar,
rm -rf /home/debian/dataflow, rozpakowanie go; przesłanie.env.productionzarówno dobackend/.env, jak ifrontend/.env.production; przesłanieetl-dataflow.conf. - Konfiguracja nginx — instalacja konfiguracji do
/etc/nginx/conf.d/etl-dataflow.confi uruchomienienginx -t. - Budowa obrazów — budowa jednego obrazu naraz (najpierw frontend, następnie 5 usług Kotlin z limitem czasu 900 s każda, aby zarządzać pamięcią VPS, następnie 2 usługi Python).
- Uruchomienie falami —
postgres(sleep 15) →zookeeper kafka(sleep 20) →keycloak prometheus grafana minio(sleep 15) →docker compose up -d(wszystkie); następnie przeładowanie systemowego nginx. - Weryfikacja —
docker compose psicurlfrontendu na127.0.0.1:3006.
Aby uruchomić pełne wdrożenie produkcyjne:
python deploy/deploy_to_vps.py
Zaszyte na sztywno poświadczenia
deploy_to_vps.py zawiera host, użytkownika i hasło VPS jako literały zaszyte na sztywno w kodzie źródłowym. Jest to znany problem bezpieczeństwa. Nie traktuj tego skryptu jako wzorca — poświadczenia należą do menedżera sekretów. To samo dotyczy deploy/.env.production (hasła do bazy danych / Keycloak / Grafana / MinIO w postaci jawnej) oraz seed_keycloak_vps.py.
Budowa obrazów po jednym naraz
Krok 5 celowo buduje obrazy sekwencyjnie. VPS ma ograniczoną pamięć i współdzieli host z niepowiązanymi vhostami Plesk, więc równoległa budowa wszystkich pięciu usług Kotlin wyczerpałaby pamięć RAM. Każda budowa Kotlin działa z limitem czasu 900 sekund.
Ostatnie zarejestrowane uruchomienie (.deploy.log, 19 maja) zakończyło się niepowodzeniem lokalnie podczas kroku 5 — błąd Windows UnicodeEncodeError (cp1252) spowodował awarię skryptu Python podczas strumieniowania wyjścia budowy frontendu. Jest to kosmetyczna awaria lokalna, a nie błąd po stronie serwera; kompletność wdrożenia dla tego uruchomienia jest niepewna i należy ją zweryfikować ręcznie.
Plik środowiska produkcyjnego
deploy/.env.production przesuwa każdy port, aby współistnieć ze stosem itsm na tym samym VPS:
| Komponent | Port produkcyjny |
|---|---|
| Postgres | 5433 |
| Keycloak | 4180 |
| Prometheus | 9095 |
| Grafana | 4001 |
| MinIO | 9002 / 9003 |
REDIS nie jest wdrożony (brama używa przejścia na ograniczanie liczby żądań w pamięci), ANTHROPIC_API_KEY jest pusty (funkcje AI copilota wyłączone), a CORS_ALLOWED_ORIGINS to https://etl.exai.cloud.
nginx — terminacja TLS i routing
Systemowy nginx na VPS (nie kontener) terminuje TLS przy użyciu certyfikatu Plesk w /opt/psa/var/certificates/scfht5otmidur05fohWXaj i odwrotnie proxuje do kontenerów.
| Ścieżka | Proxowane do | Uwagi |
|---|---|---|
/auth/ | 127.0.0.1:4180 | Kontener Keycloak |
/api/ | 127.0.0.1:8085/api/ | api-gateway; limity czasu 300 s, uaktualnienie WebSocket, SSE (proxy_buffering off) |
/ | 127.0.0.1:3006 | Kontener frontendu |
| port 80 | — | Przekierowanie 301 na HTTPS |
Nie ma oauth2-proxy. Uwierzytelnianie odbywa się bezpośrednio przez Keycloak OIDC — frontendowa aplikacja SPA wykonuje przepływ OIDC, a nginx po prostu proxuje /auth/ do kontenera Keycloak.
Poprawka nagłówka uaktualnienia w nginx
apply-nginx-fix.py rozwiązuje rzeczywisty incydent produkcyjny. Oryginalna konfiguracja zaszywała na sztywno Connection "upgrade" na /api/, co pozostawiało nginx w trybie tunelu połączenia i nigdy nie emitowało ramki HTTP/2 END_STREAM dla uwierzytelnionych tokenem JWT żądań fetch z przeglądarki. Objaw: strony marketplace, szablonów, my-pipelines i data-browser kręciły się w nieskończoność.
Poprawka wprowadza warunkową mapę:
map $http_upgrade $etl_connection_upgrade {
default upgrade;
'' close;
}
Skrypt tworzy kopię zapasową starej konfiguracji ze znacznikiem czasu, weryfikuje za pomocą nginx -t i przeładowuje tylko wtedy, gdy weryfikacja przebiegnie pomyślnie. Aby go zastosować:
python deploy/apply-nginx-fix.py
Ostrzeżenia nginx na współdzielonym hoście
Ponieważ etl.exai.cloud współdzieli VPS z itsm.exai.cloud i openmeet.exai.cloud, nazwa serwera koliduje z innymi vhostami Plesk i nginx emituje ostrzeżenia. Są one oczekiwane na tym hoście i nie wskazują na uszkodzone wdrożenie.
Migracje bazy danych — Flyway przy starcie
Na ścieżce VPS nie ma osobnego zadania migracji. Każda usługa Spring Boot uruchamia Flyway przy starcie wobec współdzielonej instancji PostgreSQL.
| Usługa | Lokalizacja migracji | Wersje | Tabela historii |
|---|---|---|---|
metadata-service | db/migration/metadata/ | V1–V51+ | flyway_schema_history |
monitor-service | db/migration/monitor/ | V9–V25 | flyway_schema_history_monitor |
pipeline-engine | db/migration/engine/ | V1, V3–V9 | flyway_schema_history_engine |
Ponieważ VPS pierwotnie miał tabele sprzed wprowadzenia Flyway, środowisko Compose wzmacnia Flyway, aby zbiegł się niezależnie od początkowego stanu bazy danych:
SPRING_FLYWAY_VALIDATE_ON_MIGRATE=false
SPRING_FLYWAY_BASELINE_ON_MIGRATE=true
SPRING_FLYWAY_BASELINE_VERSION=20.1
SPRING_FLYWAY_OUT_OF_ORDER=true
SPRING_FLYWAY_REPAIR_ON_MIGRATE=true
SPRING_FLYWAY_PLACEHOLDER_REPLACEMENT=false
Dodatkowo scripts/make-migrations-idempotent.py przepisuje DDL migracji w miejscu — CREATE TABLE → CREATE TABLE IF NOT EXISTS (to samo dla INDEX/SEQUENCE), ADD COLUMN IF NOT EXISTS oraz CREATE TRIGGER poprzedzone DROP TRIGGER IF EXISTS.
Ścieżka GKE zamiast tego uruchamia binarium Go /app/migrate --direction=up --environment=… przez kubectl exec — osobne, rozbieżne narzędzie migracji.
Inicjowanie danych po wdrożeniu
Po wdrożeniu na VPS kilka idempotentnych skryptów inicjuje środowisko uruchomieniowe:
deploy/seed_keycloak_vps.py— odwołuje się do administracyjnego REST API Keycloak na:4180. WłączaunmanagedAttributePolicy=ENABLED, ustawia atrybutworkspace_iddla 4 użytkowników demonstracyjnych, dodajeoidc-usermodel-attribute-mapper, abyworkspace_idbył przesyłany w każdym JWT, aktualizuje identyfikatory URI przekierowania / webOrigins / PKCE (S256) klientadataflow-app, resetuje hasła użytkowników demonstracyjnych i weryfikuje przyznanie JWT.deploy/seed_monitor_data.sql— inicjujemonitor_alertsimonitor_pipeline_runsrealistycznymi danymi telekomunikacyjnymi na potrzeby przeglądu e2e Playwright.deploy/create_user.py,setup_keycloak.py,ssh_cmd.py— pomocnicze skrypty jednorazowe.
Podsumowanie cyklu życia oprogramowania
Udokumentowany cykl życia CI/CD: kod (conventional commits, CODEOWNERS, Dependabot) → budowa i testy (ci.yml + zero-tolerance.yml + security-scan.yml) → budowa obrazu i publikacja do Artifact Registry → wdrożenie (develop→dev automatycznie, main→staging automatycznie, prod przez ręczny canary) → migracja → weryfikacja testami dymnymi/integracyjnymi → monitorowanie i automatyczne wycofywanie → wydanie przy tagach semver.
Rzeczywisty cykl życia VPS: git archive HEAD → przesłanie przez SSH → docker compose build na serwerze → uporządkowane docker compose up -d → Flyway uruchamia się przy starcie → ręczne inicjowanie Keycloak/monitora → przeładowanie nginx. Poprawki są stosowane za pomocą ukierunkowanych skryptów. Na VPS nie ma canary, automatycznego wycofywania ani automatycznych testów.