Lo que se construye
Dos terabytes de archivos y una base de datos Postgres en vivo se mueven de un sitio a otro, con la aplicación respondiendo a las peticiones durante todo el proceso. El método consiste en una precopia masiva mientras el origen sigue sirviendo, una réplica de streaming que se mantiene al día, y un corte que se mide en segundos, con el host antiguo actuando como proxy hacia el nuevo hasta que el DNS se actualiza.
La distancia es la parte interesante. Entre AMS-01 y FRA-01 el tiempo de ida y vuelta es de siete milisegundos y un solo flujo llena el puerto. Si se ejecuta la misma copia hacia GRU-01, donde el tiempo de ida y vuelta se acerca a los doscientos, una transferencia sin ajustar avanza a una fracción de lo que se paga mientras ambas máquinas están casi inactivas. Esa diferencia se debe por completo a la cantidad de datos que se permite tener en vuelo en cada conexión.
Antes de empezar
- Instancias de origen y destino, un túnel entre ellas y acceso root en ambas.
- DNS que controle, con el TTL del registro reducido a sesenta segundos al menos un día antes. Este es el paso que la gente se salta y luego espera seis horas.
- Suficiente disco en el destino para todo el conjunto de datos más la réplica.
1. Calcular lo que el enlace puede hacer
El producto de ancho de banda y retardo decide todo. Multiplique el tiempo de ida y vuelta por la tasa que se desea y obtendrá la cantidad de datos que deben estar sin confirmar en vuelo en cada momento:
ping -c20 target.example.com | tail -1| Ruta | RTT | En vuelo para 1 Gbit/s | Ventana predeterminada del núcleo |
|---|---|---|---|
| AMS-01 a FRA-01 | 7 ms | 0.9 MB | Suficiente |
| AMS-01 a NYC-01 | 76 ms | 9.5 MB | Insuficiente |
| AMS-01 a SIN-01 | 168 ms | 21 MB | Muy insuficiente |
| AMS-01 a GRU-01 | 196 ms | 24.5 MB | Muy insuficiente |
En ambas máquinas:
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 en lugar del control de congestión predeterminado es importante en rutas largas con cualquier pérdida, porque los algoritmos basados en pérdidas interpretan un solo paquete descartado como congestión y reducen su ventana a la mitad. En doscientos milisegundos, recuperarse de eso lleva segundos, y ocurre repetidamente.
Mida antes de comprometer dos terabytes a una suposición:
# target
apt install -y iperf3 && iperf3 -s
# source
iperf3 -c target.example.com -P 8 -t 30Ocho flujos paralelos deberían acercarse a la velocidad del puerto. Si un flujo alcanza cien megabits y ocho alcanzan ochocientos, la configuración de la ventana no ha surtido efecto; un solo flujo debería alcanzar la mayor parte del camino por sí solo una vez que hayan surtido efecto.
2. Precopia masiva
Ejecute esto contra un origen en vivo. Llevará horas y no se interrumpe nada mientras se realiza:
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 elección del cifrador no es superstición. En estos procesadores, el modo GCM acelerado por hardware mueve varios gigabytes por segundo por núcleo, mientras que el cifrador negociado por defecto en algunas versiones de cliente es notablemente más lento y convierte la transferencia en una prueba de un solo núcleo. La compresión está desactivada porque los datos ya están comprimidos y, si no lo están, la CPU gasta su tiempo en eso en lugar de en mover bytes.
Para millones de archivos pequeños, un solo flujo no es la forma adecuada. Divida por directorio de nivel superior y ejecute varios:
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/{}/Ocho copias paralelas en ocho núcleos dedicados es el número correcto aquí; dieciséis no es el doble de rápido y hará que la máquina de origen sea desagradable para lo que aún esté sirviendo.
3. La base de datos, replicada en lugar de copiada
Dump y restauración de doscientos gigabytes de Postgres supone una ventana larga en la que se pierden escrituras. Mejor transmitirla. En el origen:
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 postgresqlEn el destino:
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 postgresqlLa marca -R escribe la configuración de conexión y el archivo de señal de espera, por lo que el destino se inicia como réplica y comienza a seguir. Desde aquí permanece a uno o dos segundos del origen indefinidamente, lo que significa que el corte ya no implica mover la base de datos en absoluto.
4. Corte
Inicie una sonda desde una tercera máquina y déjela corriendo. Es tanto su verificación como su registro de lo que costó el cambio:
while true; do curl -s -o /dev/null -w "%{http_code} %{time_total}\n" https://app.example.com/health; sleep 1; done | tee cutover.logLuego, en este orden y sin pausas:
# 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"Los pasos del dos al cinco llevan unos segundos. Ahora detenga al host antiguo para que no sirva contenido obsoleto mientras el DNS se propaga, convirtiéndolo en un proxy hacia el nuevo:
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;
}
}Esta es la pieza que hace que todo el proceso no sea disruptivo. Cualquiera que todavía resuelva la dirección antigua obtiene una respuesta correcta a través del túnel en lugar de un error o, peor, una copia de los datos que dejaron de actualizarse en el corte. Ahora cambie el registro DNS.
Verificación
Comience con el registro de la sonda que ha estado recopilando:
sort cutover.log | uniq -c | sort -rn | headCada línea debería decir 200. Un puñado de respuestas más lentas durante el cambio es de esperar; cualquier no-200 significa que el paso cinco comenzó después del paso dos con un margen demasiado amplio, y el proxy en la sección anterior es lo que reduce esa brecha a nada.
Luego demuestre que los datos son idénticos y no solo que están presentes:
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 ejecución en seco que no enumera nada significa que los dos árboles coinciden en contenido, permisos, propietario y atributos extendidos. Los totales de bytes deben coincidir exactamente.
Base de datos a continuación:
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;"La recuperación debería ser ahora falsa en el destino, y los conteos de filas deberían coincidir. Finalmente, confirme que el tráfico realmente se ha movido en lugar de ser proxificado silenciosamente para siempre:
dig +short app.example.com
tail -f /var/log/nginx/access.log | wc -l # on the old host, an hour laterCuando el host antiguo haya estado inactivo durante un TTL completo más un margen cómodo, retire el proxy y destruya la instancia. No la destruya esa misma tarde; el seguro más barato en todo este procedimiento es dejar la máquina de origen intacta y apagada durante una semana.
Después
Dos terabytes sobre una ruta de larga distancia bien ajustada son aproximadamente una hora a diez gigabits, y bastante más en la práctica, ya que el origen también está sirviendo. Entre sitios europeos vecinos, planifique para una tarde; entre continentes, comience por la noche y haga el corte al día siguiente. La página de ubicaciones enumera los tiempos de ida y vuelta que medimos, y esos son los números que debe poner en la tabla del paso uno.