Cosa si ottiene
Due terabyte di file e un database Postgres live spostati da un sito all'altro, con l'applicazione che risponde alle richieste per tutto il tempo. Il metodo prevede una pre-copia in blocco mentre la sorgente continua a servire, una replica in streaming che resta sincronizzata, e un cutover misurato in secondi con il vecchio host che fa da proxy verso il nuovo finché il DNS non si aggiorna.
La distanza è la parte interessante. Tra AMS-01 e FRA-01 il round trip è di sette millisecondi e un singolo stream riempie una porta. La stessa copia verso GRU-01, dove il round trip è vicino a duecento, e un trasferimento non ottimizzato striscia a una piccola frazione di ciò che si sta pagando mentre entrambe le macchine restano quasi inattive. Questa differenza dipende interamente da quanti dati una connessione può tenere in volo.
Prima di iniziare
- Istanza sorgente e target, un tunnel tra loro e root su entrambe.
- DNS controllato, con il TTL del record abbassato a sessanta secondi almeno un giorno prima. Questo è il passaggio che la gente salta e poi aspetta sei ore.
- Disco sufficiente sul target per l'intero dataset più la replica.
1. Calcolare cosa può fare il collegamento
Il prodotto banda-ritardo decide tutto. Moltiplicando il tempo di round trip per la velocità desiderata si ottiene la quantità di dati che deve essere non riconosciuta in volo in ogni momento:
ping -c20 target.example.com | tail -1| Percorso | RTT | In volo per 1 Gbit/s | Finestra kernel predefinita |
|---|---|---|---|
| AMS-01 to FRA-01 | 7 ms | 0.9 MB | Sufficiente |
| AMS-01 to NYC-01 | 76 ms | 9.5 MB | Non sufficiente |
| AMS-01 to SIN-01 | 168 ms | 21 MB | Molto lontano |
| AMS-01 to GRU-01 | 196 ms | 24.5 MB | Molto lontano |
Su entrambe le macchine:
cat > /etc/sysctl.d/75-transfer.conf <<EOF
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 262144 134217728
net.ipv4.tcp_wmem = 4096 262144 134217728
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq
net.ipv4.tcp_mtu_probing = 1
EOF
sysctl --systemBBR invece del controllo di congestione predefinito conta su percorsi lunghi con qualsiasi perdita, perché gli algoritmi basati sulla perdita interpretano un singolo pacchetto perso come congestione e dimezzano la finestra. Oltre duecento millisecondi, riprendersi richiede secondi, e accade ripetutamente.
Misurare prima di impegnare due terabyte a un'ipotesi:
# target
apt install -y iperf3 && iperf3 -s
# source
iperf3 -c target.example.com -P 8 -t 30Otto stream paralleli dovrebbero avvicinarsi alla velocità della porta. Se uno stream arriva a cento megabit e otto a ottocento, le impostazioni della finestra non hanno avuto effetto; un singolo stream dovrebbe arrivare quasi da solo una volta che hanno avuto effetto.
2. Pre-copia in blocco
Eseguire contro una sorgente live. Ci vorranno ore e nulla viene interrotto mentre lo fa:
apt install -y rsync
rsync -aHAX --numeric-ids --partial --inplace --info=progress2 \
-e "ssh -c [email protected] -o Compression=no" \
/srv/data/ [email protected]:/srv/data/La scelta della cifratura non è superstizione. Su questi processori la modalità GCM accelerata via hardware sposta diversi gigabyte al secondo per core, mentre la cifratura predefinita negoziata da alcune versioni client è notevolmente più lenta e trasforma il trasferimento in un benchmark single-core. La compressione è disattivata perché i dati sono già compressi e, se non lo sono, la CPU usa il tempo per quello invece che per spostare byte.
Per milioni di file piccoli, un singolo stream è la forma sbagliata. Dividere per directory di primo livello ed eseguirne diverse:
ls /srv/data | xargs -P 8 -I{} rsync -aHAX --numeric-ids \
-e "ssh -c [email protected] -o Compression=no" \
/srv/data/{}/ [email protected]:/srv/data/{}/Otto copie parallele su otto core dedicati è il numero giusto qui; sedici non è due volte più veloce e renderà la macchina sorgente sgradevole per ciò che sta ancora servendo.
3. Il database, replicato piuttosto che copiato
Dump e restore di duecento gigabyte di Postgres significano una lunga finestra dove le scritture vanno perse. Invece, stream. Sulla sorgente:
sudo -u postgres psql -c "CREATE USER repl WITH REPLICATION PASSWORD '<a long password>';"
sudo -u postgres psql -c "SELECT pg_create_physical_replication_slot('target_site');"
echo "host replication repl 10.9.0.2/32 scram-sha-256" >> /etc/postgresql/17/main/pg_hba.conf
systemctl reload postgresqlSul target:
systemctl stop postgresql
rm -rf /var/lib/postgresql/17/main/*
sudo -u postgres pg_basebackup -h 10.9.0.1 -U repl -D /var/lib/postgresql/17/main \
-S target_site -X stream -R -P -c fast
systemctl start postgresqlIl flag -R scrive le impostazioni di connessione e il file segnale di standby, quindi il target si avvia come replica e inizia a seguire. Da qui rimane entro un secondo o due dalla sorgente indefinitamente, il che significa che il cutover non comporta più lo spostamento del database.
4. Cutover
Avviare una sonda da una terza macchina e lasciarla in esecuzione. È sia la verifica sia la registrazione di ciò che costa la commutazione:
while true; do curl -s -o /dev/null -w "%{http_code} %{time_total}\n" https://app.example.com/health; sleep 1; done | tee cutover.logPoi, in questo ordine e senza pause:
# 1. final delta, source still live
rsync -aHAX --numeric-ids --delete -e "ssh -c [email protected]" /srv/data/ [email protected]:/srv/data/
# 2. stop writes on the source
systemctl stop app
# 3. one more delta, now tiny
rsync -aHAX --numeric-ids --delete -e "ssh -c [email protected]" /srv/data/ [email protected]:/srv/data/
# 4. promote the replica
ssh [email protected] "sudo -u postgres pg_ctl promote -D /var/lib/postgresql/17/main"
# 5. start the application on the target
ssh [email protected] "systemctl start app"I passi da due a cinque richiedono alcuni secondi. Ora fermare il vecchio host dal servire contenuti obsoleti mentre il DNS si propaga, trasformandolo in un proxy per il nuovo:
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
location / {
proxy_pass https://10.9.0.2;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
}Questo è il pezzo che rende l'intera operazione non disruptiva. Chiunque risolva ancora il vecchio indirizzo ottiene una risposta corretta attraverso il tunnel invece di un errore o, peggio, una copia dei dati che ha smesso di aggiornarsi al cutover. Ora cambiare il record DNS.
Verifica
Inizia con il log della sonda che hai raccolto:
sort cutover.log | uniq -c | sort -rn | headOgni riga dovrebbe leggere 200. Una manciata di risposte più lente durante la commutazione è prevista; qualsiasi non-200 significa che il passo cinque è iniziato dopo il passo due con un margine troppo ampio, e il proxy nella sezione precedente è ciò che riduce questo intervallo a nulla.
Poi prova che i dati sono identici piuttosto che semplicemente presenti:
rsync -aHAXn --delete --itemize-changes /srv/data/ [email protected]:/srv/data/ | head
du -s --bytes /srv/data
ssh [email protected] "du -s --bytes /srv/data"Una prova a secco che non elenca nulla significa che i due alberi concordano su contenuto, permessi, proprietario e attributi estesi. I totali di byte dovrebbero corrispondere esattamente.
Database successivo:
ssh [email protected] "sudo -u postgres psql -c 'SELECT pg_is_in_recovery();'"
ssh [email protected] "sudo -u postgres psql app -c 'SELECT count(*) FROM orders;'"
sudo -u postgres psql app -c "SELECT count(*) FROM orders;"Il recovery dovrebbe ora essere false sul target e i conteggi delle righe dovrebbero concordare. Infine, confermare che il traffico si è davvero spostato piuttosto che essere silenziosamente proxato per sempre:
dig +short app.example.com
tail -f /var/log/nginx/access.log | wc -l # on the old host, an hour laterQuando il vecchio host è stato inattivo per un TTL completo più un margine comodo, abbattere il proxy e distruggere l'istanza. Non distruggerlo lo stesso pomeriggio; l'assicurazione più economica in questa intera procedura è lasciare la macchina sorgente intatta e spenta per una settimana.
Dopo
Due terabyte su un percorso long-haul ben ottimizzato richiedono circa un'ora a dieci gigabit e piuttosto di più nella pratica, poiché la sorgente sta anche servendo. Tra siti europei vicini, pianificare per un pomeriggio; tra continenti, iniziare la sera e fare il cutover il giorno successivo. La pagina delle posizioni elenca i tempi di round trip che misuriamo, e quelli sono i numeri da inserire nella tabella al passo uno.