Kopie zapasowe na serio
Po tej lekcji: kursant zna zasadę 3-2-1, rozróżnia kopię pełną, przyrostową i różnicową, robi kopie przez rsync, tar, borg i restic, a przede wszystkim umie z nich odtworzyć dane i sprawdzić, że odtworzenie działa.
Wstęp
„Macie kopię, prawda?”. Każdy admin ma kopię. Dopóki ktoś nie poprosi, żeby z niej coś przywrócić. Wtedy okazuje się, że skrypt od pół roku kończył się błędem, którego nikt nie czytał. Albo że kopia leżała na tym samym dysku, który właśnie padł. Albo że jest, ale zaszyfrowana hasłem, które znał tylko kolega, który odszedł w marcu.
Powiem to raz i będę powtarzał do końca kursu: nikogo nie obchodzi, czy robisz kopie. Obchodzi ich, czy umiesz odtworzyć dane. Dziś zbudujemy porządny system kopii, a potem zrobimy rzecz, której nie robi prawie nikt: sprawdzimy, czy z tej kopii da się coś odzyskać.
Zasada 3-2-1
Wyobraź sobie, że masz jedyne zdjęcia z wesela. Trzymasz je na laptopie. Laptop kradną. Dobra, nauczka, robisz kopię na dysk zewnętrzny. Trzymasz go… w torbie z laptopem. Kradną torbę. Kolejna nauczka, kopiujesz na drugi dysk i trzymasz w szufladzie w domu. Mieszkanie zalewa sąsiad z góry. Wszystko w jednym miejscu, wszystko mokre.
Z takich nauczek powstała zasada 3-2-1:
- 3 kopie danych, licząc oryginał,
- na 2 różnych nośnikach albo w dwóch różnych systemach,
- 1 kopia poza budynkiem.
W serwerowni wygląda to tak: dane na serwerze, kopia na serwerze kopii w tej samej firmie, druga kopia w innym mieście albo w chmurze.
Do tego dwie rzeczy, których zasada wprost nie mówi, a dziś są równie ważne. Po pierwsze: przynajmniej jedna kopia powinna być taka, której atakujący nie skasuje, nawet jak przejmie serwer. Program szyfrujący dane dla okupu najpierw szuka kopii. Jeśli Twój serwer może skasować kopie, to może je skasować też ten, kto go przejął. Po drugie: kopia jest warta tyle, ile ostatni udany test odtwarzania.
Pełna, przyrostowa, różnicowa
Trzy sposoby, jak często i ile kopiować. Wyobraź sobie zeszyt z notatkami ze studiów.
- Pełna kopia — kserujesz cały zeszyt. Bezpieczne, ale drogie i długie.
- Przyrostowa (incremental) — kserujesz tylko strony zapisane od ostatniego ksero, jakiegokolwiek. Szybkie i tanie. Ale żeby odtworzyć zeszyt, potrzebujesz pełnej i WSZYSTKICH przyrostów po kolei. Zgubisz jeden ze środka, łańcuch pęka.
- Różnicowa (differential) — kserujesz wszystko, co się zmieniło od ostatniej PEŁNEJ. Każdego dnia trochę więcej stron. Do odtworzenia wystarczą dwie rzeczy: pełna i ostatnia różnicowa.
Pokażę wszystkie trzy na zwykłym tar. Klucz to opcja --listed-incremental, czyli plik-notes, w którym tar zapisuje, co już widział.
mkdir -p dane kopie
for i in 1 2 3; do echo "Faktura nr $i" > dane/faktura$i.txt; done
head -c 5M /dev/urandom > dane/zdjecie.jpg
# niedziela: pełna
tar -czf kopie/pelna.tar.gz --listed-incremental=kopie/stan.snar dane
cp kopie/stan.snar kopie/stan-pelna.snar
# poniedziałek: nowa faktura, przyrost
echo "Faktura nr 4" > dane/faktura4.txt
tar -czf kopie/przyrost1.tar.gz --listed-incremental=kopie/stan.snar dane
# wtorek: kolejna, przyrost
echo "Faktura nr 5" > dane/faktura5.txt
tar -czf kopie/przyrost2.tar.gz --listed-incremental=kopie/stan.snar dane
# wtorek, wersja różnicowa: zawsze od notesu z pełnej
cp kopie/stan-pelna.snar kopie/stan-roz.snar
tar -czf kopie/roznicowa.tar.gz --listed-incremental=kopie/stan-roz.snar daneCo jest w środku:
ls -lh kopie/*.tar.gz
tar -tzf kopie/przyrost2.tar.gz
tar -tzf kopie/roznicowa.tar.gz-rw-r--r-- 1 root root 5.1M Nov 4 02:38 kopie/pelna.tar.gz
-rw-r--r-- 1 root root 225 Nov 4 02:38 kopie/przyrost1.tar.gz
-rw-r--r-- 1 root root 208 Nov 4 02:38 kopie/przyrost2.tar.gz
-rw-r--r-- 1 root root 238 Nov 4 02:38 kopie/roznicowa.tar.gz
dane/
dane/faktura5.txt
dane/
dane/faktura4.txt
dane/faktura5.txtPełna: 5 MB. Przyrosty: po dwieście bajtów. Wtorkowy przyrost ma tylko fakturę 5, różnicowa ma 4 i 5. Dokładnie jak w zeszycie.
I teraz najważniejsze, odtworzenie. Przy przyrostach rozpakowujesz wszystko po kolei, od pełnej:
mkdir odtw
for f in pelna przyrost1 przyrost2; do
tar -xzf kopie/$f.tar.gz --listed-incremental=/dev/null -C odtw
done
diff -r dane odtw/dane && echo IDENTYCZNEIDENTYCZNE--listed-incremental=/dev/null przy rozpakowaniu mówi tar-owi: „to są kawałki łańcucha, odtwarzaj także skasowania”. Jak ktoś między kopiami usunie plik, przyrost to zapamięta i przy odtwarzaniu plik też zniknie. diff -r porównuje dwa drzewa folderów i milczy, jeśli są identyczne. Milczenie diff-a to najpiękniejsza cisza w życiu admina.
rsync i migawki na twardych dowiązaniach
rsync znasz z modułu o sieci. Kopiuje tylko różnice i świetnie nadaje się do kopii na drugi serwer. Ale zwykły rsync -a --delete to lustro, nie kopia. Skasujesz plik w źródle, w nocy zniknie i w lustrze.
Jest sztuczka, która z rsync robi system migawek. Każdy dzień w osobnym folderze, ale niezmienione pliki nie zajmują miejsca drugi raz. To opcja --link-dest. Pamiętasz twarde dowiązania? Jeden plik, kilka nazw, jak jedno mieszkanie z dwoma tabliczkami na drzwiach.
rsync -a --delete dane/ migawki/2026-11-03/
echo "Faktura nr 6" > dane/faktura6.txt
rsync -a --delete --link-dest=../2026-11-03 dane/ migawki/2026-11-04/
du -sh migawki/*
ls -li migawki/*/zdjecie.jpg5.1M migawki/2026-11-03
8.0K migawki/2026-11-04
1484127 -rw-r--r-- 2 root root 5242880 Nov 4 02:38 migawki/2026-11-03/zdjecie.jpg
1484127 -rw-r--r-- 2 root root 5242880 Nov 4 02:38 migawki/2026-11-04/zdjecie.jpgDruga migawka zajmuje 8 KB, a jest w niej komplet. Ten sam numer i-węzła i licznik dowiązań 2: zdjęcie leży na dysku raz, ma dwie tabliczki. Ścieżka w --link-dest liczy się względem folderu docelowego, stąd ../. Zapamiętaj też ukośnik na końcu źródła: dane/ to „zawartość folderu”, dane to „folder razem z nazwą”.
Przed każdym --delete w nowym skrypcie: -n, czyli próba na sucho.
rm dane/faktura6.txt
rsync -an --delete -v dane/ migawki/2026-11-04/sending incremental file list
deleting faktura6.txtWolisz przeczytać „deleting” na ekranie niż w mailu od księgowej.
borg: kopie z deduplikacją i szyfrowaniem
tar i rsync są wszędzie i to ich siła. Ale zarządzanie przyrostami, szyfrowanie i usuwanie starych kopii musisz sklejać sam. Do tego są narzędzia z deduplikacją. Pierwsze to borg.
Deduplikacja to szatnia w teatrze. Sto osób przyszło w identycznej czarnej kurtce, a szatniarz wiesza jedną i daje sto numerków. Borg tnie pliki na kawałki, liczy z każdego odcisk palca i każdy kawałek zapisuje tylko raz, choćby występował w stu kopiach.
[Ubuntu] sudo apt install borgbackup. [RHEL] Z repozytorium EPEL: sudo dnf install borgbackup.
export BORG_PASSPHRASE='Dlugie-Haslo-Do-Kopii-2026'
borg init --encryption=repokey repo-borg
borg create --stats --compression zstd repo-borg::'{hostname}-{now:%Y-%m-%d_%H%M}' daneRepozytorium to magazyn, archiwum to jedna kopia w magazynie. {hostname} i {now} borg sam zamienia na nazwę serwera i datę. repokey znaczy, że klucz szyfrujący leży w repozytorium, zamknięty Twoim hasłem.
Dopisuję jedną fakturę i robię drugą kopię. Statystyki z drugiego razu:
Archive name: vm-2026-11-04_0240
Number of files: 8
Original size Compressed size Deduplicated size
This archive: 5.24 MB 5.24 MB 609 B
All archives: 10.49 MB 10.49 MB 5.25 MBDruga kopia ma komplet 5 MB danych, a dołożyła do magazynu 609 bajtów. Każde archiwum wygląda jak pełna kopia, a kosztuje jak przyrostowa. Najlepsze z obu światów. (Kompresja nic nie dała, bo moje „zdjęcie” to losowe bajty, a tych nie da się ścisnąć.)
borg list repo-borg
borg diff repo-borg::vm-2026-11-04_0239 vm-2026-11-04_0240vm-2026-11-04_0239 Wed, 2026-11-04 02:39:04 [45e67d1d...]
vm-2026-11-04_0240 Wed, 2026-11-04 02:40:05 [d9a19fff...]
added 13 B dane/faktura7.txtOdtwarzanie. borg extract rozpakowuje do BIEŻĄCEGO folderu, więc najpierw wchodzisz do pustego:
mkdir odtw-borg && cd odtw-borg
borg extract ../repo-borg::vm-2026-11-04_0240
cd .. && diff -r dane odtw-borg/dane && echo IDENTYCZNE
# jeden plik ze starszej kopii
borg extract ../repo-borg::vm-2026-11-04_0239 dane/faktura2.txtStare kopie usuwasz zasadą „ile dziennych, tygodniowych, miesięcznych”:
borg prune --list --keep-daily=7 --keep-weekly=4 --keep-monthly=6 repo-borg
borg compact repo-borg
borg check repo-borgprune wybiera, co skasować. W borgu 1.2 miejsce na dysku zwalnia dopiero compact, jak opróżnienie kosza. check sprawdza, czy magazyn jest cały.
Kopia po sieci to ta sama komenda, tylko repozytorium ma adres: ssh://kopie@backup01/~/repo. Borg łączy się przez SSH, kluczem, jak w module o sieci.
I teraz rzecz, za którą podziękujesz mi kiedyś o trzeciej w nocy:
borg key export repo-borg borg-klucz.txtKlucz i hasło trzymasz poza serwerem, na przykład w menedżerze haseł firmy. Borg sam to mówi przy init: „you will need both KEY AND PASSPHRASE to access this repo!”. Bez nich kopia jest najlepiej zabezpieczonym śmietnikiem na świecie.
restic: to samo, inaczej
Drugi popularny program to restic. Idea ta sama: deduplikacja, szyfrowanie zawsze włączone, migawki. Różnice: restic to jeden plik wykonywalny i umie pisać prosto do chmury, na przykład do S3, przez SFTP albo przez własny serwer REST. Borg do pracy po sieci potrzebuje borga po drugiej stronie.
[Ubuntu] sudo apt install restic. [RHEL] Według dokumentacji restica z EPEL: sudo dnf install epel-release, potem sudo dnf install restic. (Na samym RHEL-u EPEL włączasz instrukcją ze strony Fedory, na Rocky i Almie wystarczy epel-release.)
export RESTIC_REPOSITORY=/srv/repo-restic
export RESTIC_PASSWORD='Inne-Dlugie-Haslo-2026'
restic init
restic backup dane
echo "Faktura nr 8" > dane/faktura8.txt
restic backup dane
restic snapshotsusing parent snapshot a3bf25f8
Files: 1 new, 0 changed, 8 unmodified
Added to the repository: 3.854 KiB (1.279 KiB stored)
snapshot d2e4c2ad saved
ID Time Host Tags Paths
---------------------------------------------------------------
a3bf25f8 2026-11-04 02:40:34 vm .../dane
d2e4c2ad 2026-11-04 02:40:34 vm .../daneOdtwarzanie do osobnego folderu i jeden plik:
restic restore latest --target odtw-restic
restic restore latest --target odtw-r1 --include /dane/faktura8.txt
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
restic checklatest to najnowsza migawka. forget wybiera, co usunąć, a --prune od razu zwalnia miejsce. Zanim puścisz to na prawdziwym repozytorium, dodaj --dry-run:
keep 1 snapshots:
d2e4c2ad 2026-11-04 02:40:34 vm last snapshot .../dane
remove 1 snapshots:
a3bf25f8 2026-11-04 02:40:34 vm .../daneRepozytorium w chmurze albo na innym serwerze to tylko inny adres, na przykład sftp:kopie@backup01:/srv/restic. Dokumentacja restica mówi bez owijania: zgubisz hasło, dane przepadają. Restic powtarza to przy każdym init.
Co wybrać? Borg, jeśli masz własny serwer kopii z SSH. Restic, jeśli kopie mają lecieć do chmury. Oba lepsze niż „mam gdzieś skrypt z tarem, chyba działa”.
Test odtwarzania
Kopia, z której nikt nie próbował niczego odtworzyć, to kot Schrödingera. Dopóki nie otworzysz pudełka, jest jednocześnie dobra i do niczego. Test odtwarzania to otwarcie pudełka w spokojny wtorek, a nie w piątek o 16:52.
Minimum, raz w miesiącu i po każdej zmianie skryptu:
- Odtwórz do osobnego folderu albo na osobną maszynę wirtualną. Nigdy na żywe dane.
- Porównaj:
diff -rdla folderów,sha256sumdla ważnych plików. - Dla baz danych: wczytaj zrzut do pustej bazy i zrób
SELECT. Plik.sqlo rozmiarze zero też jest plikiem. - Zmierz czas. Jak odtworzenie 2 TB z chmury trwa dwa dni, szef powinien to wiedzieć teraz, a nie w trakcie awarii.
- Zapisz wynik: data, co odtwarzałeś, ile trwało, czy się udało.
restic restore latest --target /tmp/test-odtw
diff -r dane /tmp/test-odtw/dane && echo IDENTYCZNE
restic check --read-data-subset=5%--read-data-subset=5% każe resticowi przeczytać i sprawdzić naprawdę 5% danych w repozytorium, a nie tylko spis treści. Co miesiąc inne 5%.
Jak to działa pod spodem
tar --listed-incremental zapisuje w pliku .snar listę folderów z numerami urządzeń, i-węzłów i czasami zmian. Przy następnym przebiegu porównuje stan z notesem i pakuje tylko to, co nowe. Do archiwum dopisuje też listę zawartości każdego folderu, dzięki temu przy odtwarzaniu wie, co trzeba skasować.
rsync --link-dest dla każdego pliku sprawdza, czy w folderze porównawczym leży identyczny, czyli o tym samym rozmiarze, czasie i prawach. Jeśli tak, zamiast kopiować robi twarde dowiązanie, czyli dopisuje nową nazwę do istniejącego i-węzła.
Borg i restic tną pliki na kawałki o zmiennej długości. Granice kawałków wyznacza funkcja licząca skrót z „okienka” przesuwanego po danych, więc dopisanie bajtu na początku pliku nie przesuwa wszystkich kawałków. Każdy kawałek dostaje odcisk palca, czyli kryptograficzny skrót, i jeśli taki odcisk jest już w repozytorium, kawałek nie jest zapisywany drugi raz. Archiwum to w gruncie rzeczy lista odcisków palców. Dlatego kasowanie kopii nie jest proste: kawałek można usunąć dopiero wtedy, gdy nie wskazuje na niego żadne archiwum. Tym zajmuje się compact w borgu i prune w resticu.
Ściąga flag
| Komenda | Flaga | Co robi |
|---|---|---|
tar | --listed-incremental=PLIK | kopia przyrostowa z notesem stanu |
tar | --listed-incremental=/dev/null | przy rozpakowaniu: odtwarzaj łańcuch razem ze skasowaniami |
tar | -tzf | lista zawartości bez rozpakowania |
rsync | -a --delete | lustro z prawami i czasami, usuwa nadmiarowe |
rsync | --link-dest=DIR | niezmienione pliki jako twarde dowiązania |
rsync | -n | próba na sucho |
borg | init --encryption=repokey | nowe szyfrowane repozytorium |
borg | create --stats | nowe archiwum ze statystyką |
borg | list / diff / extract | lista, różnice, odtworzenie |
borg | prune --keep-daily=N + compact | usuwanie starych kopii i zwolnienie miejsca |
borg | key export | kopia klucza poza serwer |
restic | init / backup / snapshots | repozytorium, kopia, lista |
restic | restore latest --target DIR | odtworzenie do folderu |
restic | --include ŚCIEŻKA | odtworzenie wybranego pliku |
restic | forget --keep-daily N --prune | usuwanie starych migawek |
restic | check --read-data-subset=5% | sprawdzenie części danych |
⚠ Typowy błąd
Nocny backup borga z crona, codziennie o 2:00. Pewnego dnia danych jest więcej, kopia trwa ponad dobę i o 2:00 następnego dnia startuje druga. W skrzynce:
Failed to create/acquire the lock /srv/repo-borg/lock.exclusive (timeout).Kod wyjścia 2. Druga kopia się nie zrobiła, bo pierwsza trzyma kłódkę na repozytorium. Kłódka chroni magazyn przed dwoma piszącymi naraz i to dobrze. Źle jest, jak nikt nie czyta maili z crona i kopie nie robią się od tygodnia.
Naprawa: najpierw ps aux | grep borg. Jeśli borg nadal pracuje, czekaj. Jeśli nie pracuje, bo proces został zabity albo serwer się zrestartował w trakcie, to kłódka została po zmarłym. Wtedy i tylko wtedy: borg break-lock repo-borg. Na przyszłość: flock w skrypcie, jak w module o skryptach, sprawdzanie kodu wyjścia i alarm przez logger z poprzedniej lekcji, gdy kopia się nie udała. Kopia, która cicho nie działa, jest gorsza niż jej brak, bo daje fałszywe poczucie bezpieczeństwa.
Drugi klasyk z restica, po zmianie serwera albo hasła w zmiennej:
Fatal: wrong password or no key foundZłe hasło. Jeśli to prawdziwe zgubione hasło, a nie literówka, to nie ma tylnych drzwi. Dlatego hasło do kopii trzymasz w dwóch miejscach, a test odtwarzania robisz z hasła wyjętego z sejfu, nie z historii Basha.
✎ Zadanie
- Zrób repozytorium borga albo restica i kopię swojego folderu
/etc. - Zmień jeden plik w
/etc, na przykład dopisz komentarz do/etc/hosts, i zrób drugą kopię. Pokaż różnice między kopiami (borg diffalborestic diff). - Odtwórz
/etc/hostsz pierwszej kopii do folderu/tmp/testi porównaj z obecnym przezdiff. - Ustaw usuwanie starych kopii: 7 dziennych, 4 tygodniowe, i uruchom to na sucho.
Pokaż rozwiązanie
Kopia /etc borgiem. Ścieżkę podaję względną, z /, żeby w archiwum leżało ładne etc/hosts, a nie cała ścieżka:
export BORG_PASSPHRASE='Haslo-Do-Kopii-Etc-2026'
sudo -E borg init -e repokey /root/repo
cd /
sudo -E borg create --stats /root/repo::etc-1 etc
echo '# serwer kopii: backup01' | sudo tee -a /etc/hosts
sudo -E borg create --stats /root/repo::etc-2 etc
sudo -E borg diff /root/repo::etc-1 etc-2Number of files: 1860
This archive: 8.95 MB 2.82 MB 2.72 MB
...
This archive: 8.95 MB 2.82 MB 825 B
+106 B -81 B [mtime: ...] etc/hostsDrugi raz cały /etc, prawie 9 MB, a w magazynie przybyło 825 bajtów. sudo -E przekazuje zmienną z hasłem do sudo. Odtworzenie starej wersji do /tmp/test i porównanie:
mkdir -p /tmp/test && cd /tmp/test
sudo -E borg extract /root/repo::etc-1 etc/hosts
diff /tmp/test/etc/hosts /etc/hosts4a5
> # serwer kopii: backup01Różnica to dokładnie moja linijka. Na koniec sprzątanie na sucho:
sudo -E borg prune --dry-run --list --keep-daily=7 --keep-weekly=4 /root/repoNa razie obie kopie zostają, bo mamy ich mniej, niż pozwala reguła. Wersja z resticiem wygląda tak samo: restic backup /etc, restic diff ID1 ID2, restic restore ID1 --target /tmp/test --include /etc/hosts, restic forget --keep-daily 7 --keep-weekly 4 --dry-run.
Co dalej
Kopie masz. A co, jeśli serwer w ogóle nie chce wstać? W następnej lekcji tryb ratunkowy i awaryjny, edycja wpisu w GRUB-ie, reset zapomnianego hasła roota na Red Hacie z rd.break, fsck przy starcie, kernel panic i jedna literówka w /etc/fstab, która potrafi położyć cały serwer.