Przejdź do treści
Q
QuoteNode

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:

  1. Zrzut bazy danych — pełny zrzut PostgreSQL w formacie niestandardowym (pg_dump --format=custom), obejmujący wszystkie tabele, sekwencje i ograniczenia.
  2. Magazyn plików — wszystkie przesłane pliki (zdjęcia produktów, logo firm) oraz wygenerowane pliki PDF, zarchiwizowane jako files.tar.gz ze ścieżkami względnymi.
  3. Manifest kopii zapasowej — plik backup-manifest.json zawierający wersję źródłową, tryb szyfrowania, tryb ładunku bazy danych, sumy kontrolne oraz metadane zgodności odtwarzania.
  4. Sumy kontrolne integralności — plik checksums.sha256 zawierają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=NONE
  • BACKUP_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. Kombinacja NONE + PII_DECRYPTED jest odrzucana.

Tryby ładunku bazy danych

TrybOpis
APP_ENCRYPTEDDomyślny. Pola PII pozostają zaszyfrowane kluczem aplikacji DB_ENCRYPTION_KEY. Odtwarzanie wymaga tego samego klucza szyfrowania lub pasującego odcisku klucza.
PII_DECRYPTEDPola 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 odcisk
  • quotenode-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:

  1. Sprawdza sumę kontrolną SHA-256 szyfrowanego archiwum względem oczekiwanej wartości lub pliku pomocniczego .sha256.
  2. Odszyfrowuje za pomocą age -d do prywatnego katalogu tymczasowego.
  3. Waliduje wpisy tar (odrzuca ścieżki bezwzględne, .., dowiązania symboliczne, pliki urządzeń).
  4. Sprawdza, czy backup-manifest.json istnieje i ma obsługiwaną wersję.
  5. Uruchamia sha256sum -c checksums.sha256.
  6. Uruchamia pg_restore --list db.dump, gdy pg_restore jest dostępny.
  7. 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_dump w 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.

TopologiaKiedy 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 procesieLaptop / 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_MODE decyduje, który kontener faktycznie uruchamia zaplanowane zadanie kopii zapasowej.
  • BACKUP_RUNTIME_PROFILE to etykieta zapisywana w logach i manifestach kopii zapasowych, dzięki której możesz później sprawdzić, jak każda kopia została utworzona.
TopologiaBackend webBackup worker
Dedykowany worker (domyślnie)JOBS_MODE=web + BACKUP_RUNTIME_PROFILE=WORKERkontener JOBS_MODE=backup-only
W procesieJOBS_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.

ProfilOpis
WORKERDomyślny. Stały kontener backup-worker uruchamia zaplanowane kopie zapasowe przez cron.
WEB_MAINTENANCEBackend web wykonuje zaplanowane kopie zapasowe bezpośrednio. Nie jest potrzebny dodatkowy kontener workera.
ONE_SHOTZewnętrzny cron lub operator uruchamia scripts/backup-one-shot.sh i kończy działanie. Brak stałego workera.
OFFLINE_MAINTENANCEZautomatyzowany 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:

  1. Ogłasza zaplanowaną konserwację (konfigurowalny okres karencji).
  2. Opcjonalnie włącza statyczną stronę konserwacji na czas wyłączenia usług.
  3. Zatrzymuje frontend, backend i backup-worker (utrzymuje działanie PostgreSQL).
  4. Uruchamia kopię jednorazową.
  5. Restartuje wszystkie usługi.
  6. Weryfikuje stan zdrowia backendu.
  7. 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 przez BACKUP_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):

  1. Przejdź do Ustawienia > Backup.
  2. Kliknij Pobierz przy wpisie kopii zapasowej.
  3. Wprowadź swoje bieżące hasło (oraz kod TOTP, jeśli włączono MFA).
  4. System wydaje krótkotrwałe, jednorazowe pozwolenie na pobranie (domyślnie: 120 sekund).
  5. 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:

  1. Wyodrębnia i waliduje archiwum (manifest, sumy kontrolne, bezpieczeństwo wpisów tar).
  2. Uruchamia kontrole wstępne (pojemność dysku, wersja PostgreSQL, łączność z bazą danych).
  3. Tworzy zabezpieczającą kopię docelową przed jakąkolwiek operacją destrukcyjną.
  4. Zapisuje dziennik odtwarzania na poziomie hosta (JSONL, przetrwa wymianę bazy danych).
  5. Odtwarza bazę danych.
  6. Przygotowuje pliki w katalogu tymczasowym i podmienia je na miejsce.
  7. Zachowuje tożsamość docelową (ID instancji, metadane szyfrowania kopii zapasowych, stan licencji).
  8. Czyści sesje, publiczne linki ofert i tokeny powiadomień.
  9. Resetuje lub wymusza zmianę dostępu administratora.
  10. Ponownie szyfruje dane PII, jeśli kopia jest typu PII_DECRYPTED, a środowisko docelowe ma ENCRYPT_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_DECRYPTED bez 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 z forcePasswordChange=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_KEY co 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

  1. Włącz codzienne automatyczne kopie zapasowe z szyfrowaniem AGE_RECIPIENT i magazynem zdalnym.
  2. Wygeneruj zestaw odzyskiwania podczas konfiguracji i przechowuj go w bezpiecznym miejscu offline.
  3. Zweryfikuj swój zestaw odzyskiwania względem rzeczywistej kopii zapasowej za pomocą dołączonych skryptów weryfikujących.
  4. Okresowo testuj procedury odtwarzania — kopia zapasowa, która nigdy nie została przetestowana, nie jest kopią zapasową.
  5. Pobierz kopię zapasową co najmniej raz w miesiącu do lokalizacji offline (wymaga autoryzacji step-up).
  6. Monitoruj status kopii zapasowych w panelu administracyjnym — ostrzeżenia pojawiają się dla nieaktualnych, nieudanych lub nieszyfrowanych kopii.
  7. Przechowuj co najmniej 7 dni historii kopii zapasowych (wartość domyślna).

Last reviewed: Recently