Zwölf Builds

Zwei Terabyte zwischen zwei unserer Standorte verschieben ohne Ausfallzeit

Ein Pre-Copy, eine Streaming-Replikation und ein Cutover über Proxy, mit der TCP-Optimierung, die darüber entscheidet, ob ein Langstrecken-Transfer eine Stunde oder einen Tag dauert.

Was dabei herauskommt

Zwei Terabyte Dateien und eine laufende Postgres-Datenbank werden von einem Standort zum anderen verschoben, während die Anwendung durchgehend Anfragen beantwortet. Die Methode ist ein Bulk-Vorkopieren, während die Quelle weiter bedient, ein Streaming-Replikat, das in Schritt bleibt, und ein Wechsel, der in Sekunden gemessen wird, wobei der alte Host bis zum DNS-Umstieg auf den neuen proxyt.

Die Entfernung ist der interessante Teil. Zwischen AMS-01 und FRA-01 beträgt die Roundtrip-Zeit sieben Millisekunden, und ein einzelner Stream füllt einen Port. Führt man dieselbe Kopie nach GRU-01 aus, wo die Roundtrip-Zeit nahe zweihundert beträgt, kriecht eine unangepasste Übertragung mit einem kleinen Bruchteil dessen dahin, was man bezahlt, während beide Maschinen fast im Leerlauf sind. Diese Lücke liegt vollständig daran, wie viele Daten eine Verbindung in der Luft haben darf.

Bevor Sie beginnen

  • Quell- und Zielinstanzen, ein Tunnel zwischen ihnen und Root auf beiden.
  • DNS, das Sie kontrollieren, mit der TTL des Datensatzes mindestens einen Tag vorher auf sechzig Sekunden gesenkt. Das ist der Schritt, den man überspringt und dann sechs Stunden wartet.
  • Genug Speicher auf dem Ziel für den gesamten Datensatz plus das Replikat.

1. Ermitteln, was die Leitung kann

Das Bandbreite-Verzögerungs-Produkt entscheidet über alles. Multiplizieren Sie die Roundtrip-Zeit mit der gewünschten Rate, und Sie erhalten die Datenmenge, die zu jedem Zeitpunkt unbestätigt in der Luft sein muss:

ping -c20 target.example.com | tail -1
PfadRTTIn Flight für 1 Gbit/sStandard-Kernel-Fenster
AMS-01 nach FRA-017 ms0.9 MBAusreichend
AMS-01 nach NYC-0176 ms9.5 MBNicht ausreichend
AMS-01 nach SIN-01168 ms21 MBBei weitem nicht
AMS-01 nach GRU-01196 ms24.5 MBBei weitem nicht

Auf beiden Maschinen:

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 --system

BBR statt der Standard-Staukontrolle macht auf langen Pfaden mit jeglichem Verlust einen Unterschied, weil verlustbasierte Algorithmen ein einzelnes verworfenes Paket als Stau interpretieren und ihr Fenster halbieren. Über zweihundert Millisekunden dauert die Erholung davon Sekunden, und das passiert wiederholt.

Messen Sie, bevor Sie zwei Terabyte auf eine Annahme festlegen:

# target
apt install -y iperf3 && iperf3 -s
# source
iperf3 -c target.example.com -P 8 -t 30

Acht parallele Streams sollten sich der Portgeschwindigkeit nähern. Wenn ein Stream hundert Megabit schafft und acht schaffen achthundert, haben die Fenstereinstellungen nicht gegriffen; ein einzelner Stream sollte nach deren Aktivierung den größten Teil des Weges alleine schaffen.

2. Bulk-Vorkopieren

Führen Sie dies gegen eine Live-Quelle aus. Es dauert Stunden, und nichts wird dabei unterbrochen:

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/

Die Wahl der Chiffre ist kein Aberglaube. Auf diesen Prozessoren bewegt die hardwarebeschleunigte GCM-Mode mehrere Gigabyte pro Sekunde pro Kern, während die standardmäßig ausgehandelte Chiffre bei einigen Client-Versionen deutlich langsamer ist und Ihre Übertragung in einen Single-Core-Benchmark verwandelt. Die Komprimierung ist aus, weil die Daten bereits komprimiert sind, und wenn nicht, verbringt die CPU ihre Zeit damit, statt Bytes zu bewegen.

Für Millionen kleiner Dateien ist ein Stream die falsche Form. Teilen Sie nach Top-Level-Verzeichnissen auf und führen Sie mehrere aus:

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/{}/

Acht parallele Kopien auf acht dedizierten Kernen sind hier die richtige Zahl; sechzehn sind nicht doppelt so schnell und machen die Quellmaschine für das, was sie noch bedient, unangenehm.

3. Die Datenbank, repliziert statt kopiert

Das Dumpen und Wiederherstellen von zweihundert Gigabyte Postgres bedeutet ein langes Fenster, in dem Schreibvorgänge verloren gehen. Streamen Sie stattdessen. Auf der Quelle:

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 postgresql

Auf dem Ziel:

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 postgresql

Das -R-Flag schreibt die Verbindungseinstellungen und die Standby-Signaldatendatei, sodass das Ziel als Replikat hochkommt und zu folgen beginnt. Von hier an bleibt es dauerhaft innerhalb von einer oder zwei Sekunden um die Quelle, was bedeutet, dass der Wechsel keine Datenbankbewegung mehr beinhaltet.

4. Wechsel

Starten Sie eine Sonde von einer dritten Maschine und lassen Sie sie laufen. Sie ist sowohl Ihre Verifizierung als auch Ihr Protokoll dessen, was der Wechsel kostet:

while true; do curl -s -o /dev/null -w "%{http_code} %{time_total}\n" https://app.example.com/health; sleep 1; done | tee cutover.log

Dann, in dieser Reihenfolge und ohne Pausen:

# 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"

Die Schritte zwei bis fünf dauern einige Sekunden. Stoppen Sie nun den alten Host, während DNS propagiert, damit er keine veralteten Inhalte ausliefert, indem Sie ihn in einen Proxy für den neuen verwandeln:

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;
  }
}

Das ist das Stück, das das Ganze nicht störend macht. Jeder, der noch die alte Adresse auflöst, bekommt eine korrekte Antwort über den Tunnel statt eines Fehlers oder, schlimmer, einer Kopie der Daten, die zum Zeitpunkt des Wechsels aufgehört hat, aktualisiert zu werden. Ändern Sie jetzt den DNS-Eintrag.

Verifizieren

Beginnen Sie mit dem Sondenprotokoll, das Sie gesammelt haben:

sort cutover.log | uniq -c | sort -rn | head

Jede Zeile sollte 200 lauten. Einige langsamere Antworten während des Wechsels sind zu erwarten; jeder Nicht-200 bedeutet, dass Schritt fünf nach Schritt zwei mit zu großem Abstand begonnen hat, und der Proxy im vorherigen Abschnitt schrumpft diese Lücke auf nichts.

Dann beweisen Sie, dass die Daten identisch sind, nicht nur vorhanden:

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"

Ein Probelauf, der nichts auflistet, bedeutet, dass die beiden Bäume bei Inhalt, Berechtigungen, Eigentümer und erweiterten Attributen übereinstimmen. Bytessummen sollten exakt übereinstimmen.

Datenbank als Nächstes:

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;"

Die Wiederherstellung sollte auf dem Ziel nun false sein, und die Zeilenzahlen sollten übereinstimmen. Bestätigen Sie schließlich, dass der Verkehr wirklich gewechselt ist, statt stillschweigend für immer proxied zu werden:

dig +short app.example.com
tail -f /var/log/nginx/access.log | wc -l   # on the old host, an hour later

Wenn der alte Host für eine volle TTL plus einer komfortablen Marge inaktiv war, nehmen Sie den Proxy herunter und zerstören Sie die Instanz. Zerstören Sie sie nicht am selben Nachmittag; die billigste Versicherung in diesem gesamten Verfahren ist, die Quellmaschine intakt und ausgeschaltet für eine Woche zu lassen.

Danach

Zwei Terabyte über einen gut abgestimmten Langstreckenpfad sind ungefähr eine Stunde bei zehn Gigabit und in der Praxis eher länger, da die Quelle auch bedient. Zwischen benachbarten europäischen Standorten planen Sie einen Nachmittag; zwischen Kontinenten starten Sie abends und machen den Wechsel am nächsten Tag. Die Standorteseite listet die Roundtrip-Zeiten, die wir messen, und das sind die Zahlen, die Sie in die Tabelle in Schritt eins eintragen.

Bereit, wenn Sie es sind

Wählen Sie eine Stadt. Wählen Sie eine Größe. Bezahlen Sie in Coins.

Keine Formulare darüber, wer Sie sind, kein Warten auf einen Menschen, der Sie genehmigt, kein Anruf zur Verifizierung. Die Rechnung wird beglichen, und die Zugangsdaten landen in Ihrem Posteingang.