Что здесь происходит
Два терабайта файлов и рабочая база данных Postgres переезжают с одного сайта на другой, и приложение всё это время отвечает на запросы. Метод — массовое предварительное копирование, пока источник продолжает обслуживать запросы, потоковая реплика, которая не отстаёт, и переключение, измеряемое секундами, при этом старый хост проксирует на новый, пока DNS не догонит.
Самое интересное — расстояние. Между AMS-01 и FRA-01 время кругового пути семь миллисекунд, и один поток заполнит порт. Запустите то же копирование на GRU-01, где время кругового пути около двухсот, и ненастроенная передача будет ползти ничтожной долей от того, что вы оплачиваете, пока обе машины почти простаивают. Этот разрыв целиком зависит от того, сколько данных одному соединению разрешено держать в полёте.
Перед началом
- Исходный и целевой инстансы, туннель между ними и root на обоих.
- DNS под вашим контролем, с TTL записи, сниженным до шестидесяти секунд минимум за день. Это шаг, который все пропускают, а потом ждут шесть часов.
- Достаточно диска на целевом хосте для всего набора данных плюс реплика.
1. Выясните, на что способен канал
Произведение полосы на задержку решает всё. Умножьте время кругового пути на желаемую скорость — и получите объём данных, который должен быть в полёте без подтверждения в любой момент:
ping -c20 target.example.com | tail -1| Путь | RTT | В полёте при 1 Гбит/с | Окно ядра по умолчанию |
|---|---|---|---|
| AMS-01 to FRA-01 | 7 мс | 0.9 МБ | Достаточно |
| AMS-01 to NYC-01 | 76 мс | 9.5 МБ | Недостаточно |
| AMS-01 to SIN-01 | 168 мс | 21 МБ | Совсем не то |
| AMS-01 to GRU-01 | 196 мс | 24.5 МБ | Совсем не то |
На обеих машинах:
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 вместо алгоритма управления перегрузкой по умолчанию важен на длинных путях при любых потерях, потому что алгоритмы, основанные на потерях, интерпретируют один потерянный пакет как перегрузку и вдвое уменьшают окно. При задержке более двухсот миллисекунд восстановление занимает секунды, и это повторяется многократно.
Измерьте, прежде чем доверить два терабайта предположению:
# target
apt install -y iperf3 && iperf3 -s
# source
iperf3 -c target.example.com -P 8 -t 30Восемь параллельных потоков должны приблизиться к скорости порта. Если один поток даёт сто мегабит, а восемь — восемьсот, значит, настройки окна не подействовали; один поток должен сам по себе достигать почти полной скорости после их применения.
2. Массовое предварительное копирование
Запустите это против работающего источника. Это займёт часы, и за это время ничего не прервётся:
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/Выбор шифра — не суеверие. На этих процессорах аппаратно ускоренный режим GCM перемещает несколько гигабайт в секунду на ядро, а шифр, согласованный по умолчанию в некоторых версиях клиента, заметно медленнее и превращает вашу передачу в однопоточный тест. Сжатие выключено, потому что данные уже сжаты, а если нет, то CPU тратит время на это вместо переноса байтов.
Для миллионов мелких файлов один поток — неправильная форма. Разбейте по каталогам верхнего уровня и запустите несколько:
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/{}/Восемь параллельных копий на восьми выделенных ядрах — правильное число; шестнадцать не в два раза быстрее и сделают исходную машину неприятной для того, что она ещё обслуживает.
3. База данных: репликация, а не копирование
Дамп и восстановление двухсот гигабайт Postgres означают долгое окно, когда записи теряются. Вместо этого стримьте. На источнике:
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На целевом хосте:
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Флаг -R записывает настройки подключения и сигнальный файл standby, так что целевой хост поднимается как реплика и начинает следовать за источником. С этого момента он остаётся отстающим на секунду-другую неопределённо долго, что означает, что при переключении больше не нужно перемещать базу данных.
4. Переключение
Запустите пробу с третьей машины и оставьте её работать. Это одновременно и ваша проверка, и запись того, чего стоило переключение:
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Затем, в этом порядке и без пауз:
# 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"Шаги со второго по пятый занимают несколько секунд. Теперь остановите старый хост от раздачи устаревшего контента, пока распространяется DNS, превратив его в прокси для нового:
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;
}
}Именно эта часть делает всю операцию неразрушающей. Те, кто всё ещё резолвит старый адрес, получают правильный ответ через туннель, а не ошибку или, хуже, копию данных, которая перестала обновляться в момент переключения. Теперь измените DNS-запись.
Проверка
Начните с журнала пробы, который вы собирали:
sort cutover.log | uniq -c | sort -rn | headКаждая строка должна быть 200. Пара более медленных ответов во время переключения ожидаема; любой код, отличный от 200, означает, что пятый шаг начался с большим отставанием от второго, и прокси из предыдущего раздела сокращает этот разрыв до нуля.
Затем докажите, что данные идентичны, а не просто присутствуют:
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"Пробный запуск, который ничего не перечисляет, означает, что два дерева согласованы по содержимому, правам, владельцу и расширенным атрибутам. Общее количество байтов должно совпадать точно.
Далее база данных:
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;"Recovery должен быть false на целевом хосте, и количество строк должно совпадать. Наконец, подтвердите, что трафик действительно переехал, а не тихо проксируется вечно:
dig +short app.example.com
tail -f /var/log/nginx/access.log | wc -l # on the old host, an hour laterКогда старый хост простаивает полное TTL плюс достаточный запас, снимите прокси и уничтожьте инстанс. Не уничтожайте его в тот же день; самая дешёвая страховка во всей процедуре — оставить исходную машину нетронутой и выключенной на неделю.
После
Два терабайта по хорошо настроенному длинному пути — это примерно час на десяти гигабитах и гораздо дольше на практике, поскольку источник также обслуживает запросы. Между соседними европейскими сайтами рассчитывайте на вторую половину дня; между континентами начинайте вечером и делайте переключение на следующий день. Страница локаций содержит времена кругового пути, которые мы измеряем, и именно эти цифры нужно подставить в таблицу в шаге первом.