Wiki
Kopie zapasowe i odzyskiwanie
Jak QuoteNode obsługuje kopie zapasowe bazy danych i plików — archiwa szyfrowane age, odtwarzanie sterowane manifestem, zestawy odzyskiwania i profile runtime.
Kopie zapasowe i odzyskiwanie
QuoteNode zawiera wbudowany system kopii zapasowych, który chroni Twoje dane handlowe przed awariami sprzętu, przypadkowym usunięciem i incydentami operacyjnymi.
Co jest objęte kopią zapasową
Każda kopia zapasowa tworzy archiwum zawierające:
- Zrzut bazy danych — pełny zrzut PostgreSQL w formacie niestandardowym (
pg_dump --format=custom), obejmujący wszystkie tabele, sekwencje i ograniczenia. - Magazyn plików — wszystkie przesłane pliki (zdjęcia produktów, logo firm) oraz wygenerowane pliki PDF, zarchiwizowane jako
files.tar.gzze ścieżkami względnymi. - Manifest kopii zapasowej — plik
backup-manifest.jsonzawierający wersję źródłową, tryb szyfrowania, tryb ładunku bazy danych, sumy kontrolne oraz metadane zgodności odtwarzania. - Sumy kontrolne integralności — plik
checksums.sha256zawierający skróty SHA-256 wszystkich komponentów archiwum.
Szyfrowanie archiwum
Nowe szyfrowane kopie zapasowe używają szyfrowania odbiorcą age (AGE_RECIPIENT). To jedyny obsługiwany model szyfrowania nowych archiwów.
- Proces tworzenia kopii zapasowej używa wyłącznie publicznego odbiorcy age (
age1...) — do tworzenia kopii nie jest potrzebny żaden klucz prywatny na serwerze. - Szyfrowane archiwa mają rozszerzenie
.tar.age. - Suma kontrolna SHA-256 szyfrowanego archiwum jest przechowywana obok archiwum w celu weryfikacji po stronie operatora.
- Odszyfrowanie wymaga pasującej prywatnej tożsamości age, która jest przechowywana wyłącznie w zestawie odzyskiwania operatora.
- Surowa prywatna tożsamość age nigdy nie jest przechowywana w bazie danych, logach, DTO ani argumentach procesu po potwierdzeniu konfiguracji.
Lokalne kopie nieszyfrowane
Lokalne nieszyfrowane kopie zapasowe (.tar) są dozwolone tylko wtedy, gdy spełnione są wszystkie poniższe warunki:
BACKUP_ARCHIVE_ENCRYPTION_MODE=NONEBACKUP_ALLOW_UNENCRYPTED_LOCAL_BACKUP=true- Miejsce docelowe to magazyn lokalny
BACKUP_DATABASE_PAYLOAD_MODE=APP_ENCRYPTED- Administrator jawnie potwierdził ryzyko w interfejsie
Archiwa nieszyfrowane są blokowane dla magazynu zdalnego oraz dla ładunków z odszyfrowanymi danymi PII.
Konfiguracja (.env). Odbiorca age jest generowany przez Zestaw odzyskiwania (Administracja → Bezpieczeństwo → Backup) i przechowywany w instancji — nie umieszczasz klucza w .env. Ustaw tylko dwie zmienne trybu:
BACKUP_ENABLED=true
BACKUP_ARCHIVE_ENCRYPTION_MODE=AGE_RECIPIENT
BACKUP_DATABASE_PAYLOAD_MODE=APP_ENCRYPTED
# Alternative — encrypted archive + decrypted PII (cross-instance portability):
# BACKUP_DATABASE_PAYLOAD_MODE=PII_DECRYPTED
BACKUP_ALLOW_UNENCRYPTED_LOCAL_BACKUP=false
Aby wyłączyć szyfrowanie archiwum (tylko lokalnie, niezalecane — dane PII nadal pozostają zaszyfrowane w ładunku):
BACKUP_ARCHIVE_ENCRYPTION_MODE=NONE
BACKUP_ALLOW_UNENCRYPTED_LOCAL_BACKUP=true
Przy
AGE_RECIPIENT, jeśli nie wygenerowano jeszcze żadnego odbiorcy, kopia zapasowa kończy się głośnym błędem zamiast zapisać niezabezpieczone archiwum. KombinacjaNONE+PII_DECRYPTEDjest odrzucana.
Tryby ładunku bazy danych
| Tryb | Opis |
|---|---|
APP_ENCRYPTED | Domyślny. Pola PII pozostają zaszyfrowane kluczem aplikacji DB_ENCRYPTION_KEY. Odtwarzanie wymaga tego samego klucza szyfrowania lub pasującego odcisku klucza. |
PII_DECRYPTED | Pola PII są odszyfrowywane do tymczasowej bazy danych przed archiwizacją. Skróty haseł użytkowników pozostają bez zmian. Wymaga szyfrowania archiwum AGE_RECIPIENT. Przydatne do przenoszenia między instancjami. |
Podczas odtwarzania kopii PII_DECRYPTED do środowiska docelowego z ENCRYPT_PII=true proces odtwarzania automatycznie ponownie szyfruje dane PII kluczem DB_ENCRYPTION_KEY środowiska docelowego przed normalnym uruchomieniem aplikacji.
Zestaw odzyskiwania
Podczas konfiguracji administrator generuje (lub importuje) odbiorcę age dla szyfrowanych kopii zapasowych. Zestaw odzyskiwania to plik ZIP zawierający:
quotenode-age-identity.txt— prywatna tożsamość age (zwracana tylko raz)quotenode-age-recipient.txt— publiczny odbiorca i odciskquotenode-backup-verify.sh— lokalny skrypt weryfikacyjny (Linux/macOS)quotenode-backup-verify.ps1— lokalny skrypt weryfikacyjny (Windows)README.md— komendy weryfikacji i odtwarzania
Krytyczne: Prywatna tożsamość age jest zwracana tylko raz podczas generowania zestawu. W razie utraty szyfrowane archiwa nie będą mogły zostać odszyfrowane. Przechowuj zestaw odzyskiwania w bezpiecznym miejscu offline.
Administrator musi potwierdzić, że zestaw został zapisany i przetestowany, zanim kreator konfiguracji oznaczy backup jako gotowy do produkcji.
Lokalna weryfikacja kopii zapasowej
Pobrane kopie zapasowe można zweryfikować poza działającą aplikacją za pomocą skryptów z zestawu odzyskiwania:
scripts/quotenode-backup-verify.sh \
--archive /path/backup.tar.age \
--identity /path/quotenode-age-identity.txt \
--expected-sha256 <sha256-from-ui-or-sidecar>
Weryfikator:
- Sprawdza sumę kontrolną SHA-256 szyfrowanego archiwum względem oczekiwanej wartości lub pliku pomocniczego
.sha256. - Odszyfrowuje za pomocą
age -ddo prywatnego katalogu tymczasowego. - Waliduje wpisy tar (odrzuca ścieżki bezwzględne,
.., dowiązania symboliczne, pliki urządzeń). - Sprawdza, czy
backup-manifest.jsonistnieje i ma obsługiwaną wersję. - Uruchamia
sha256sum -c checksums.sha256. - Uruchamia
pg_restore --list db.dump, gdypg_restorejest dostępny. - Domyślnie usuwa odszyfrowane dane tymczasowe (użyj
--keep-decrypted, aby je zachować).
Backup online (domyślny)
Kopie zapasowe online działają, gdy aplikacja obsługuje żądania. Nie ma przestoju.
pg_dumpw formacie niestandardowym tworzy transakcyjnie spójną migawkę bez blokowania tabel ani wstrzymywania zapytań.- Magazyn plików jest archiwizowany równolegle — przesłane pliki i PDF-y są niezmienne po utworzeniu, więc archiwum jest zawsze spójne.
- PostgreSQL nie musi się zatrzymywać, aby uzyskać spójny zrzut bazy danych.
Jak działają kopie zapasowe: wybór topologii
Istnieją dwa sposoby ciągłego uruchamiania zaplanowanych kopii zapasowych. Oba tworzą identyczne archiwa — różnią się jedynie tym, gdzie wykonywane jest zadanie kopii zapasowej.
| Topologia | Kiedy stosować | Kontenery |
|---|---|---|
| Dedykowany worker (domyślnie) | Produkcja. Kopie zapasowe działają w osobnym kontenerze backup-worker, więc nigdy nie rywalizują z ruchem webowym o pamięć ani CPU. | backend web + backup-worker |
| W procesie | Laptop / ewaluacja / małe instalacje na jednym hoście. Backend web sam wykonuje kopie zapasowe — o jeden kontener mniej do obsługi. | tylko backend web |
Samo zadanie kopii zapasowej to ten sam potok powłoki (pg_dump → archiwizacja → szyfrowanie) w obu przypadkach, uruchamiany poza stertą JVM, więc jego koszt pamięci jest umiarkowany.
Jak wybrać topologię
Dwie zmienne środowiskowe współpracują ze sobą:
JOBS_MODEdecyduje, który kontener faktycznie uruchamia zaplanowane zadanie kopii zapasowej.BACKUP_RUNTIME_PROFILEto etykieta zapisywana w logach i manifestach kopii zapasowych, dzięki której możesz później sprawdzić, jak każda kopia została utworzona.
| Topologia | Backend web | Backup worker |
|---|---|---|
| Dedykowany worker (domyślnie) | JOBS_MODE=web + BACKUP_RUNTIME_PROFILE=WORKER | kontener JOBS_MODE=backup-only |
| W procesie | JOBS_MODE=all + BACKUP_RUNTIME_PROFILE=WEB_MAINTENANCE | (brak) |
Dołączony produkcyjny plik Compose używa topologii dedykowanego workera. Przewodniki laptop/ewaluacja używają trybu w procesie (
JOBS_MODE=all+BACKUP_RUNTIME_PROFILE=WEB_MAINTENANCE), aby uruchamiać tylko jeden kontener backendu.
Dokumentacja profili runtime
BACKUP_RUNTIME_PROFILE przyjmuje cztery wartości. Pierwsze dwie dotyczą powyższych topologii ciągłych; ostatnie dwie dotyczą jednorazowych kopii zapasowych sterowanych przez operatora.
| Profil | Opis |
|---|---|
WORKER | Domyślny. Stały kontener backup-worker uruchamia zaplanowane kopie zapasowe przez cron. |
WEB_MAINTENANCE | Backend web wykonuje zaplanowane kopie zapasowe bezpośrednio. Nie jest potrzebny dodatkowy kontener workera. |
ONE_SHOT | Zewnętrzny cron lub operator uruchamia scripts/backup-one-shot.sh i kończy działanie. Brak stałego workera. |
OFFLINE_MAINTENANCE | Zautomatyzowany skrypt ogłasza przestój, zatrzymuje frontend/backend/backup-worker, utrzymuje działanie PostgreSQL, wykonuje kopię zapasową, restartuje usługi i weryfikuje stan zdrowia. |
Kopia jednorazowa (one-shot)
COMPOSE_PROJECT_NAME=quotenode COMPOSE_ENV_FILE=infra/.env.prod \
BACKUP_ARCHIVE_ENCRYPTION_MODE=AGE_RECIPIENT \
BACKUP_AGE_RECIPIENT=age1... \
bash scripts/backup-one-shot.sh
Konserwacja offline
COMPOSE_PROJECT_NAME=quotenode COMPOSE_ENV_FILE=infra/.env.prod \
BACKUP_OFFLINE_NOTICE_SECONDS=300 \
bash scripts/backup-offline-maintenance.sh
Skrypt konserwacji offline:
- Ogłasza zaplanowaną konserwację (konfigurowalny okres karencji).
- Opcjonalnie włącza statyczną stronę konserwacji na czas wyłączenia usług.
- Zatrzymuje frontend, backend i backup-worker (utrzymuje działanie PostgreSQL).
- Uruchamia kopię jednorazową.
- Restartuje wszystkie usługi.
- Weryfikuje stan zdrowia backendu.
- Zapisuje dziennik konserwacji JSONL na poziomie hosta.
Jeśli skrypt zawiedzie w którymkolwiek momencie, mechanizm bezpieczeństwa automatycznie restartuje usługi.
Użyj --dry-run, aby podejrzeć plan bez wykonywania:
bash scripts/backup-offline-maintenance.sh --dry-run
Harmonogram
- Domyślny harmonogram: codziennie o 2:00 (
0 0 2 * * *, konfigurowalny przezBACKUP_CRON) - Włączanie/wyłączanie:
BACKUP_ENABLED=true|false - Ręczne wyzwalanie: Administratorzy mogą wyzwolić natychmiastową kopię zapasową z panelu administracyjnego lub przez API (
POST /api/v1/admin/backup/trigger). Wyzwalacz zwraca odpowiedź natychmiast (HTTP 202), podczas gdy kopia zapasowa działa asynchronicznie. - Ochrona przed współbieżnością: Blokada oparta na bazie danych zapewnia, że w danym momencie działa tylko jedna kopia zapasowa — we wszystkich workerach i ręcznych wyzwoleniach.
Dla profili ONE_SHOT i OFFLINE_MAINTENANCE wewnętrzny harmonogram jest wyłączony — kopiami zapasowymi zarządza się zewnętrznie.
Opcje magazynu
Magazyn lokalny (domyślny)
Kopie zapasowe są przechowywane w BACKUP_LOCAL_DIR (domyślnie: /app/data/backups), zmapowanym na wolumin Dockera.
W środowisku produkcyjnym lokalne kopie zapasowe powinny być uzupełniane o kopie zdalne.
Magazyn zdalny przez rclone
Jeśli skonfigurowano BACKUP_RCLONE_REMOTE, kopie zapasowe są automatycznie przesyłane do zdalnego miejsca docelowego po utworzeniu lokalnym. rclone obsługuje ponad 70 dostawców magazynu w chmurze, w tym zgodne z S3, Google Cloud Storage, Azure Blob, SFTP i WebDAV.
Pobieranie kopii zapasowych
Pobieranie archiwów kopii zapasowych wymaga autoryzacji podwyższonej (step-up):
- Przejdź do Ustawienia > Backup.
- Kliknij Pobierz przy wpisie kopii zapasowej.
- Wprowadź swoje bieżące hasło (oraz kod TOTP, jeśli włączono MFA).
- System wydaje krótkotrwałe, jednorazowe pozwolenie na pobranie (domyślnie: 120 sekund).
- Archiwum pobiera się automatycznie.
Token pozwolenia jest przechowywany wyłącznie jako skrót SHA-256. Utworzenie pozwolenia i pobranie są audytowane, a pozostali administratorzy są powiadamiani.
Uwaga: Bezpośrednie pobranie bez pozwolenia zwraca 403. Kopie zapasowe w magazynie zdalnym nie mogą być pobierane przez panel administracyjny.
Metadane i ślad audytowy
Każda kopia zapasowa jest śledzona w tabeli backup_logs wraz z:
- Statusem (RUNNING, SUCCESS, FAILED)
- Inicjatorem (SCHEDULER lub ADMIN)
- Czasem rozpoczęcia/zakończenia
- Rozmiarem (w bajtach) i sumą kontrolną SHA-256
- Miejscem docelowym (ścieżka lokalna lub adres URL zdalny)
- Wersją manifestu i wersją aplikacji źródłowej
- Trybem szyfrowania archiwum i odciskiem
- Trybem ładunku bazy danych
- Profilem runtime
- Statusem weryfikacji (NOT_RUN, PASSED, FAILED, SKIPPED_TIMEOUT)
- Statusem testu odtwarzania i statusem zgodności odtwarzania
Odtwarzanie
Użycie scripts/restore.sh
Kanoniczny skrypt odtwarzania obsługuje pełny cykl życia:
COMPOSE_PROJECT_NAME=quotenode \
COMPOSE_ENV_FILE=infra/.env.prod \
RESTORE_AGE_IDENTITY_FILE=/path/to/quotenode-age-identity.txt \
[email protected] \
RESTORE_ADMIN_PASSWORD_FILE=/path/to/password \
bash scripts/restore.sh /path/to/backup.tar.age
Skrypt:
- Wyodrębnia i waliduje archiwum (manifest, sumy kontrolne, bezpieczeństwo wpisów tar).
- Uruchamia kontrole wstępne (pojemność dysku, wersja PostgreSQL, łączność z bazą danych).
- Tworzy zabezpieczającą kopię docelową przed jakąkolwiek operacją destrukcyjną.
- Zapisuje dziennik odtwarzania na poziomie hosta (JSONL, przetrwa wymianę bazy danych).
- Odtwarza bazę danych.
- Przygotowuje pliki w katalogu tymczasowym i podmienia je na miejsce.
- Zachowuje tożsamość docelową (ID instancji, metadane szyfrowania kopii zapasowych, stan licencji).
- Czyści sesje, publiczne linki ofert i tokeny powiadomień.
- Resetuje lub wymusza zmianę dostępu administratora.
- Ponownie szyfruje dane PII, jeśli kopia jest typu
PII_DECRYPTED, a środowisko docelowe maENCRYPT_PII=true.
Odtwarzanie między instancjami
Podczas odtwarzania kopii zapasowej z innej instalacji domyślnym trybem jest PRESERVE_TARGET_IDENTITY:
- ID instancji docelowej, odbiorca age kopii zapasowych, metadane zestawu odzyskiwania oraz stan licencji są zachowywane.
- Sesje, publiczne linki ofert i tokeny powiadomień są czyszczone.
- Hasło administratora jest resetowane/wymuszana jest jego zmiana.
- Dane biznesowe są w pełni zastępowane (bez scalania).
Zgodność odtwarzania
Kontrola wstępna odtwarzania weryfikuje:
- Starsza kopia do nowszej aplikacji: dozwolone (zgodne).
- Nowsza kopia do starszej aplikacji: zablokowane.
- Brak manifestu: zablokowane.
- Niewłaściwy odcisk klucza szyfrowania dla ładunku
APP_ENCRYPTED: zablokowane przed pracą destrukcyjną. PII_DECRYPTEDbez szyfrowania archiwum: zablokowane.
Gwarancja dostępu administratora
Odtwarzanie gwarantuje dostęp administratora za pomocą jednego z:
RESTORE_ADMIN_PASSWORD_FILE— hasło dostarczone przez operatora (ustawiane zforcePasswordChange=true)RESTORE_ADMIN_PASSWORD— wyłącznie do użytku lokalnego/testowego- Automatycznie generowany jednorazowy token resetu — wypisywany na konsolę i do dziennika na poziomie hosta
Uruchomienie próbne (dry run)
RESTORE_DRY_RUN=true bash scripts/restore.sh /path/to/backup.tar.age
Waliduje archiwum, sprawdza sumy kontrolne i manifest oraz raportuje plan odtwarzania bez modyfikowania czegokolwiek.
Odzyskiwanie po awarii — odbudowa od zera
Twój serwer przepadł. Masz archiwum kopii zapasowej (.tar.age lub .tar) oraz nowy serwer z Dockerem. Oto jak przeprowadzić odtwarzanie.
Krok 1 — Przygotuj nowy serwer
mkdir quotenode && cd quotenode
Skonfiguruj docker-compose.yml i .env zgodnie z opisem w Przewodniku instalacji.
Krytyczne dla kopii APP_ENCRYPTED: Użyj tego samego
DB_ENCRYPTION_KEYco w oryginalnej instalacji. Bez niego zaszyfrowane dane PII będą trwale nieczytelne.
Krytyczne dla szyfrowanych archiwów: Potrzebujesz pliku prywatnej tożsamości z zestawu odzyskiwania (
quotenode-age-identity.txt), aby odszyfrować archiwum.
Krok 2 — Skopiuj kopię zapasową i skrypt odtwarzania
scp backup.tar.age user@new-server:~/quotenode/
scp scripts/restore.sh user@new-server:~/quotenode/
Krok 3 — Uruchom odtwarzanie
RESTORE_AGE_IDENTITY_FILE=./quotenode-age-identity.txt \
[email protected] \
RESTORE_ADMIN_PASSWORD_FILE=./admin-password \
./restore.sh --fresh-install backup.tar.age
Krok 4 — Zweryfikuj
Otwórz swoją domenę i zaloguj się. Wszyscy klienci, oferty, produkty i ustawienia powinny być dokładnie takie, jakie były w momencie tworzenia kopii zapasowej.
Retencja kopii zapasowych
- Lokalnie: zachowywanych jest
BACKUP_RETENTION_DAILY(domyślnie: 7) najnowszych udanych kopii zapasowych. - Zdalnie: skonfiguruj zasady cyklu życia u swojego dostawcy magazynu.
- Kopie z odszyfrowanymi danymi PII powinny mieć surowszą retencję ze względu na ich wrażliwy charakter.
Limit czasu kopii zapasowej
Każda kopia zapasowa ma konfigurowalny limit czasu (domyślnie: 30 minut przez BACKUP_TIMEOUT_MINUTES). W razie przekroczenia kopia jest przerywana i zapisywana jako FAILED.
Zalecana strategia
- Włącz codzienne automatyczne kopie zapasowe z szyfrowaniem
AGE_RECIPIENTi magazynem zdalnym. - Wygeneruj zestaw odzyskiwania podczas konfiguracji i przechowuj go w bezpiecznym miejscu offline.
- Zweryfikuj swój zestaw odzyskiwania względem rzeczywistej kopii zapasowej za pomocą dołączonych skryptów weryfikujących.
- Okresowo testuj procedury odtwarzania — kopia zapasowa, która nigdy nie została przetestowana, nie jest kopią zapasową.
- Pobierz kopię zapasową co najmniej raz w miesiącu do lokalizacji offline (wymaga autoryzacji step-up).
- Monitoruj status kopii zapasowych w panelu administracyjnym — ostrzeżenia pojawiają się dla nieaktualnych, nieudanych lub nieszyfrowanych kopii.
- Przechowuj co najmniej 7 dni historii kopii zapasowych (wartość domyślna).