Operacje
Playbooki operacyjne i runbooki
Ta strona zbiera procedury operacyjne do obsługi platformy DataFlow AI Platform: runbook przełączania awaryjnego do odzyskiwania po awarii dla udokumentowanej topologii GKE oraz zestaw praktycznych runbooków dla produkcyjnego systemu na pojedynczym VPS, który faktycznie obsługuje dziś użytkowników. Każda procedura to ponumerowana sekwencja zrób-to-potem-tamto.
Zanim zaczniesz
Każdą procedurę tutaj kształtują dwa fakty:
- Produkcja to pojedynczy VPS Debian
etl.exai.cloud(57.129.120.73), uruchomiony na Docker Compose. Na tej ścieżce nie ma canary, nie ma automatycznego wycofywania zmian ani automatycznych testów. - Runbook DR/przełączania awaryjnego opisuje wieloregionowy model GKE, który nie jest tym, co działa na produkcji. Jest udokumentowany poniżej dla kompletności i dlatego, że stanowi docelową topologię — ale prawdziwy incydent na VPS obsługuje się runbookami Compose, a nie przełączaniem awaryjnym GKE.
Poświadczenia znajdują się w kodzie źródłowym — obchodź się ostrożnie
Kilka skryptów wdrożeniowych (deploy_to_vps.py, apply-nginx-fix.py, seed_keycloak_vps.py) oraz deploy/.env.production zawiera zakodowane na sztywno lub jawnotekstowe sekrety produkcyjne. To znane odkrycie dotyczące bezpieczeństwa. Postępując według tych runbooków, preferuj pozyskiwanie poświadczeń z menedżera sekretów i potraktuj rotację (zobacz poniżej) jako priorytet.
Runbook 1 — Odzyskiwanie po awarii / przełączanie awaryjne (GKE)
To udokumentowana wieloregionowa procedura DR z docs/runbooks/dr-failover-runbook.md. Dotyczy topologii GKE, a nie VPS.
Cele: RTO < 4 godziny, RPO < 1 minuta (opóźnienie asynchronicznej replikacji Cloud SQL).
Topologia:
europe-central2 (PRIMARY) europe-west3 (DR)
┌──────────────────────────┐ ┌──────────────────────────┐
│ GKE dataflow-autopilot- │ │ GKE dataflow-autopilot-dr │
│ production │ │ (1 replica, passive, │
│ Cloud SQL primary │ async │ read-only) │
│ │ ──────▶ │ Cloud SQL read replica │
│ │ repl. │ dataflow-pg-dr-replica │
└────────────┬──────────────┘ └────────────┬─────────────┘
│ │
└────── DNS health-check failover ────┘
api.dataflow.polkomtel.pl
Procedura przełączania awaryjnego (7 kroków)
- Zweryfikuj, że region podstawowy jest niedostępny. Sprawdź status klastra GKE, status Cloud SQL, endpoint stanu oraz stronę statusu GCP. Przełączanie awaryjne wymaga zatwierdzenia przez lidera dyżuru on-call przed kontynuacją.
- Awansuj replikę Cloud SQL. Uruchom
gcloud sql instances promote-replica— to nieodwracalne. Odpytuj instancję, aż jej stan będzieRUNNABLE. - Przełącz wdrożenia DR na tryb odczytu-zapisu. Załataj ConfigMap, aby ustawić
DB_READ_ONLY=false. - Zwiększ skalę wdrożeń DR. Skaluj z 1 → 3 replik i poczekaj na zakończenie wdrożenia (rollout).
- Przełącz DNS awaryjnie. Zweryfikuj lub wymuś przełączenie awaryjne DNS na adres IP DR
10.2.0.100. - Wykonaj test dymny DR. Potwierdź, że pody działają, że wszystkie 5 endpointów stanu usług odpowiada oraz że łączność z bazą danych działa.
- Powiadom interesariuszy. Wyślij powiadomienia przez Slacka, e-mail oraz PagerDuty.
Procedura powrotu (failback) (F1–F7)
- Zweryfikuj, że region podstawowy został odzyskany.
- Zbuduj świeżą replikę w
europe-central2z bieżącego (awansowanego) regionu podstawowego. - Zsynchronizuj nową replikę.
- Awansuj ją z powrotem na region podstawowy.
- Zwiększ skalę wdrożeń podstawowych i zmniejsz skalę wdrożeń DR.
- Przełącz DNS z powrotem na adres IP podstawowy
10.0.0.100. - Ponownie ustanów replikę DR i zaktualizuj stan Terraform.
Harmonogram testów
| Częstotliwość | Czynność |
|---|---|
| Ciągle | Monitorowanie opóźnienia replikacji |
| Co tydzień | Weryfikacja kontroli stanu (health-check) |
| Co miesiąc | Walidacja infrastruktury DR |
| Co kwartał | Ćwiczenie tabletop + pełny test przełączania awaryjnego |
Runbook 2 — Zrestartuj usługę (VPS)
Użyj tego, gdy pojedyncza usługa jest w złej kondycji, ale host i warstwa infrastruktury są w porządku.
- Potwierdź objaw.
docker compose ps— zidentyfikuj, która usługa nie jesthealthy. - Najpierw przechwyć dowody.
docker compose logs --tail=300 <service>i zapisz wynik przed restartem, aby przyczyna awarii nie została utracona. - Zrestartuj pojedynczą usługę.
docker compose restart <service>. - Obserwuj jej powrót.
docker compose logs -f <service>, aż uruchamianie się zakończy (Flyway uruchamia się przy starcie usług Kotlina — wypatruj sukcesu migracji). - Zweryfikuj stan.
curl -sf http://127.0.0.1:<host-port>/actuator/health | jq(na przykład8085dla gateway'a,8084dla monitor-service). - Sprawdź usługi zależne. Jeśli zrestartowałeś
postgres,keycloaklubkafka, zrestartuj usługi aplikacyjne, które od nich zależą, respektując kolejność startu: postgres → keycloak/kafka → usługi aplikacyjne → frontend. - Potwierdź od początku do końca. Załaduj
https://etl.exai.cloudi przetestuj dotkniętą stronę.
Respektuj kolejność startu
Plik Compose wymusza kolejność startu przez healthchecki i depends_on. Jeśli restartujesz infrastrukturę (postgres, kafka, keycloak), zwykły restart samego tego kontenera może pozostawić usługi aplikacyjne połączone z nieaktualnym endpointem. W razie wątpliwości zrestartuj potem zależne usługi aplikacyjne.
Runbook 3 — Zbadaj nieudane wdrożenie (VPS)
Użyj tego po tym, jak python deploy/deploy_to_vps.py zgłosi awarię lub zakończy się niejednoznacznie.
- Przeczytaj log wdrożenia. Sprawdź
deploy/.deploy.log, gdzie uruchomienie się zatrzymało. Uwaga: windowsowyUnicodeEncodeError(cp1252) może spowodować awarię lokalnego skryptu podczas strumieniowania wyniku budowania — to kwestia kosmetyczna i niekoniecznie oznacza, że wdrożenie po stronie serwera się nie powiodło. - Połącz się przez SSH z VPS i sprawdź stan kontenerów.
docker compose ps— czy wszystkie 8 usług oraz warstwa infrastruktury są obecne ihealthy? - Zidentyfikuj nieudany krok. Skrypt ma 7 kroków (archiwizacja → połączenie → wysyłka → konfiguracja nginx → budowanie → start w falach → weryfikacja). Awarie budowania (krok 5) najczęściej wynikają z presji pamięci VPS podczas budowania usług Kotlina.
- Sprawdź wynik budowania. Jeśli obraz nie zbudował się, ponownie uruchom to pojedyncze budowanie na serwerze:
docker compose build <service>(każde budowanie Kotlina może trwać kilka minut; skrypt dopuszcza 900s). - Sprawdź migracje Flyway.
docker compose logs <kotlin-service> | grep -i flyway— migracje uruchamiają się przy starcie, a nieudana migracja blokuje usługę. Zobacz Runbook 6, jeśli migracje utknęły. - Zwaliduj nginx.
nginx -tna hoście; jeśli krok 4 pozostawił zepsutą konfigurację, przywróć kopię zapasową ze znacznikiem czasu. - Uruchom ponownie lub zakończ ręcznie. Albo ponownie uruchom
deploy_to_vps.py(wykonuje pełne przebudowanie), albo, jeśli zawiodła tylko fala, uruchom pozostałe usługi:docker compose up -d. - W razie potrzeby ponownie zasiej (re-seed). Jeśli Keycloak został odtworzony, ponownie uruchom
python deploy/seed_keycloak_vps.py. - Zweryfikuj. Wykonaj
curlna frontend na127.0.0.1:3006i potwierdź, żehttps://etl.exai.cloudsię ładuje.
Runbook 4 — Odzyskaj zablokowany pipeline
Użyj tego, gdy uruchomienie pipeline'u zawiesiło się, nie postępuje lub utknęło w stanie nieterminalnym.
- Znajdź uruchomienie. Zidentyfikuj
runIdi bieżący status — przez interfejs Monitor lub bezpośrednio:docker compose exec postgres psql -U postgres -d dataflow_metadata \ -c "SELECT id, status, started_at FROM monitor_pipeline_runs ORDER BY started_at DESC LIMIT 20;" - Strumieniuj log uruchomienia. Pipeline-engine strumieniuje logi przez WebSocket na
/api/v1/runs/{runId}/stream; przeglądarka logów w interfejsie to renderuje. Potwierdź, czy uruchomienie jest faktycznie zawieszone, czy tylko wolne. - Sprawdź silnik.
docker compose logs --tail=300 pipeline-engine— szukaj błędów na poziomie zadań, niepowodzeń wykrywania cyklu/wiszących węzłów DAG lub nasycenia puli wątków. - Sprawdź samonaprawianie. Przy awarii zadania
SelfHealingServicesilnika klasyfikuje awarię i stosuje strategie odzyskiwania. Potwierdź, czy odzyskiwanie już się uruchomiło i do czego doprowadziło. - Sprawdź obliczenia w dół łańcucha. Jeśli pipeline używa Flink lub Spark/Dataproc, zweryfikuj, czy zewnętrzne zadanie faktycznie się uruchomiło — zablokowane uruchomienie może być zablokowanym zadaniem zewnętrznym, a nie problemem silnika.
- Anuluj kooperatywnie.
ExecutionContextsilnika wspiera kooperatywne anulowanie — anuluj uruchomienie przez API/interfejs, a nie przez zabicie kontenera. - Jeśli sam silnik jest zaklinowany, postępuj według Runbooka 2, aby zrestartować
pipeline-engine. Uruchomienia w trakcie zostaną utracone; sprawdź potemmonitor_pipeline_runs, aby potwierdzić, że uruchomienie jest w stanie terminalnym. - Uruchom pipeline ponownie, gdy przyczyna jest zrozumiana i usunięta.
Runbook 5 — Rotacja poświadczeń
Użyj tego, aby rotować sekrety produkcyjne, które obecnie znajdują się w deploy/.env.production oraz w skryptach wdrożeniowych.
- Zinwentaryzuj sekrety. Sekrety produkcyjne VPS to: hasło Postgres, hasło administratora Keycloak, hasło administratora Grafany, klucze dostępu/sekretne MinIO oraz hasła użytkowników demo. Hasło SSH do VPS jest zakodowane na sztywno w
deploy_to_vps.pyorazapply-nginx-fix.py. - Wygeneruj nowe wartości. Użyj silnych, losowo generowanych sekretów.
- Zaktualizuj
deploy/.env.production. Zastąp odpowiednie wartości. Ten plik jest źródłem prawdy dla środowiska Compose. - Rotuj poświadczenia magazynu danych w miejscu. W przypadku Postgres zmień hasło roli na nową wartość przed zrestartowaniem zależnych usług, aby pomyślnie się ponownie połączyły.
- Rotuj hasło SSH do VPS na hoście i zaktualizuj je w skryptach wdrożeniowych (lub, najlepiej, przenieś je poza kod źródłowy do menedżera sekretów).
- Wdróż ponownie. Uruchom
python deploy/deploy_to_vps.py, aby nowy.env.productionzostał wysłany, a usługi zrestartowały się z nowymi wartościami. - Ponownie zasiej Keycloak. Uruchom
python deploy/seed_keycloak_vps.py— jest idempotentny i resetuje hasła użytkowników demo oraz konfigurację klientadataflow-app. - Zweryfikuj. Potwierdź, że logowanie działa na
https://etl.exai.cloud, że logowanie do Grafany działa oraz żedocker compose pspokazuje wszystko jakohealthy.
Rotuj wszystko, jeśli skrypt wyciekł
Ponieważ hasło SSH i hasła magazynów danych są zakomitowane w kodzie źródłowym, traktuj każde ujawnienie repozytorium jako kompromitację poświadczeń. Rotuj pełny zestaw — SSH do VPS, Postgres, administrator Keycloak, Grafana, MinIO — a nie tylko to, które podejrzewasz.
Runbook 6 — Napraw migrację bazy danych
Użyj tego, gdy usługa Kotlina nie uruchamia się, ponieważ Flyway nie może zastosować lub zwalidować swoich migracji. Jest to najbardziej prawdopodobne na VPS, który rozpoczął życie z tabelami sprzed Flyway.
- Zidentyfikuj zawodzącą usługę.
docker compose logs <service> | grep -i flyway— błąd nazywa zawodzącą wersję oraz tabelę historii (flyway_schema_history,flyway_schema_history_enginelubflyway_schema_history_monitor). - Potwierdź, że flagi utwardzania są ustawione. Środowisko Compose powinno już ustawiać, dla dotkniętej usługi:
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=falseREPAIR_ON_MIGRATE=truepozwala Flyway samodzielnie skorygować rozbieżną historię przy starcie. - Uczyń migracje idempotentnymi. Jeśli migracja zawodzi, ponieważ obiekt już istnieje, uruchom przepisywacz:
Konwertujepython scripts/make-migrations-idempotent.py <migration-files>CREATE TABLE→CREATE TABLE IF NOT EXISTS(tak samo dla INDEX/SEQUENCE),ADD COLUMN IF NOT EXISTSoraz poprzedzaCREATE TRIGGERprzezDROP TRIGGER IF EXISTS. - W razie potrzeby sprawdź tabelę historii:
Wiersz zdocker compose exec postgres psql -U postgres -d dataflow_metadata \ -c "SELECT version, description, success FROM flyway_schema_history ORDER BY installed_rank DESC LIMIT 15;"success = falseto nieudana migracja, którą trzeba rozwiązać lub naprawić. - Zrestartuj usługę.
docker compose restart <service>i obserwuj logi — zREPAIR_ON_MIGRATEFlyway powinien zbiec się (skonwergować) przy starcie. - Pamiętaj o zależności kolejności lineage.
lineage-servicema Flyway wyłączony i ponownie wykorzystuje tabele lineage metadata-service (tworzone przez metadata V4/V48). Jeśli lineage zawodzi, przyczyną źródłową jest zwykle to, żemetadata-servicenie zastosował jeszcze tych migracji — najpierw napraw metadata. - Zweryfikuj. Usługa osiąga stan
healthy, a/actuator/healthzwracaUP.
Runbook 7 — Reaguj na incydent
Ogólna procedura dla nieplanowanego problemu produkcyjnego na VPS.
- Potwierdź i określ zakres. Odtwórz objaw. Określ zasięg rażenia: jedna strona, jedna usługa czy cała platforma? Uruchom
docker compose ps. - Zadeklaruj i komunikuj. W przypadku czegokolwiek widocznego dla użytkownika, zadeklaruj incydent i powiadom interesariuszy. (Na ścieżce GKE jest to PagerDuty
#dataflow-incidents; na VPS użyj uzgodnionego kanału zespołu.) - Triażuj z obserwowalnością. Użyj listy kontrolnej obserwowalności: gotowość gateway'a, panele latencji/5xx w Grafanie, skoki awarii w
monitor_pipeline_runs,monitor_alerts. PrzechwyćX-Request-Idnieudanego żądania, aby skorelować logi między usługami. - Ustabilizuj przed naprawą. Preferuj najszybsze bezpieczne działanie przywracające usługę — zwykle restart dotkniętej usługi (Runbook 2) — zamiast dogłębnej naprawy podczas incydentu.
- Zastosuj właściwy runbook. Nieudane wdrożenie → Runbook 3. Zablokowany pipeline → Runbook 4. Awaria migracji → Runbook 6. Zawieszenie nginx przy pobieraniu JWT → zastosuj
python deploy/apply-nginx-fix.py(tworzy kopię zapasową, waliduje przeznginx -ti przeładowuje tylko przy sukcesie). - Zweryfikuj odzyskanie. Załaduj
https://etl.exai.cloud, przetestuj dotknięty przepływ pracy i potwierdź, że endpointy stanu oraz strumienie alertów są czyste. - Zakomunikuj rozwiązanie. Powiadom interesariuszy, że incydent został rozwiązany.
- Napisz przegląd poincydentalny. Zapisz oś czasu, przyczynę źródłową oraz działania następcze — zwłaszcza to, co powinno stać się nowym lub zaktualizowanym runbookiem.
Incydent z nagłówkiem upgrade nginx
Powracający, dobrze zrozumiany tryb awarii: jeśli konfiguracja /api/ nginx koduje na sztywno Connection "upgrade", pobrania w przeglądarce uwierzytelnione JWT nigdy nie otrzymują ramki HTTP/2 END_STREAM, a strony (marketplace, templates, my-pipelines, data-browser) kręcą się w nieskończoność. Naprawą jest warunkowa mapa map $http_upgrade $etl_connection_upgrade { default upgrade; '' close; } stosowana przez apply-nginx-fix.py. Jeśli widzisz zawieszone spinnery po zmianie nginx, podejrzewaj to w pierwszej kolejności.
Szybki przegląd runbooków
| Sytuacja | Runbook | Kluczowe polecenie |
|---|---|---|
| Awaria regionu GKE | 1 — Przełączanie awaryjne DR | gcloud sql instances promote-replica |
| Jedna usługa w złej kondycji | 2 — Restart usługi | docker compose restart <service> |
| Wdrożenie nieudane/niejednoznaczne | 3 — Zbadaj wdrożenie | sprawdź deploy/.deploy.log, docker compose ps |
| Pipeline zawieszony | 4 — Odzyskaj pipeline | kooperatywne anulowanie, potem ponowne uruchomienie |
| Rotacja sekretów | 5 — Rotacja poświadczeń | edytuj .env.production, wdróż ponownie, ponownie zasiej |
| Usługa nie uruchamia się przez Flyway | 6 — Napraw migrację | make-migrations-idempotent.py |
| Nieplanowany problem produkcyjny | 7 — Reagowanie na incydenty | lista kontrolna obserwowalności + ukierunkowany runbook |
| Zawieszenie nginx przy pobieraniu JWT | 7 (callout) | python deploy/apply-nginx-fix.py |