Manifest, wpięcie w kustomization i runbook krok po kroku. DECYZJE ZAPISANE W MANIFEŚCIE, ŻEBY NIE TRZEBA ICH BYŁO ODTWARZAĆ Z GŁOWY: local-path, NIE NFS. Postgres zakłada semantykę blokad i fsync, której NFS nie gwarantuje — to klasyczne źródło uszkodzenia bazy przy nagłym restarcie. Ceną jest przywiązanie do węzła; przy luście odtwarzalnym z Excela to akceptowalne. strategy: Recreate. Wolumen jest ReadWriteOnce, a dwa procesy Postgresa na jednym katalogu danych to uszkodzona baza — rolling próbowałby wstać z nowym podem, zanim stary zejdzie. PGDATA w PODKATALOGU wolumenu: katalog główny potrafi zawierać wpisy systemu plików, a initdb odmawia pracy w niepustym katalogu. Wersja PRZYPIĘTA i poza image-updaterem: podbicie majora wymaga migracji katalogu danych, więc nie może się zdarzyć samo, w nocy, przy okazji builda aplikacji. C.UTF-8 zamiast pl_PL.UTF-8: dopasowanie tekstu robimy przez unaccent i pg_trgm, nie przez collation, a pl_PL wymagałby obrazu z wygenerowanymi lokalizacjami. DATA_PROVIDER zostaje na `excel`. Postgres można wdrożyć i obejrzeć BEZ ryzyka dla działającego wyszukiwania; przełączenie to osobna, późniejsza decyzja. SPRAWDZONE PRZED ODDANIEM: `kubectl kustomize astrololo` składa komplet 21 zasobów bez błędu, YAML parsuje się poprawnie, audyt odwołań potwierdza, że jedynym brakującym sekretem jest astrololo-postgres (oba klucze), a DSN postgresql+psycopg:// jest rozpoznawany przez SQLAlchemy z psycopg 3. NIE SPRAWDZONE: skrypt inicjalizujący nie biegł przeciwko prawdziwemu Postgresowi — na maszynie, na której to powstawało, nie ma Dockera. Zapisane wprost w runbooku; krok 4 jest tam prawdziwym testem. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
11 KiB
Postgres — lustro baz w SQL (DAN-28)
Runbook krok po kroku. Zakłada, że nie pamiętasz nic z rozmowy, w której to powstało.
Co to jest — i czego to NIE jest
Postgres trzyma lustro plików Excela, żeby wyszukiwanie było szybsze i mądrzejsze.
ŹRÓDŁEM PRAWDY SĄ PLIKI EXCELA NA NFS. Utrata tej bazy nie jest utratą danych — odbudowuje się z plików. Dlatego kopie zapasowe Postgresa są tu opcjonalne, a wolumen stoi na
local-pathzamiast na NFS.
Po co w ogóle Postgres, skoro danych jest mało (~54 tys. wierszy)? Nie dla skali —
przy takim rozmiarze SQLite bywa szybszy. Dla wyszukiwania w polskim,
nieznormalizowanym tekście: pg_trgm (dopasowanie mimo literówek) i unaccent
(ogonki) nie mają w SQLite taniego zamiennika.
Część 1 — co zrobiłem za Ciebie (w repo)
Nic z tego nie wymaga Twojej ręki, ale wiedz, co jest gdzie:
| plik | co w nim jest |
|---|---|
astrololo/postgres.yaml |
PVC (5 Gi, local-path), ConfigMap z SQL-em inicjalizującym, Deployment (Recreate), Service ClusterIP |
astrololo/kustomization.yaml |
postgres.yaml dopisany przed data.yaml |
astrololo/data.yaml |
SQL_URL z sekretu; DATA_PROVIDER zostaje excel |
repo aplikacji: services/data/requirements.txt |
sterownik psycopg[binary] |
Decyzje, które w tych plikach zapadły — żeby nie trzeba było ich odtwarzać z głowy:
local-path, nie NFS. Postgres zakłada semantykę blokad ifsync, której NFS nie gwarantuje — to klasyczne źródło uszkodzenia bazy przy nagłym restarcie. Ceną jest przywiązanie do węzła; przy luście odtwarzalnym z Excela to nie boli.strategy: Recreate. Wolumen jestReadWriteOnce, a dwa procesy Postgresa na jednym katalogu danych to uszkodzona baza. Rolling próbowałby wstać z nowym podem, zanim stary zejdzie.PGDATAw podkatalogu (…/data/pgdata). Katalog główny wolumenu potrafi zawierać wpisy systemu plików, ainitdbodmawia pracy w niepustym katalogu.- Wersja przypięta (
postgres:17) i POZA image-updaterem. Podbicie majora wymaga migracji katalogu danych — nie może się zdarzyć samo, w nocy, przy okazji builda aplikacji. DATA_PROVIDERzostaje naexcel. Postgres można wdrożyć i obejrzeć bez żadnego ryzyka dla działającego wyszukiwania. Przełączenie nasqlto osobna, późniejsza decyzja — po wdrożeniu lustra.
Część 2 — co musisz zrobić Ty
Krok 0. Sprawdź, że klaster ma local-path
kubectl get storageclass
Oczekiwany wynik: linia z local-path i dopiskiem (default). W k3s jest
domyślnie. Jeśli jej nie ma — zatrzymaj się tutaj i daj znać; bez niej PVC
zawiśnie w Pending.
Krok 1. Utwórz sekret astrololo-postgres
Ta sama konwencja co astrololo-auth: sekretu nie ma w repo, bo to repo
GitOps i cokolwiek by tu wpadło, zostałoby w historii gita na zawsze.
PGPASS="$(openssl rand -base64 24 | tr -d '/+=' | head -c 32)"
kubectl -n astrololo create secret generic astrololo-postgres \
--from-literal=POSTGRES_PASSWORD="$PGPASS" \
--from-literal=SQL_URL="postgresql+psycopg://astrololo:${PGPASS}@postgres:5432/astrololo"
unset PGPASS
Hasła nie musisz nigdzie zapisywać ani nigdy oglądać — używają go tylko usługi między sobą. Gdybyś kiedyś go potrzebował:
kubectl -n astrololo get secret astrololo-postgres \
-o jsonpath='{.data.POSTGRES_PASSWORD}' | base64 -d; echo
Dlaczego dwa klucze, skoro hasło jest w obu?
POSTGRES_PASSWORDczyta sam Postgres przy inicjalizacji,SQL_URLczyta warstwa danych. Rozdzielone, bo to dwa różne formaty i dwóch różnych odbiorców — sklejanie DSN-a w manifeście wymagałoby wstawienia tam hasła.
Krok 2. Wdróż
Jeśli ArgoCD pilnuje katalogu astrololo, wystarczy zmergować PR — reszta
zrobi się sama. Ręcznie:
kubectl apply -k astrololo
Krok 3. Sprawdź, że wstało
kubectl -n astrololo rollout status deploy/postgres
kubectl -n astrololo get pvc postgres-data
Oczekiwane: deployment "postgres" successfully rolled out i PVC w stanie Bound.
Krok 4. Sprawdź, że rozszerzenia się założyły
To jest najważniejszy sprawdzian tego wdrożenia — bez tych rozszerzeń cały sens stawiania Postgresa znika.
kubectl -n astrololo exec deploy/postgres -- \
psql -U astrololo -d astrololo -c "\dx"
Na liście muszą być pg_trgm i unaccent. Sprawdź też, że działają:
kubectl -n astrololo exec deploy/postgres -- psql -U astrololo -d astrololo -c \
"SELECT unaccent('różdżka') AS bez_ogonkow,
similarity(unaccent('różdżka'), 'rozdzka') AS po_zdjeciu_ogonkow,
similarity('kowalski', 'kowlaski') > 0.3 AS literowka_lapana;"
Oczekiwane dokładnie:
bez_ogonkow | po_zdjeciu_ogonkow | literowka_lapana
-------------+--------------------+------------------
rozdzka | 1 | t
Pierwsza kolumna pokazuje, że unaccent zna polskie znaki; druga, że po zdjęciu
ogonków słowa są identyczne (stąd równo 1); trzecia, że pg_trgm łapie
przestawione litery. Jeśli druga kolumna nie jest równa 1, rozszerzenia są, ale
reguły unaccent nie obsługują polskich znaków — daj znać, bo to zmienia sposób
budowania indeksów.
Krok 5. Sprawdź, że warstwa danych ma połączenie
kubectl -n astrololo rollout restart deploy/data
kubectl -n astrololo rollout status deploy/data
kubectl -n astrololo logs deploy/data --tail=30 | grep -i -E "sql|postgres|error" || echo "brak wzmianek — OK"
Na tym etapie warstwa danych nadal czyta Excela (DATA_PROVIDER=excel).
Postgres tylko stoi i czeka. Aplikacja ma działać dokładnie jak przedtem —
i to jest oczekiwany wynik tego wdrożenia.
Część 3 — obsługa na co dzień
Zajrzeć do bazy
kubectl -n astrololo exec -it deploy/postgres -- psql -U astrololo -d astrololo
Przydatne w środku: \dt mirror.* (tabele lustra), \dx (rozszerzenia),
\l (bazy), \q (wyjście).
Podłączyć narzędzie graficzne z laptopa
Baza celowo nie jest wystawiona poza klaster. Na czas jednej sesji:
kubectl -n astrololo port-forward deploy/postgres 5432:5432
i łączysz się na localhost:5432, użytkownik astrololo, baza astrololo,
hasło jak w kroku 1. Po zamknięciu terminala tunel znika.
Dodanie rozszerzenia do ISTNIEJĄCEJ bazy
⚠️ Pułapka. Skrypty z
/docker-entrypoint-initdb.d/uruchamiają się wyłącznie przy pierwszej inicjalizacji, na pustym katalogu danych. Dopisanie rozszerzenia do ConfigMapy nie zrobi nic, jeśli baza już istnieje.
kubectl -n astrololo exec deploy/postgres -- \
psql -U astrololo -d astrololo -c "CREATE EXTENSION IF NOT EXISTS nazwa;"
Kopia zapasowa (opcjonalna — patrz nagłówek)
kubectl -n astrololo exec deploy/postgres -- \
pg_dump -U astrololo -d astrololo -Fc > astrololo-$(date +%F).dump
Odtworzenie:
kubectl -n astrololo exec -i deploy/postgres -- \
pg_restore -U astrololo -d astrololo --clean --if-exists < astrololo-2026-08-06.dump
Wyczyścić lustro i wczytać od nowa
kubectl -n astrololo exec deploy/postgres -- psql -U astrololo -d astrololo -c \
"DROP SCHEMA mirror CASCADE; CREATE SCHEMA mirror;"
Po tym warstwa danych odbuduje lustro z plików Excela (gdy lustro będzie już zaimplementowane — dziś ta komenda tylko czyści pusty schemat).
Podbicie wersji Postgresa
Nie robi się tego przez zmianę tagu w manifeście. Major wymaga migracji katalogu danych. Ponieważ to lustro:
kubectl -n astrololo scale deploy/data --replicas=0- skasuj Deployment i PVC (
kubectl -n astrololo delete deploy/postgres pvc/postgres-data) - zmień tag obrazu w
postgres.yaml, wdróż ponownie kubectl -n astrololo scale deploy/data --replicas=1— lustro odbuduje się z Excela
To jest właśnie ta sytuacja, w której „lustro, nie źródło prawdy" oszczędza dzień pracy.
Co zostało sprawdzone przed oddaniem, a co nie
Sprawdzone: kubectl kustomize astrololo składa komplet 21 zasobów bez błędu;
YAML wszystkich manifestów parsuje się poprawnie; audyt odwołań do sekretów
potwierdza, że jedyne brakujące to astrololo-postgres (oba klucze); DSN
postgresql+psycopg://… jest poprawnie rozpoznawany przez SQLAlchemy 2.0
z zainstalowanym psycopg 3.
NIE sprawdzone, bo nie było na czym: skrypt inicjalizujący nie został uruchomiony przeciwko prawdziwemu Postgresowi (na maszynie, na której to powstawało, nie ma Dockera). Składnia jest prosta i przejrzana, ale krok 4 jest tu prawdziwym testem — jeśli coś ma nie zadziałać, to właśnie tam.
Część 4 — typowe problemy
| objaw | przyczyna | co zrobić |
|---|---|---|
PVC wisi w Pending |
brak local-path albo brak miejsca na węźle |
kubectl get storageclass, kubectl describe pvc postgres-data |
pod w CrashLoopBackOff, w logach directory not empty |
PGDATA wskazuje katalog główny wolumenu |
sprawdź, czy PGDATA to …/data/pgdata (jest w manifeście) |
pod nie startuje, secret not found |
nie wykonany krok 1 | utwórz sekret i kubectl -n astrololo rollout restart deploy/postgres |
\dx nie pokazuje pg_trgm |
baza założona przed dodaniem ConfigMapy | patrz „Dodanie rozszerzenia do istniejącej bazy" |
warstwa danych: ModuleNotFoundError: psycopg |
obraz zbudowany przed dodaniem sterownika | poczekaj na build z CI albo kubectl -n astrololo rollout restart deploy/data po nowym obrazie |
password authentication failed |
SQL_URL w sekrecie nie zgadza się z POSTGRES_PASSWORD |
odtwórz sekret krokiem 1 (oba klucze naraz) i zrestartuj oba deploymenty |
Gdy nic nie pasuje — pierwsze dwie komendy do wklejenia:
kubectl -n astrololo describe pod -l app=postgres | tail -30
kubectl -n astrololo logs deploy/postgres --tail=50
Część 5 — pełne odtworzenie ręczne, bez ArgoCD i bez repo
Gdyby trzeba było postawić to od zera na czystym klastrze:
kubectl create namespace astrololo
PGPASS="$(openssl rand -base64 24 | tr -d '/+=' | head -c 32)"
kubectl -n astrololo create secret generic astrololo-postgres \
--from-literal=POSTGRES_PASSWORD="$PGPASS" \
--from-literal=SQL_URL="postgresql+psycopg://astrololo:${PGPASS}@postgres:5432/astrololo"
unset PGPASS
kubectl apply -f astrololo/postgres.yaml
kubectl -n astrololo rollout status deploy/postgres
kubectl -n astrololo exec deploy/postgres -- psql -U astrololo -d astrololo -c "\dx"
Reszta aplikacji wymaga jeszcze sekretów astrololo-auth i astrololo-link —
patrz README.md.