Twaalf builds

Twee terabytes verplaatsen tussen twee van onze locaties zonder downtime

Een pre-copy, een streaming replica en een proxied cutover, met de TCP-afstemming die bepaalt of een langeafstandsoverdracht een uur of een dag duurt.

Wat dit bouwt

Twee terabyte aan bestanden en een live Postgres-database verplaatst van de ene site naar de andere, terwijl de applicatie gedurende de hele tijd verzoeken blijft beantwoorden. De methode is een bulk-pre-copy terwijl de bron blijft serveren, een streaming-replica die in de pas blijft, en een cutover die in seconden wordt gemeten, waarbij de oude host proxyt naar de nieuwe totdat DNS is bijgewerkt.

Afstand is het interessante deel. Tussen AMS-01 en FRA-01 is de round trip zeven milliseconden en een enkele stream vult een poort. Voer dezelfde kopie uit naar GRU-01, waar de round trip bijna tweehonderd milliseconden is, en een niet afgestelde overdracht kruipt voort op een klein deel van waar je voor betaalt, terwijl beide machines bijna niets doen. Dat verschil is volledig te wijten aan hoeveel data één verbinding onderweg mag hebben.

Voordat je begint

  • Bron- en doelinstanties, een tunnel ertussen, en root op beide.
  • DNS dat je beheert, met de TTL van de records minstens een dag van tevoren verlaagd naar zestig seconden. Dit is de stap die mensen overslaan en dan zes uur wachten.
  • Genoeg schijfruimte op het doel voor de hele dataset plus de replica.

Het bandwidth-delay product bepaalt alles. Vermenigvuldig de round-trip-tijd met de snelheid die je wilt en je krijgt de hoeveelheid data die op elk moment onbevestigd onderweg moet zijn:

ping -c20 target.example.com | tail -1
PadRTTOnderweg voor 1 Gbit/sStandaard kernelvenster
AMS-01 naar FRA-017 ms0,9 MBVoldoende
AMS-01 naar NYC-0176 ms9,5 MBOnvoldoende
AMS-01 naar SIN-01168 ms21 MBLang niet genoeg
AMS-01 naar GRU-01196 ms24,5 MBLang niet genoeg

Op beide machines:

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 in plaats van de standaard congestiecontrole is belangrijk over lange paden met enig verlies, omdat op verlies gebaseerde algoritmen een enkel verloren pakket interpreteren als congestie en hun venster halveren. Over tweehonderd milliseconden duurt het seconden om daarvan te herstellen, en het gebeurt herhaaldelijk.

Meet voordat je twee terabyte aan een aanname toevertrouwt:

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

Acht parallelle streams zouden de poortsnelheid moeten benaderen. Als één stream honderd megabit haalt en acht stream achthonderd, dan zijn de vensterinstellingen niet effectief; een enkele stream zou het grootste deel van de weg zelf moeten afleggen zodra ze dat zijn.

2. Bulk-pre-copy

Voer dit uit tegen een live bron. Het zal uren duren, en er wordt niets onderbroken terwijl het loopt:

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/

De keuze van de ciphersuite is geen bijgeloof. Op deze processoren verplaatst de hardware-versnelde GCM-modus meerdere gigabytes per seconde per kern, terwijl de standaard onderhandelde ciphersuite op sommige clientversies aanzienlijk langzamer is en je overdracht verandert in een single-core benchmark. Compressie is uit omdat de data al gecomprimeerd is en, als dat niet zo is, besteedt de CPU zijn tijd daaraan in plaats van aan het verplaatsen van bytes.

Voor miljoenen kleine bestanden is één stream de verkeerde vorm. Splits op top-level directory en voer meerdere uit:

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 parallelle kopieën op acht dedicated cores is hier het juiste aantal; zestien is niet twee keer zo snel en maakt de bronmachine onaangenaam voor wat het nog steeds serveert.

3. De database, gerepliceerd in plaats van gekopieerd

Het dumpen en herstellen van tweehonderd gigabyte aan Postgres betekent een lang venster waarin writes verloren gaan. Stream het in plaats daarvan. Op de bron:

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

Op het doel:

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

De -R-vlag schrijft de verbindingsinstellingen en het standby-signaalbestand, zodat het doel als replica opstart en begint te volgen. Vanaf hier blijft het binnen een seconde of twee van de bron, voor onbepaalde tijd, wat betekent dat de cutover de database helemaal niet meer hoeft te verplaatsen.

4. Cutover

Start een probe vanaf een derde machine en laat deze draaien. Het is zowel je verificatie als je registratie van wat de switch kost:

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

Voer dan, in deze volgorde en zonder pauzes:

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

Stappen twee tot en met vijf duren enkele seconden. Stop nu de oude host met het serveren van verouderde inhoud terwijl DNS propageert, door er een proxy voor de nieuwe van te maken:

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

Dit is het stuk dat het geheel niet-verstorend maakt. Iedereen die nog het oude adres resolveert, krijgt een correct antwoord via de tunnel in plaats van een fout of, erger, een kopie van de data die bij de cutover stopte met updaten. Verander nu het DNS-record.

Verifieer het

Begin met het probe-logboek dat je hebt verzameld:

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

Elke regel moet 200 lezen. Een handvol langzamere antwoorden tijdens de switch is te verwachten; elke non-200 betekent dat stap vijf te lang na stap twee begon, en de proxy in de vorige sectie is wat dat gat tot niets terugbrengt.

Bewijs dan dat de data identiek is in plaats van slechts aanwezig:

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"

Een dry run die niets specificeert betekent dat de twee bomen overeenkomen in inhoud, permissies, eigenaarschap en uitgebreide attributen. Byteaantallen moeten exact overeenkomen.

Database vervolgens:

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

Herstel zou nu false moeten zijn op het doel, en de rijtellingen moeten overeenkomen. Bevestig ten slotte dat verkeer echt is verplaatst in plaats van stilzwijgend voor altijd geproxied:

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

Wanneer de oude host een volledige TTL plus een ruime marge idle is geweest, haal de proxy weg en vernietig de instantie. Vernietig het niet dezelfde middag; de goedkoopste verzekering in deze hele procedure is de bronmachine intact en uitgeschakeld laten voor een week.

Achteraf

Twee terabyte over een goed afgesteld lange-afstandspad is ruwweg een uur op tien gigabit en in de praktijk nogal langer, omdat de bron ook serveert. Tussen naburige Europese sites, plan een middag; tussen continenten, start het 's avonds en doe de cutover de volgende dag. De locatiepagina vermeldt de round-trip-tijden die wij meten, en dat zijn de getallen om in de tabel in stap één te zetten.

Klaar wanneer jij dat bent

Kies een stad. Kies een formaat. Betaal in munt.

Geen formulieren over wie je bent, geen wachten op een mens die je goedkeurt, geen telefoontje om iets te verifiëren. De factuur wordt betaald en de inloggegevens belanden in je inbox.