Doze construções

Movendo dois terabytes entre dois de nossos sites sem interrupção

Uma pré-cópia, uma réplica de streaming e uma troca com proxy, com o ajuste de TCP que decide se uma transferência de longa distância leva uma hora ou um dia.

O que este guia constrói

Dois terabytes de arquivos e um banco de dados Postgres ao vivo são movidos de um site para outro, com o aplicativo respondendo a requisições durante todo o processo. O método é uma pré-cópia em massa enquanto a origem continua servindo, uma réplica de streaming que permanece sincronizada, e um corte que dura segundos, com o host antigo fazendo proxy para o novo até o DNS se atualizar.

A distância é a parte interessante. Entre AMS-01 e FRA-01, o tempo de ida e volta é de sete milissegundos e um único fluxo preencherá uma porta. Execute a mesma cópia para GRU-01, onde o round trip se aproxima de duzentos, e uma transferência não ajustada se arrasta a uma pequena fração do que você paga, enquanto ambas as máquinas ficam quase ociosas. Essa discrepância se resume inteiramente à quantidade de dados que uma conexão pode ter em voo.

Antes de começar

  • Instâncias de origem e destino, um túnel entre elas e acesso root em ambas.
  • DNS que você controla, com o TTL do registro reduzido para sessenta segundos pelo menos um dia antes. Este é o passo que as pessoas pulam e depois esperam seis horas.
  • Disco suficiente no destino para todo o conjunto de dados mais a réplica.

O produto de atraso de largura de banda decide tudo. Multiplique o tempo de ida e volta pela taxa desejada e você obtém a quantidade de dados que deve estar não reconhecidos em voo a qualquer momento:

ping -c20 target.example.com | tail -1
CaminhoRTTEm voo para 1 Gbit/sJanela padrão do kernel
AMS-01 a FRA-017 ms0.9 MBSuficiente
AMS-01 a NYC-0176 ms9.5 MBNão suficiente
AMS-01 a SIN-01168 ms21 MBLonge de ser suficiente
AMS-01 a GRU-01196 ms24.5 MBLonge de ser suficiente

Em ambas as 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 --system

BBR em vez do controle de congestionamento padrão é importante em caminhos longos com qualquer perda, porque algoritmos baseados em perda interpretam um único pacote descartado como congestionamento e reduzem sua janela pela metade. Em duzentos milissegundos, a recuperação disso leva segundos, e isso acontece repetidamente.

Meça antes de comprometer dois terabytes a uma suposição:

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

Oito fluxos paralelos devem se aproximar da velocidade da porta. Se um fluxo gerencia cem megabits e oito gerenciam oitocentos, as configurações de janela não surtiram efeito; um único fluxo deve percorrer a maior parte do caminho sozinho depois que elas estiverem aplicadas.

2. Pré-cópia em massa

Execute isso contra uma origem ativa. Levará horas, e nada será interrompido enquanto isso:

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/

A escolha da cifra não é superstição. Nesses processadores, o modo GCM acelerado por hardware move vários gigabytes por segundo por núcleo, enquanto a cifra negociada por padrão em algumas versões de cliente é visivelmente mais lenta e transforma sua transferência em um benchmark de um único núcleo. A compressão está desativada porque os dados já estão compactados e, se não estiverem, a CPU gasta tempo nisso em vez de mover bytes.

Para milhões de arquivos pequenos, um fluxo é o formato errado. Divida por diretório de nível superior e execute vários:

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

Oito cópias paralelas em oito núcleos dedicados é o número certo aqui; dezesseis não é duas vezes mais rápido e tornará a máquina de origem desagradável para o que ainda está servindo.

3. O banco de dados, replicado em vez de copiado

Despejar e restaurar duzentos gigabytes de Postgres significa uma longa janela onde as gravações são perdidas. Transmita-o em vez disso. Na origem:

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

No 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 postgresql

A flag -R grava as configurações de conexão e o arquivo de sinal do standby, para que o destino suba como réplica e comece a seguir. A partir daqui, permanece dentro de um ou dois segundos da origem indefinidamente, o que significa que o corte não envolve mais mover o banco de dados.

4. Corte

Inicie uma sondagem de uma terceira máquina e deixe-a rodando. É tanto sua verificação quanto seu registro do que custou a troca:

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

Então, nesta ordem e sem 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"

Os passos dois a cinco levam alguns segundos. Agora, impeça o host antigo de servir conteúdo obsoleto enquanto o DNS se propaga, transformando-o em um proxy para o novo:

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 é a peça que torna todo o processo não disruptivo. Qualquer pessoa que ainda resolva o endereço antigo obtém uma resposta correta pelo túnel em vez de um erro ou, pior, uma cópia dos dados que parou de atualizar no corte. Agora, altere o registro DNS.

Verifique

Comece com o log de sondagem que você coletou:

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

Cada linha deve ler 200. Algumas respostas mais lentas durante a troca são esperadas; qualquer não-200 significa que o passo cinco começou depois do passo dois por uma margem muito grande, e o proxy na seção anterior é o que reduz essa lacuna a nada.

Em seguida, prove que os dados são idênticos em vez de meramente 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"

Uma execução seca que não detalha nada significa que as duas árvores concordam quanto a conteúdo, permissões, propriedade e atributos estendidos. As somas de bytes devem corresponder exatamente.

Banco de dados em seguida:

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

A recuperação deve agora ser falsa no destino, e as contagens de linhas devem concordar. Finalmente, confirme que o tráfego realmente se moveu em vez de ser silenciosamente proxiado para sempre:

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

Quando o host antigo estiver ocioso por um TTL completo mais uma margem confortável, remova o proxy e destrua a instância. Não a destrua na mesma tarde; o seguro mais barato em todo este procedimento é deixar a máquina de origem intacta e desligada por uma semana.

Depois

Dois terabytes em um caminho de longa distância bem ajustado é aproximadamente uma hora a dez gigabits e bastante mais na prática, já que a origem também está servindo. Entre sites europeus vizinhos, planeje uma tarde; entre continentes, comece à noite e faça o corte no dia seguinte. A página de localizações lista os tempos de ida e volta que medimos, e esses são os números para colocar na tabela do passo um.

Pronto quando você estiver

Escolha uma cidade. Escolha um tamanho. Pague em cripto.

Sem formulários sobre quem você é, sem esperar aprovação de um humano, sem ligação para verificar nada. O pagamento é confirmado e as credenciais chegam na sua caixa de entrada.