SSD Nodes Learn 🎉 VPS od $5.50/mies.
Przewodniki Matt ConnorAutor: Matt Connor

Jak bezpiecznie przenieść serwer na nowy VPS

Skuteczna migracja serwera Linux krok po kroku. Dowiedz się, jak uniknąć przestojów poprzez konfigurację od zera, synchronizację danych i bezpieczne przełączenie rekordów DNS.

Migracja serwera na nowy VPS jako zaplanowane przełączenie

Aby przeprowadzić migrację serwera na nowy VPS, należy potraktować przeniesienie jako zaplanowane przełączenie, a nie zwykłe kopiowanie danych. Nową maszynę należy skonfigurować od podstaw, zsynchronizować dane dwukrotnie i zweryfikować jej poprawne działanie na przypisanym adresie IP przed jakąkolwiek zmianą w DNS. Dopiero po potwierdzeniu gotowości należy zmienić rekordy i pozostawić stary serwer uruchomiony do czasu uzyskania pewności co do stabilności nowego środowiska. Kopiowanie bajtów jest najprostszą częścią procesu. Kolejność operacji decyduje o tym, czy migracja przebiegnie bezproblemowo, czy wygeneruje koszty.

Niniejszy przewodnik dotyczy jednego serwera Linux obsługującego aplikację webową, bazę danych oraz certyfikat TLS (transport layer security). Scenariusz ten obejmuje większość konfiguracji typu single-server. W procesie biorą udział dwa hosty, dlatego w każdym przykładzie w komentarzu wskazano, na którym hoście należy wykonać polecenie. Adresy pochodzą z zakresów dokumentacyjnych: 198.51.100.10 to stary serwer, a 203.0.113.20 to nowy.

Przed rozpoczęciem należy zapoznać się z całą procedurą. Pierwszy krok, polegający na obniżeniu wartości TTL w rekordach DNS, musi zostać wykonany na kilka dni przed właściwą migracją.

Sporządź inwentaryzację przed przystąpieniem do budowy

Nie można odtworzyć serwera, którego konfiguracja nie została opisana. Poświęć godzinę na spisanie zadań wykonywanych przez stary serwer, ponieważ po migracji zawsze psuje się to, o czym nikt nie pamiętał: zadanie cron, wyjątek w zaporze sieciowej lub plik środowiskowy znajdujący się poza katalogiem aplikacji.

Uruchom poniższe polecenia na starym serwerze i przechowuj dane wyjściowe w miejscu dostępnym z poziomu nowej maszyny.

# old server: packages you asked for, not the dependencies they pulled in
apt-mark showmanual > ~/inv-packages.txt

# old server: what runs now, and what starts at boot
systemctl list-units --type=service --state=running --no-pager > ~/inv-services.txt
systemctl list-unit-files --state=enabled --no-pager >> ~/inv-services.txt
systemctl list-timers --all --no-pager > ~/inv-timers.txt

# old server: what is listening, on which address and port
sudo ss -tulpn > ~/inv-ports.txt

# old server: human accounts, skipping the system ones
awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $3, $6, $7}' /etc/passwd

apt-mark showmanual to lista, którą warto posiadać, ponieważ pomija ona wszystkie pakiety zainstalowane jako zależności. Pełne polecenie dpkg --get-selections na pięcioletnim serwerze zwróci dwa tysiące linii i nie dostarczy żadnych informacji o przeznaczeniu systemu.

Zaplanowane zadania ukrywają się w dwóch miejscach, dlatego sprawdź oba. Zadanie uruchamiane tylko raz w miesiącu to takie, które odkryjesz sześć tygodni po migracji.

# old server: per-user crontabs, then the system drop-ins
for u in $(cut -d: -f1 /etc/passwd); do sudo crontab -lu "$u" 2>/dev/null | sed "s/^/$u: /"; done
sudo ls -la /etc/cron.d /etc/cron.daily /etc/cron.hourly

Następnie sprawdź elementy, które nie są zwykłymi plikami: reguły zapory sieciowej, certyfikaty, bazy danych oraz rzeczywistą ilość danych do przeniesienia.

# old server
sudo ufw status verbose        # or: sudo nft list ruleset
sudo certbot certificates
sudo -u postgres psql -c '\l'  # or: sudo mysql -e 'SHOW DATABASES;'
sudo du -xh --max-depth=1 / | sort -h

certbot certificates wyświetla nazwę każdego certyfikatu, obsługiwane domeny, datę wygaśnięcia oraz ścieżki plików na dysku. Dane wyjściowe tego polecenia stanowią listę kontrolną TLS. du -x działa w obrębie jednego systemu plików, więc nie przeszuka zamontowanego wolumenu kopii zapasowej i nie poda zawyżonego wyniku.

Dwa elementy znajdują się poza serwerem i są za każdym razem pomijane. Po pierwsze, każda zewnętrzna usługa, która korzysta z listy dozwolonych adresów IP (allowlist) Twojego serwera: bramka płatności, zarządzana baza danych, przekaźnik SMTP lub API partnera. Nowa maszyna otrzyma nowy adres, więc przed przełączeniem usług należy dodać nowy adres IP do tych list. Po drugie, rekordy DNS, których nie utworzyłeś samodzielnie, takie jak rekord MX lub rekord SPF zawierający stary adres IP w polu tekstowym.

Dlaczego warto przeprowadzić ponowną instalację zamiast klonowania starego systemu plików root

Klonowanie całego systemu plików root na nowy VPS wydaje się szybsze i takie jest, dopóki nie pojawią się problemy. System plików root, który pracował w środowisku produkcyjnym przez lata, zawiera ręcznie edytowane konfiguracje, których nikt nie udokumentował, pakiety z repozytoriów, które już nie istnieją, oraz konfigurację rozruchu dostosowaną do wirtualnego sprzętu starej platformy. Importując wszystko, przenosisz również przyczyny, dla których zdecydowano się na migrację.

Ponowna instalacja jest wolniejsza pierwszego dnia, ale tańsza w każdym kolejnym. Instalujesz aktualne wydanie, stosujesz podstawowe zabezpieczenia, a następnie kopiujesz tylko dane: katalog aplikacji, konfiguracje witryn, zrzut bazy danych, certyfikaty oraz pliki użytkowników. Wszystko, czego nie potrafisz wyjaśnić, nie powinno zostać przeniesione. Skonfiguruj nową maszynę tak, jak każdą inną, korzystając z pierwszych dziesięciu minut na nowym VPS, a następnie dodawaj usługi z inwentarza jedna po drugiej, weryfikując poprawność działania każdej z nich przed dodaniem kolejnej.

Kiedy przywrócenie obrazu lub migawki jest właściwym rozwiązaniem

Istnieje jeden uzasadniony wyjątek od zasady przebudowywania serwera. Jeśli stary serwer nie uruchamia się lub aplikacja jest na tyle stara, że nikt nie potrafi już zbudować jej ze źródeł, przywrócenie obrazu dostawcy lub migawki jest pragmatycznym rozwiązaniem. Ma ono jednak istotne ograniczenia: działa w obrębie jednego dostawcy, często tylko w ramach jednej rodziny planów, ponieważ przywrócony dysk oczekuje specyficznych dla danej platformy urządzeń wirtualnych i nazewnictwa sieciowego.

Migawka działającego serwera niesie ze sobą ten sam problem spójności, co każda inna kopia plików działającej bazy danych. Przywracanie obrazu należy traktować jako ścieżkę odzyskiwania danych, a nie plan migracji. Przed oparciem na nim strategii operacyjnej należy przeczytać dlaczego migawka nie jest tym samym co kopia zapasowa.

Przepływ plików: rsync przez SSH

Uruchom rsync ze starego serwera, wypychając dane na nowy. Wypychanie jest zazwyczaj prostsze, ponieważ stary serwer posiada już dane i może odczytać je wszystkie jako sudo.

# old server: dry run first, and read what it says it will do
sudo rsync -aHAX --dry-run --itemize-changes \
  -e 'ssh -i /root/.ssh/id_ed25519' \
  /srv/app/ deploy@203.0.113.20:/srv/app/

# old server: the real bulk pass
sudo rsync -aHAX --info=progress2 \
  -e 'ssh -i /root/.ssh/id_ed25519' \
  /srv/app/ deploy@203.0.113.20:/srv/app/

Flagi mają znaczenie. -a zachowuje uprawnienia, znaczniki czasu, dowiązania symboliczne oraz właściciela. -H zachowuje dowiązania twarde jako dowiązania twarde, zamiast rozwijać je do osobnych kopii. -A kopiuje listy kontroli dostępu POSIX (ACL), a -X kopiuje rozszerzone atrybuty. Bez tych dwóch ostatnich, plik wyglądający na identyczny może zachowywać się inaczej, ponieważ etykiety SELinux oraz listy ACL znajdują się w rozszerzonych atrybutach i nic innego ich nie rejestruje.

Dwa szczegóły powodują większość awarii w tym procesie.

Końcowy ukośnik decyduje, gdzie trafią dane. /srv/app/ oznacza zawartość tego katalogu. /srv/app oznacza sam katalog. Błąd w tym miejscu spowoduje powstanie /srv/app/app na nowym serwerze, a aplikacja po uruchomieniu zgłosi brak plików, ponieważ ścieżki, z którymi została skonfigurowana, znajdują się teraz o jeden poziom za płytko.

W sudo tylda oznacza katalog domowy użytkownika root. Zapisanie -e 'ssh -i ~/.ssh/id_ed25519' wewnątrz sudo rsync powoduje szukanie klucza w /root/.ssh, a nie w Twoim własnym katalogu domowym. Jeśli klucza tam nie ma, SSH wyświetli Permission denied (publickey), rsync wyświetli rsync: connection unexpectedly closed i zakończy działanie z kodem błędu. Zapisz pełną ścieżkę do klucza. Jeśli komunikat o uwierzytelnieniu nadal się pojawia po poprawieniu ścieżki, błąd publickey ma krótką listę przyczyn, a uprawnienia do katalogów na nowym serwerze są kolejną rzeczą do sprawdzenia.

Własność plików wymaga podjęcia decyzji. Uruchomiony jako root, rsync domyślnie mapuje właściciela i grupę według nazwy, więc plik należący do www-data na starym serwerze stanie się własnością www-data na nowym, nawet jeśli numeryczny identyfikator UID (user ID) jest inny. Jest to pożądane zachowanie przy przebudowie systemu. Dodaj --numeric-ids tylko wtedy, gdy kopiujesz system plików, którego konta nie istnieją na serwerze docelowym, a następnie sprawdź wynik za pomocą ls -ln, ponieważ plik należący do UID bez przypisanego konta wyświetla się jako sama liczba, a każda usługa próbująca go odczytać otrzyma odmowę dostępu.

Uruchom kopiowanie głównej części danych z kilkudniowym wyprzedzeniem, gdy stary serwer nadal obsługuje ruch. Powtarzaj tę operację dowolną liczbę razy: rsync przesyła tylko to, co uległo zmianie, więc drugie przejście zajmuje minuty zamiast godzin. Ostatnie przejście, wykonane w oknie serwisowym, dodaje --delete, dzięki czemu pliki usunięte na starym serwerze znikną również na nowym.

# old server: final pass, inside the window, after the app has stopped writing
sudo rsync -aHAX --delete --info=progress2 \
  -e 'ssh -i /root/.ssh/id_ed25519' \
  /srv/app/ deploy@203.0.113.20:/srv/app/

--delete usuwa pliki w lokalizacji docelowej, których nie ma już w źródle, więc błędna ścieżka źródłowa w połączeniu z --delete opróżni katalog docelowy. Zawsze uruchamiaj to najpierw z --dry-run. Długie transfery przerywają się również w przypadku zerwania sesji SSH na Twoim laptopie, dlatego uruchamiaj je wewnątrz tmux lub screen na starym serwerze. Dodaj --bwlimit=20M, jeśli kopiowanie wysyca łącze, podczas gdy stary serwer nadal obsługuje użytkowników.

Migracja bazy danych: natywny zrzut

Baza danych nie jest zwykłym katalogiem plików, mimo że tak wygląda. To zestaw plików, stan w pamięci operacyjnej oraz dziennik transakcji (write-ahead log), spójny wyłącznie w momentach określonych przez sam silnik bazy. Należy używać narzędzi dostarczonych przez producenta.

PostgreSQL wymaga dwóch zrzutów, ponieważ role są współdzielone w całym klastrze, a pg_dump ich nie uwzględnia:

# old server
sudo -u postgres pg_dumpall --globals-only -f /var/backups/globals.sql
sudo -u postgres pg_dump -Fc -f /var/backups/appdb.dump appdb
# new server: restore in this order
sudo -u postgres psql -f /var/backups/globals.sql
sudo -u postgres createdb -O appuser appdb
sudo -u postgres pg_restore -d appdb --no-owner /var/backups/appdb.dump

Pominięcie globals.sql spowoduje odtworzenie wszystkich tabel, ale żadna rola aplikacji nie będzie mogła ich odczytać, ponieważ instrukcje GRANT odwołują się do nieistniejącego użytkownika. -Fc zapisuje dane w niestandardowym formacie archiwum, który odczytuje wyłącznie pg_restore i który pozwala na późniejsze przywrócenie wybranych tabel. Przywracanie danych powinno odbywać się w tej samej lub nowszej wersji głównej. Przejście wsteczne, na przykład z 17 na 16, nie jest wspierane, a pg_restore odrzuci archiwum z błędem nieobsługiwanej wersji w nagłówku pliku przed rozpoczęciem zapisu.

MySQL oraz MariaDB używają jednego polecenia z czterema opcjami, które nie są domyślnie aktywne:

# old server
sudo mysqldump --single-transaction --routines --triggers --events \
  --databases appdb > /var/backups/appdb.sql
# new server
sudo mysql < /var/backups/appdb.sql

--single-transaction wykonuje spójną migawkę bez blokowania operacji zapisu, ale tylko dla tabel InnoDB. Tabela MyISAM w tej samej bazie danych jest kopiowana bez takiej gwarancji, dlatego przed zaufaniem zrzutowi należy sprawdzić silniki pamięci masowej. Opcje --routines, --triggers oraz --events są domyślnie wyłączone, co oznacza, że zwykły zrzut przywraca dane, ale pomija procedury składowane oraz zaplanowane zdarzenia. Użytkownicy bazy danych i ich uprawnienia znajdują się w systemowej bazie mysql, której zrzut --databases appdb nie obejmuje, dlatego należy odtworzyć je na nowym serwerze za pomocą CREATE USER oraz GRANT. MariaDB 11 dostarcza to samo narzędzie co mariadb-dump i utrzymuje mysqldump jako dowiązanie symboliczne, więc od sierpnia 2026 obie nazwy działają poprawnie.

SQLite to pojedynczy plik, a jego kopiowanie w trakcie zapisu przez aplikację prowadzi do powstania uszkodzonego pliku. Istnieje bezpieczna metoda wykonania kopii:

# old server
sqlite3 /var/lib/app/app.db ".backup '/var/backups/app.db'"

Niezależnie od silnika, należy zweryfikować zrzut przed jego użyciem. Zrzut, który został przerwany z powodu braku miejsca na dysku, zostanie przywrócony bez zgłaszania błędów, aż do momentu, w którym plik został ucięty.

# old server: a complete mysqldump ends with a line reading "-- Dump completed on ..."
tail -n 3 /var/backups/appdb.sql

# new server: after restore, count rows in a table whose size you know
sudo mysql -e 'SELECT COUNT(*) FROM appdb.orders;'

Dlaczego nie należy używać rsync do kopiowania działającej bazy danych

Narzędzie rsync kopiuje pliki pojedynczo. Działająca baza danych zapisuje dane do wielu plików jednocześnie, więc w momencie, gdy rsync dociera do ostatniego pliku, pierwszy jest już nieaktualny. Kopia zawiera strony danych pochodzące z różnych momentów czasowych, co stanowi stan, w którym baza nigdy się nie znajdowała. Skutkiem jest albo serwer, który odmawia uruchomienia, albo gorszy scenariusz: serwer uruchamia się, przez tydzień zwraca poprawne wyniki, a następnie ulega awarii, gdy zapytanie trafi na uszkodzoną stronę danych. W międzyczasie nie pojawiają się żadne ostrzeżenia.

Istnieją dwa bezpieczne sposoby przenoszenia plików bazy danych. Pierwszy to zatrzymanie bazy, skopiowanie plików i ponowne uruchomienie: jest to metoda poprawna, prosta, ale wiąże się z przestojem trwającym tyle, co proces kopiowania. Drugi sposób to użycie narzędzia przeznaczonego do tworzenia fizycznej kopii działającego serwera. W przypadku PostgreSQL jest to pg_basebackup, które współpracuje z serwerem, zapewniając spójność kopii:

# new server: pull a physical copy from the old one
pg_basebackup -h 198.51.100.10 -U replicator -D /var/lib/postgresql/16/main -X stream -P

Wymaga to roli z atrybutem REPLICATION oraz odpowiedniego wpisu pg_hba.conf na starym serwerze, co wiąże się z większym nakładem pracy konfiguracyjnej niż w przypadku zrzutu (dump). Metoda ta jest opłacalna, gdy baza danych jest na tyle duża, że czas wykonania zrzutu i odtworzenia danych przekracza dopuszczalne okno serwisowe. W przypadku typowej migracji pojedynczego serwera, lepszym rozwiązaniem jest zrzut danych.

Przebuduj certyfikaty przed przełączeniem, a nie po nim

Certyfikat TLS jest powiązany z nazwą domeny, a nie z adresem IP, dlatego sam plik certyfikatu można przenieść bez problemów. Problematyczne jest natomiast odnawianie. Domyślne wyzwanie HTTP-01 narzędzia Certbot wymaga, aby urząd certyfikacji pobrał plik przez port 80 dla certyfikowanej nazwy. Dopóki rekordy DNS nie wskazują na nowy serwer, żądanie trafia na stary, co powoduje niepowodzenie odnowienia na nowym serwerze.

Pierwszą opcją jest skopiowanie istniejących certyfikatów wraz z ich stanem odnowienia. Pozostają one ważne do daty wygaśnięcia, niezależnie od tego, na którym serwerze się znajdują.

# old server
sudo rsync -aHAX -e 'ssh -i /root/.ssh/id_ed25519' \
  /etc/letsencrypt/ root@203.0.113.20:/etc/letsencrypt/

Każdy plik w /etc/letsencrypt/renewal/ wskazuje wtyczkę uwierzytelniającą, która wydała certyfikat, dlatego należy zainstalować tę samą wtyczkę na nowym serwerze (na przykład python3-certbot-nginx), w przeciwnym razie pierwsze odnowienie zakończy się błędem informującym o nieznanym mechanizmie uwierzytelniania. Zweryfikuj działanie odnowienia, zanim stanie się ono niezbędne:

# new server, after DNS has moved
sudo certbot renew --dry-run

Drugą opcją jest wydanie nowego certyfikatu na nowym serwerze przy użyciu wyzwania DNS-01, które potwierdza kontrolę nad domeną poprzez rekord TXT i nie wymaga dostępu do portu 80. Metoda ta działa przed migracją, gdy nazwa wciąż wskazuje na stary serwer, co czyni ją czystszym rozwiązaniem, jeśli możliwe jest zautomatyzowanie obsługi dostawcy DNS. Wydawanie certyfikatów przy użyciu wyzwania DNS-01 zawiera informacje o konfiguracji wtyczek i danych uwierzytelniających.

Niezależnie od wybranej metody, sprawdź, co faktycznie prezentuje nowy serwer, bez zmiany rekordów DNS:

# your laptop
echo | openssl s_client -connect 203.0.113.20:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -subject -dates -issuer

-servername wysyła SNI (Server Name Indication), co pozwala serwerowi WWW wybrać odpowiedni wirtualny host. Pominięcie tego parametru spowoduje otrzymanie domyślnego certyfikatu dla danego adresu IP, co wywoła błąd niezgodności, który wygląda na poważny problem, choć nim nie jest.

Obniżenie wartości TTL DNS na kilka dni przed migracją

DNS to miejsce, w którym nawet starannie zaplanowana migracja może zawieść, ponieważ opóźnienie jest wpisane w protokół i nie można go skrócić w dniu przełączenia. Resolver, który zbuforował rekord A, będzie go serwował przez czas trwania TTL (time to live), który otrzymał. Obniżenie TTL w tej chwili nie wpłynie na resolver, który pobrał rekord dziesięć minut temu z poprzednią wartością: będzie on przechowywał stary rekord przez resztę czasu starego TTL i dopiero wtedy pobierze nową, krótszą wartość. Dlatego należy obniżyć TTL z wyprzedzeniem wynoszącym co najmniej jeden pełny cykl starego TTL przed planowaną migracją. Jeden dzień przed jest bezpiecznym rozwiązaniem. Jeśli te zagadnienia są nowe, przewodnik po rekordach, resolverach i buforowaniu stanowi niezbędne wprowadzenie.

Poniższe liczby wynikają z obliczeń matematycznych opartych na wartości TTL, a nie z pomiarów.

ChartHow long a stale IP can survive, by record TTL
The data behind this chart
[
  {
    "label": "TTL 3600 s (common default)",
    "ttl_seconds": 3600,
    "worst_case_stale_minutes": 60
  },
  {
    "label": "TTL 900 s",
    "ttl_seconds": 900,
    "worst_case_stale_minutes": 15
  },
  {
    "label": "TTL 300 s (lowered for cutover)",
    "ttl_seconds": 300,
    "worst_case_stale_minutes": 5
  },
  {
    "label": "TTL 60 s (short window)",
    "ttl_seconds": 60,
    "worst_case_stale_minutes": 1
  }
]

Rekord opublikowany z TTL wynoszącym 3600 sekund może kierować użytkowników na stary adres IP przez 60 minut po wprowadzeniu zmiany. Po obniżeniu wartości do 300 sekund, ten najgorszy scenariusz skraca się do 5 minut. Należy traktować te wartości jako minimum, a nie gwarancję. Niektóre resolvory stosują własne minimalne wartości TTL i ignorują krótsze czasy, a niektóre środowiska uruchomieniowe aplikacji buforują rozwiązany adres przez cały czas działania procesu. Klient, który uruchomił się przed zmianą, może nigdy nie sprawdzić rekordu ponownie, dopóki nie zostanie zrestartowany.

Podczas sprawdzania, czy niższy TTL jest aktywny, należy odczytać odpowiedź autorytatywną, a nie własną pamięć podręczną:

# your laptop: ask the zone's own nameserver, so no cache is involved
dig +short NS example.com
dig +noall +answer @$(dig +short NS example.com | head -n1) example.com A

Drugie pole w tej linii odpowiedzi to TTL wyrażony w sekundach. Następnie należy sprawdzić rekordy, o których często się zapomina: rekord AAAA, jeśli stary serwer posiadał IPv6, nazwę www, gdy jest ona osobnym rekordem A, a nie CNAME, każdy rekord MX wskazujący na serwer, rekord SPF zawierający stary adres IP oraz rekord odwrotnego DNS (PTR) dla nowego adresu. Jeśli serwer wysyła pocztę, rekord PTR należy ustawić w panelu sterowania dostawcy przed migracją, ponieważ serwery pocztowe odbiorców sprawdzają go, a brak rekordu PTR skutkuje odrzuceniem wiadomości wiele godzin po tym, jak wszystko inne wydawało się działać poprawnie.

Weryfikacja nowego serwera po adresie IP przed zmianą DNS

Można przetestować całą aplikację na nowym serwerze, podczas gdy rekordy DNS nadal wskazują na stary. Należy nadpisać rozpoznawanie nazwy dla pojedynczego żądania:

# your laptop: force one name to the new IP, for this request only
curl -sS --resolve example.com:443:203.0.113.20 \
  -o /dev/null -w '%{http_code} %{ssl_verify_result}\n' https://example.com/

--resolve zmienia jedynie cel połączenia. Certyfikat TLS jest nadal weryfikowany względem rzeczywistej nazwy, co pozwala sprawdzić zarówno poprawność certyfikatu, jak i samej usługi. %{ssl_verify_result} wyświetla 0, gdy łańcuch certyfikatów zostanie zweryfikowany.

Aby przeglądać witrynę w przeglądarce, należy nadpisać nazwę dla całego systemu operacyjnego, dodając jeden wiersz do pliku /etc/hosts na laptopie lub do C:\Windows\System32\drivers\etc\hosts w systemie Windows:

203.0.113.20 example.com www.example.com

Następnie należy przejść przez aplikację w sposób, w jaki zrobiłby to użytkownik. Zalogować się. Załadować stronę odczytującą dane z bazy. Przesłać formularz, który zapisuje dane. Przesłać plik i potwierdzić jego zapis na dysku. Wywołać funkcję wysyłającą e-mail i sprawdzić, czy dotarł, ponieważ wychodzący ruch SMTP z nowego adresu IP często bywa problematyczny. Po zakończeniu testów należy niezwłocznie usunąć wpis z pliku hosts. Pozostawienie go skutkuje wielogodzinnym debugowaniem witryny, która dla wszystkich innych użytkowników działa poprawnie.

Procedura przełączenia krok po kroku

  1. Kilka dni wcześniej: zmniejsz wartość TTL, wykonaj wstępną synchronizację rsync, przygotuj nowy serwer i przetestuj go z użyciem nadpisania w pliku hosts.
  2. W dniu przełączenia, przed oknem serwisowym: dodaj nowy adres IP do wszystkich list dozwolonych (allowlist) podmiotów zewnętrznych i upewnij się, że zadanie kopii zapasowej na nowym serwerze jest skonfigurowane i wskazuje na odpowiednie repozytorium.
  3. Otwórz okno serwisowe: przełącz aplikację na starym serwerze w tryb konserwacji (maintenance mode), aby wstrzymać przyjmowanie zapisów.
  4. Wykonaj końcowy zrzut bazy danych, a następnie uruchom ostatnią synchronizację rsync z flagą --delete.
  5. Przywróć zrzut na nowym serwerze i uruchom usługi.
  6. Przeprowadź ponowne testy przez --resolve oraz nadpisanie w pliku hosts, w tym jeden rzeczywisty zapis.
  7. Zmień rekordy A oraz AAAA na nowy adres IP.
  8. Monitoruj oba serwery. Dziennik dostępu (access log) starego serwera wskazuje, kto nadal się z nim łączy; liczba ta powinna dążyć do zera w miarę wygasania TTL.
  9. Wyłącz stronę konserwacji.
  10. Pozostaw stary serwer uruchomiony i nienaruszony przez co najmniej tydzień.

Krok z trybem konserwacji jest najczęściej pomijany, a stanowi kluczowe zabezpieczenie. Gdy nowa baza danych przyjmie zapis, wycofanie zmian (rollback) oznacza albo utratę tego zapisu, albo konieczność zrzutu nowej bazy i załadowania jej do starej. Kilkuminutowe okno tylko do odczytu jest niewielkim kosztem. Dwie bazy danych, które przyjęły zapisy, oznaczają dni ręcznej synchronizacji danych.

Plan wycofania zmian

Wycofanie zmian to jedna czynność: przywrócenie rekordów DNS do wartości 198.51.100.10. Powodzenie tego procesu zależy od czterech wcześniejszych działań.

  • Stary serwer nadal działa, jego usługi są uruchomione, a dane nienaruszone. Zatrzymano na nim zapisy, ale nie wycofano go z eksploatacji.
  • Wartość TTL jest niska, więc droga powrotna jest równie szybka, co proces wdrażania.
  • Nowy adres IP został dodany do list dozwolonych podmiotów zewnętrznych, a nie zastąpił starego. Usunięcie starego adresu spowodowałoby awarię ścieżki wycofania w bramce płatności.
  • Nowy serwer nie przyjął żadnych zapisów, których nie można zidentyfikować, ponieważ jedynymi operacjami zapisu były własne transakcje testowe.

Przed rozpoczęciem okna serwisowego należy ustalić, co wyzwala procedurę wycofania zmian. Wystarczą dwa wyzwalacze: każdy błąd, którego nie można zdiagnozować w określonym czasie, oraz jakakolwiek utrata danych. Zapisanie tych założeń z wyprzedzeniem zapobiega godzinom domysłów, które zmieniają dziesięciominutową przerwę w działaniu w długotrwałą awarię.

Weryfikacja poprawności migracji

Migracja nie kończy się w momencie, gdy strona zaczyna się ładować. Należy sprawdzić elementy, które zawodzą dopiero po pewnym czasie.

# new server
systemctl --failed
journalctl -p err -b --no-pager | tail -n 40
sudo certbot certificates
sudo systemctl list-timers --all --no-pager

Wynik systemctl --failed raportujący 0 loaded units listed jest pożądanym stanem. Polecenie certbot certificates powinno wskazywać oczekiwane daty wygaśnięcia, a list-timers powinno wyświetlać wszystkie zaplanowane zadania z inwentarza wraz z rzeczywistym czasem następnego uruchomienia, a nie pustym polem.

Następnie należy celowo zrestartować nowy serwer, obserwując proces. Usługa uruchomiona ręcznie i niewłączona w systemd działa bez zarzutu do momentu pierwszego nieplanowanego restartu o trzeciej nad ranem.

# new server
sudo reboot

# your laptop, once it comes back
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/

Jeśli aplikacja działa w kontenerach, ta sama pułapka przybiera inną formę, ponieważ stos compose wymaga jawnej polityki restartu, aby uruchomić się automatycznie po restarcie systemu.

Ostatni test jest najłatwiejszy do odłożenia w czasie, a jednocześnie najważniejszy: zadanie kopii zapasowej. Migracja, która kończy się pozostawieniem serwera bez backupu, jedynie zamienia jedno ryzyko na inne. Należy ręcznie uruchomić kopię zapasową na nowej maszynie, a następnie przywrócić z niej pojedynczy plik do tymczasowego katalogu. Repozytorium restic z przetestowanym procesem przywracania to rozwiązanie, które faktycznie pomaga w sytuacji awaryjnej. Jeśli stary i nowy serwer działają równolegle przez tydzień, spójny sposób dostępu i konfiguracji każdego hosta zapobiega rozbieżnościom w ich ustawieniach, gdy oba są aktywne.

Po przełączeniu: stary serwer i ostatnie czynności

Stary serwer należy zachować przez okres od jednego do dwóch tygodni. Koszt utrzymania odpowiada jednej miesięcznej opłacie za plan, który i tak miał zostać anulowany, a stanowi jedyną możliwość wycofania zmian. Następnie należy zamknąć pozostałe kwestie.

  • Ponowne użycie tej samej nazwy hosta w pliku ~/.ssh/config dla nowej maszyny spowoduje wystąpienie WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! przy pierwszym połączeniu, ponieważ nazwa ta odpowiada teraz innym kluczem hosta. Należy usunąć nieaktualny wpis za pomocą ssh-keygen -R example.com dopiero po upewnieniu się, co było przyczyną zmiany, a nie odruchowo, ponieważ to samo ostrzeżenie pojawia się w przypadku ataku typu interception. Migracja to również dobry moment na weryfikację uprawnień dostępu dla poszczególnych kluczy, co opisano w zarządzanie kluczami SSH w małej infrastrukturze.
  • Należy wykonać ostatnią migawkę lub kopię zapasową starego serwera i przechowywać ją w lokalizacji innej niż u starego dostawcy.
  • Należy usunąć stary adres IP z monitoringu, rekordów SPF oraz z list dozwolonych u stron trzecich, zachowując tę kolejność i wykonując to jako ostatni krok.
  • Plan u starego dostawcy należy anulować dopiero po potwierdzeniu, że ostatnia kopia zapasowa jest poprawnie odczytywana w innej lokalizacji.

FAQ

Ile czasu zajmuje migracja serwera na nowy VPS?

Przerwa w działaniu widoczna dla użytkownika ogranicza się zazwyczaj do końcowego zrzutu bazy danych, ostatniej synchronizacji rsync oraz uruchomienia usług, co w przypadku małej aplikacji zajmuje od 10 do 30 minut. Czas kalendarzowy jest dłuższy, ponieważ wartość DNS TTL należy obniżyć co najmniej na okres odpowiadający staremu TTL przed przełączeniem; bezpieczniejszym rozwiązaniem jest zrobienie tego z jednodniowym wyprzedzeniem. Kopiowanie dużych zbiorów danych również warto zaplanować z kilkudniowym wyprzedzeniem. Proces ten odbywa się na działającym serwerze, a powtórne uruchomienie przesyła jedynie zmiany, które wystąpiły od ostatniej synchronizacji.

Czy można użyć rsync do skopiowania działającej bazy MySQL lub PostgreSQL zamiast wykonywać zrzut?

Nie. rsync kopiuje pliki pojedynczo, podczas gdy baza danych zapisuje dane do wielu plików jednocześnie. W efekcie kopia zawiera fragmenty danych z różnych momentów czasowych i reprezentuje stan, w którym baza nigdy nie istniała. Może ona odmówić uruchomienia lub ulec awarii w późniejszym czasie, gdy zapytanie trafi na uszkodzoną stronę danych. Należy użyć pg_dump z pg_dumpall --globals-only lub mysqldump --single-transaction, albo najpierw zatrzymać bazę danych, a następnie skopiować pliki. W przypadku dużego klastra PostgreSQL, pg_basebackup pozwala na wykonanie spójnej kopii fizycznej działającego serwera.

Jak przetestować nowy VPS przed zmianą rekordów DNS?

Należy nadpisać rozpoznawanie nazw na własnym komputerze. W przypadku pojedynczego żądania, curl --resolve example.com:443:203.0.113.20 https://example.com/ kieruje połączenie na nowy adres IP, jednocześnie weryfikując certyfikat dla właściwej nazwy hosta. W celu przetestowania w przeglądarce, należy dodać wpis 203.0.113.20 example.com do pliku /etc/hosts na komputerze lokalnym, sprawdzić logowanie, odczyt z bazy, zapis formularza oraz przesyłanie plików, a następnie usunąć ten wpis. Aby sprawdzić sam certyfikat, należy wykonać openssl s_client -connect 203.0.113.20:443 -servername example.com.

Jaką wartość TTL ustawić i kiedy ją obniżyć?

Wartość rekordów A oraz AAAA należy obniżyć do 300 sekund, wykonując to co najmniej na okres odpowiadający pełnemu staremu TTL przed przełączeniem. Resolver, który zbuforował rekord przed zmianą, zachowa starą wartość przez pozostały czas starego TTL, więc obniżenie wartości na godzinę przed migracją nie przyniesie efektu, jeśli stary TTL wynosił 86400. Po zakończeniu migracji, gdy logi dostępowe starego serwera przestaną wykazywać ruch, wartość TTL można przywrócić do standardowego poziomu.

Czy należy skopiować certyfikat TLS, czy wygenerować nowy na nowym serwerze?

Oba rozwiązania są poprawne. Skopiowanie /etc/letsencrypt/ pozwala zachować ważność certyfikatu do pierwotnej daty wygaśnięcia, jednak na nowym serwerze należy zainstalować tę samą wtyczkę uwierzytelniającą certbot, w przeciwnym razie pierwsze odnowienie zakończy się niepowodzeniem; po zmianie DNS warto wykonać certbot renew --dry-run w celu weryfikacji. Wygenerowanie nowego certyfikatu jest czystszym rozwiązaniem, jeśli można użyć wyzwania DNS-01, ponieważ potwierdza ono kontrolę poprzez rekord TXT i działa przed skierowaniem DNS na nowy serwer. Wyzwanie HTTP-01 nie może zostać użyte na nowym serwerze, dopóki rekordy DNS nie zostaną zaktualizowane, ponieważ żądanie walidacyjne trafiłoby na stary serwer.