Любой сервер, которым вы управляете, рано или поздно переедет. Хостинг-провайдер поднимает цены, или вдруг просит документ, который вам совсем не хочется отправлять, или получает жалобу на соседа по подсети и на полдня обнуляет весь /24. Вопрос никогда не стоял, мигрировать вам или нет — только в том, случится это тем утром вторника, которое выбрали вы, или той субботней ночью, которую выбрал за вас кто-то другой.
Механика — не самое сложное. Скопировать файлы — это rsync, и это вы уже знаете. Миграции портит именно тайминг: во время переезда одновременно идут три отдельных часовых механизма, они не синхронизированы, и любой настоящий сбой случается как раз в промежутках между ними. Это руководство — про эти промежутки — про DNS TTL, который стоило снизить ещё неделю назад, про базу данных, которую вы скопировали, пока в неё ещё шла запись, про сертификат, существующий только на машине, которую вы только что выключили, и про учётные данные, которые старый хостинг-провайдер мог читать всё это время.
Команды рассчитаны на Debian или Ubuntu на обеих сторонах и на веб-приложение с базой данных, потому что именно это чаще всего и переносят. Nextcloud, игровой сервер, бот или инстанс BTCPay — все они устроены одинаково — отличается только шаг с данными.
Три часовых механизма и почему «нулевой простой» — неверная цель
Миграция — это не одно событие. Это три таймера, идущих на разных скоростях, и всё искусство в том, чтобы не дать им плохо наложиться друг на друга:
- DNS-часы. С момента, когда вы меняете A-запись, резолверы продолжают отвечать старым адресом, пока не истечёт срок жизни закешированной копии. В момент переключения вы этими часами уже не управляете — вы управляли ими раньше, когда задавали TTL, за много дней до этого.
- Часы данных. Последняя копия ваших данных — это снимок одного момента. Всё, что записано после этого момента, живёт только на старой машине и будет потеряно, если вы не воспроизведёте эти изменения заново или не остановите запись вовсе.
- Часы сессий. Загрузки файлов в процессе, открытые WebSocket-соединения, платёжный колбэк, который придёт через сорок секунд от процессора, определившего адрес вашего хоста две минуты назад. Всё это попадёт на ту машину, которую определил отправитель, а не на ту, которую предпочли бы вы.
Погоня за буквально нулевым простоем означает, что все трое часов должны идти одновременно, а на практике это значит держать приложение сразу на двух серверах, пишущих в две базы данных одновременно. Это по-настоящему сложная задача, и решать её ради одного-единственного VPS — неправильный выбор. Честная цель куда более скромная и куда более достижимая:
Ни один посетитель не видит ошибку, и ни одна запись не теряется. Девяностосекундное окно, когда сайт работает, но только для чтения, удовлетворяет обоим условиям, и почти никто этого не заметит. Живое переключение без окна обслуживания, которое тихо теряет последние сорок минут отправленных форм, не удовлетворяет ни одному, и узнаете вы об этом от клиента.
Так что план такой: сделать окно «только для чтения» как можно короче, как можно скучнее и обратимым. Всё, что дальше, служит именно этим трём вещам.
Что остаётся у старого хостинг-провайдера после того, как вы ушли
Эту часть обычно пропускают, а ведь именно она — причина, по которой многие вообще затевают миграцию, так что стоит разобрать её точно. Пока ваш сервер жил на чужом железе, у провайдера была возможность видеть:
- Диск целиком. Если том не был зашифрован через LUKS и не разблокировался только вами, гипервизор мог прочитать в нём каждый байт — ключи, токены, содержимое базы данных, всё. Даже при шифровании в памяти работающей виртуальной машины лежит разблокированный ключ.
- Все учётные данные, которые использовала машина. API-токены в файлах окружения, пароли SMTP, секреты RPC демона кошелька, ваши публичные SSH-ключи и, если вы хоть раз вставляли приватный ключ в консоль восстановления, — кое-что похуже.
- Всё, что требовал аккаунт. Имя, карту, адрес, номер телефона для SMS-подтверждения, IP-адреса, с которых вы входили. Этот набор не уменьшается, когда вы закрываете аккаунт, а в большинстве юрисдикций провайдер обязан хранить часть этого годами.
Ничего зловещего в этом нет — это то, что означает работа виртуальной машины на чужом железе, везде, включая и нас. Важно другое: миграция — единственная по-настоящему чистая точка разрыва, которая у вас есть. Новый сервер начинает с чистых ключей, чистых токенов и чистого IP; если вы перенесёте старые секреты вместе с собой, вы перенесёте и старую степень раскрытости, и разрыв окажется лишь косметическим.
Так что считайте каждый секрет на старой машине скомпрометированным по умолчанию и перевыпускайте его прямо во время переезда. Это стоит один час один раз. Сделать то же самое потом, отдельно, — задача, до которой руки не доходят никогда. И если часть причины переезда в том, что утечкой был сам аккаунт, то оплата нового сервера в Monero на аккаунт без KYC — это шаг, который делает разрыв настоящим, а не символическим — хотя стоит честно понимать, что это даёт, а что нет, а это уже отдельная тема.
TTL-часы запускаются за неделю до переезда
Time to live — это число секунд, в течение которых резолвер имеет право держать вашу запись, прежде чем спросить снова. Если у вашей A-записи TTL по умолчанию равен 3600, то в момент, когда вы её меняете, резолвер, который спросил секунду назад, ещё пятьдесят девять минут пятьдесят девять секунд будет продолжать отправлять посетителей на старый сервер. А при 86400, которые многие регистраторы всё ещё ставят по умолчанию, это превращается в целые сутки.
Проверьте, что стоит на самом деле — а не то, что вам кажется, будто вы настроили:
dig +noall +answer example.com A
dig +noall +answer www.example.com A
dig +noall +answer example.com MX
dig +noall +answer example.com NSВторой столбец в каждом ответе — это TTL с обратным отсчётом. Снизьте у всех записей, участвующих в переезде, TTL до 300 секунд, и сделайте это минимум за время, вдвое превышающее текущий TTL, до самого переключения: если запись стоит на 86400, само изменение будет становиться видимым всем целые сутки, и в этом состоит рекурсивная шутка в самом сердце любой DNS-миграции.
Две детали, на которых обычно ловятся:
- У NS-записей свой собственный TTL, обычно долгий, и задаётся он у регистратора, а не в вашей зоне. Это важно, только если вы заодно меняете серверы имён — а делать это в ту же неделю, что и переезд сервера, не стоит. Смените хостинг, дайте всему устояться, а затем, если хочется, смените и DNS-провайдера. Две переменные — две разные выходные.
- «Распространение DNS» — это не то, что происходит на самом деле. Ничего никуда не распространяется; истекает кеш. Нет ни очереди, в которой можно постоять, ни кнопки, которая протолкнёт обновление быстрее. Единственный рычаг — это TTL, и к моменту переключения он уже нажат.
Пока вы всё равно в файле зоны, выпишите все записи, которые указывают на IP-адрес сервера, а не на имя. Обычно находится на одну больше, чем вы помните: mail, webmail, голый @, старый staging, запись SPF с литералом ip4: внутри, и AAAA-запись, которую вы добавили, когда только получили блок IPv6, а потом благополучно забыли о ней. У каждой из них должен быть план, а AAAA — классический тихий сбой — вы переключаете A-запись, с вашего ноутбука всё выглядит нормально, а каждый посетитель с работающим IPv6 как ни в чём не бывало продолжает попадать на сервер, который вы уже стёрли.
Подбор размера нового сервера и что проверить, прежде чем на нём остановиться
Устойте перед соблазном заказать точно такую же конфигурацию, что у вас сейчас. Вы покупаете исходя из цифр в старом счёте, а не из того, что реально потребляет нагрузка. Сначала потратьте две минуты на замеры:
# peak RAM actually in use, not allocated
free -m
# what has been paging, if anything
vmstat -s | grep -i swap
# real disk consumption, biggest first
du -xh --max-depth=2 / 2>/dev/null | sort -rh | head -20
# load relative to core count
uptime; nprocХорошо работают два простых правила. Если средняя нагрузка (load average) держится ниже числа ядер, а своп ни разу не тронут, значит, сервер не упирается ни в CPU, ни в память, и можно смело брать план вбок или даже поменьше. Если диск занят больше чем на семьдесят процентов, подбирайте новый объём под то, что понадобится через год, а не под то, что используется сегодня — расширение тома потом — это миграция в миниатюре, а вы прямо сейчас как раз делаете одну такую.
У нас это раскладывается чётко: Pup (1 vCPU, 1 GB, 25 GB) тянет статичный сайт, VPN или небольшого бота; Cub (1 vCPU, 2 GB, 40 GB) — самый маленький план, на котором приложение и его собственная база данных уживаются без свопа; Scout (2 vCPU, 4 GB, 70 GB) — комфортный вариант по умолчанию для настоящего сайта с настоящим трафиком; Hunter (4 vCPU, 8 GB, 140 GB) и выше — это уже территория контейнеров, CI или нескольких сервисов на одной машине. Все планы — на NVMe и с безлимитным трафиком, так что перерасход трафика — частая причина переезда сама по себе — просто перестаёт быть переменной. Вся линейка тарифов — здесь.
Прежде чем переносить хоть один байт, проверьте три вещи насчёт машины, которую вам только что выдали. Сейчас каждая из них ничего не стоит, а после переключения обойдётся дорого:
- Репутация IP. Переработанный адрес с чужой историей заставит отклонять вашу почту, а посетителей — проходить лишние проверки. Наши адреса проверяются и сегментируются по риску ещё до выдачи, но теперь на нём именно ваш сервис, так что проверьте сами — пятиминутная версия проверки — здесь. Сделайте это до переключения DNS, пока ответ вам ещё ничего не стоит.
- Обратный DNS. Если сервер вообще будет отправлять почту, PTR-запись должна резолвиться в имя хоста, которое, в свою очередь, резолвится обратно в тот же IP. Кастомный rDNS выдаётся по запросу — закажите его сейчас, чтобы он успел устояться к переключению.
- Маршрут от того места, где находятся ваши пользователи.
mtr, запущенный с машины в регионе ваших пользователей, расскажет о выборе юрисдикции больше, чем любой техпаспорт. Измеренная задержка всегда лучше задержки, которую вы себе просто предположили.
Пошаговая инструкция
- Снизьте все TTL за несколько дней до переезда
Это первый шаг, потому что только у него есть обязательный период ожидания. Всё остальное можно сделать за один вечер; этот шаг поторопить нельзя, и если его пропустить, переключение на пять минут превратится в переключение на два дня.
Зайдите туда, где живёт ваша зона, и выставьте TTL в 300 у каждой записи, которая будет меняться: корневой
A,AAAA,www,mailи всё остальное, что указывает на IP-литерал. Затем проверьте это снаружи, потому что панель управления и реальность иногда расходятся:dig +noall +answer example.com A @1.1.1.1 dig +noall +answer example.com AAAA @1.1.1.1 dig +noall +answer www.example.com A @8.8.8.8Число перед
IN— это оставшийся TTL. Запросите снова через минуту: отсчёт должен идти от 300, а не от чего-то большего. Если отсчёт всё ещё идёт от 3600, значит, старое значение закешировано, и остаётся только подождать — именно поэтому это делают за семь дней до переезда, а не утром в день самого переезда.Держите TTL на 300 всю миграцию и ещё неделю после, чтобы откат тоже был быстрым. Верните значение к чему-то разумному — подойдёт и 3600 — как только старый сервер будет выведен из строя.
- Проведите инвентаризацию старого сервера, прежде чем что-либо копировать
Вы переносите не диск, а работающую систему, а то, что обычно забывают, никогда не лежит в
/var/www. Это cron-задача, которая запускается четвёртого числа каждого месяца, правило файрвола, добавленное во время инцидента, пакет, установленный из стороннего репозитория два года назад. Зафиксируйте состояние машины в виде текста и перенесите этот текст вместе со всем остальным:mkdir -p /root/mig dpkg --get-selections > /root/mig/packages.txt systemctl list-units --type=service --state=running --no-pager > /root/mig/services.txt systemctl list-timers --all --no-pager > /root/mig/timers.txt crontab -l > /root/mig/cron-root.txt 2>/dev/null for u in $(cut -d: -f1 /etc/passwd); do crontab -lu "$u" 2>/dev/null | sed "s/^/[$u] /"; done > /root/mig/cron-users.txt ss -tlnp > /root/mig/listening.txt nft list ruleset > /root/mig/firewall.txt 2>/dev/null || iptables-save > /root/mig/firewall.txt cp -a /etc/hosts /etc/fstab /root/mig/ du -xh --max-depth=2 / 2>/dev/null | sort -rh | head -40 > /root/mig/disk.txtТеперь построчно прочитайте
listening.txtи разберитесь с каждым портом. Этот файл — самый точный ответ на вопрос «а что этот сервер вообще делает», и в нём почти всегда находится сюрприз — экспортёр метрик, забытая копия staging-окружения, база данных, слушающая на публичном интерфейсе, хотя этого никогда не должно было случиться.Заодно составьте список исключений, чтобы первая синхронизация не потратила час на данные, которые вам не нужны:
cat > /root/mig/excludes <<'EOF' /dev /proc /sys /run /tmp /var/tmp /var/cache /var/lib/apt/lists /swapfile /var/lib/docker/overlay2 /var/lib/mysql /var/lib/postgresql **/node_modules **/.cache EOFОбратите внимание, что каталоги баз данных исключены намеренно. У них будет свой собственный шаг, и скопировать их уже здесь — как раз та ошибка, для предотвращения которой существует шаг шесть.
- Разверните новый сервер и защитите его прежде, чем на нём появится что-либо важное
Закажите сервер, выберите локацию и по возможности возьмите ту же основную версию ОС, что и на старой машине. Если менять хостинг и переходить с Debian 12 на Debian 13 одновременно, то когда что-то сломается, вы не поймёте, какое из изменений тому виной. Сначала переезд, обновление — потом.
Прежде чем на сервер попадёт что-то важное, проведите десятиминутную защиту: SSH только по ключу, вход под root отключён, файрвол по умолчанию всё запрещает, автоматические обновления безопасности включены. Сейчас это займёт десять минут, а встраивать это в уже работающий сервис потом — по-настоящему муторно.
Затем создайте на старом сервере ключ, который существует только для этой миграции, чтобы отзыв доступа к миграции впоследствии никогда не означал вмешательства в ваш собственный логин:
# on the OLD server ssh-keygen -t ed25519 -f /root/.ssh/id_migrate -C 'migration' -N '' cat /root/.ssh/id_migrate.pubПоместите этот публичный ключ в
/root/.ssh/authorized_keysна новом сервере, а затем убедитесь в направлении передачи — со старого на новый, чтобы учётные данные жили на машине, которую вы покидаете, и умерли вместе с ней:ssh -i /root/.ssh/id_migrate root@NEW_IP 'hostname; df -h /; free -m'Наконец, впишите новый IP в
/etc/hostsстарого сервера под именем вродеnewbox. Каждая последующая команда станет короче и, что куда полезнее, её станет труднее случайно направить не на ту машину в час ночи. - Проверьте новый IP, прежде чем доверить ему хоть что-то
Теперь у вас есть адрес, который раньше никогда не использовался для вашего сервиса, и это последний момент, когда найти с ним проблему ничего не стоит. Три проверки, пять минут:
Чёрные списки. Прогоните адрес через проверку по множеству RBL — полная процедура и то, как читать результаты, — здесь. Попадание в политический список вроде Spamhaus PBL — нормально для адреса дата-центра и почти ничего не значит для веб-трафика. А вот попадание в SBL или XBL — уже серьёзно, и поднимать вопрос нужно сейчас, а не после того, как на этот адрес перейдут ваши пользователи.
Обратный DNS. Проверьте, чем адрес отвечает сегодня:
dig +short -x NEW_IPЕсли сервер будет отправлять почту, закажите нужную вам PTR-запись и убедитесь, что она указывает на имя хоста, чья A-запись, в свою очередь, указывает обратно на тот же IP. Прямая и обратная записи должны совпадать; несовпадение хуже, чем универсальное имя.
Доступность и маршрут. Убедитесь, что нужные вам порты действительно открыты сквозным образом, снаружи, а не полагайтесь на то, что ваш файрвол — единственное, что стоит на пути:
# from a third machine, or your laptop nc -vz NEW_IP 22 mtr -rwc 20 NEW_IPВывод
mtrстоит того, чтобы его сохранить. Потери на последнем хопе важны; потери на промежуточном хопе обычно означают, что маршрутизатор занижает приоритет ICMP, и ничего не значат. Если задержка из региона ваших пользователей заметно хуже, чем у старого хостинга, лучше узнать об этом сейчас, пока передумать стоит один заказ и никаких данных. - Скопируйте файловую систему через rsync — и сначала сделайте пробный прогон
Теперь — основной перенос. Сделайте это в два прохода: сначала полная копия заранее, сколько бы времени это ни заняло, а затем короткие дельта-проходы позже, которые переносят только изменения. Всегда сначала запускайте пробный прогон (dry-run) — вывод покажет ровно то, что вот-вот произойдёт, и разовое чтение этого вывода спасло больше миграций, чем любая другая привычка из этого руководства.
# on the OLD server, dry run rsync -aHAXx --numeric-ids --info=progress2 --dry-run \ --exclude-from=/root/mig/excludes \ -e 'ssh -i /root/.ssh/id_migrate' \ /var/www/ root@newbox:/var/www/Затем — та же команда без
--dry-run. Повторите это для каждого важного каталога:/etcвыборочно, а не целиком,/home,/srv,/optи там, где ваше приложение реально хранит загруженные файлы.Каждый флаг здесь не просто так.
-aсохраняет права, владельца, время изменения и симлинки;-Hсохраняет жёсткие ссылки;-Aи-Xпереносят ACL и расширенные атрибуты, а это нужно вашему каталогу загрузок, если на него хоть раз их устанавливали;-xне даёт rsync уходить в другие смонтированные файловые системы.--numeric-ids— это тот флаг, который часто забывают, а потом жалеют: без него rsync сопоставляет владельца по имени, и если уwww-dataразные UID на двух серверах, каждый файл прибудет с неправильным владельцем, а разбираться с этим потом — занятие муторное.Два предупреждения. Копирование
/etcцеликом на работающую систему перезапишет сетевые настройки новой машины, её fstab и её SSH host keys — переносите конкретные нужные вам настройки, а не весь каталог. И приберегите--deleteтолько для финального прохода: он уместен, когда нужно, чтобы новый сервер в точности совпал со старым, и разрушителен, если на приёмнике уже что-то создано.Когда первый полный проход завершится, сравните обе стороны, чтобы убедиться, что перенос сделал именно то, что вы ожидали:
du -sh /var/www ssh -i /root/.ssh/id_migrate root@newbox 'du -sh /var/www' - Перенесите базу данных дампом, а не копией файлов
Это тот шаг, который решает, будет ли ваша миграция скучной или запоминающейся. Каталог данных базы согласован только тогда, когда сервер остановлен. Скопируйте его вживую — и получите набор файлов, которые выглядят нормально, переносятся нормально, а восстанавливаются в базу данных, которая незаметно и необратимо повреждена — часто без единой ошибки, вплоть до недель спустя.
Снимайте дамп как положено. Для MySQL или MariaDB именно
--single-transactionдаёт согласованный снимок, не блокируя всё целиком:mysqldump --single-transaction --quick --routines --triggers --events \ --default-character-set=utf8mb4 --databases appdb \ | zstd -T0 > /root/mig/appdb.sql.zstДля PostgreSQL стоит использовать собственный формат (custom format) — он сжимает данные и позволяет восстанавливать выборочно, если понадобится:
pg_dump -Fc -Z6 appdb > /root/mig/appdb.dumpПеренесите файл и восстановите:
scp -i /root/.ssh/id_migrate /root/mig/appdb.sql.zst root@newbox:/root/ # on the NEW server zstd -dc /root/appdb.sql.zst | mysql # postgres equivalent pg_restore -d appdb -j4 /root/appdb.dumpЗатем проверьте, потому что «восстановление завершилось» и «данные на месте» — это разные утверждения. Сравните количество строк в важных таблицах на обеих сторонах:
mysql -N -e "SELECT COUNT(*) FROM appdb.orders; SELECT COUNT(*) FROM appdb.users;"В дампах прячутся две вещи: кодировка и сравнение (collation) — именно отсюда берутся испорченные буквы с диакритическими знаками — если старая база работает на
utf8, а не наutf8mb4, осознанно решите, не пора ли заодно это исправить; и пользователи базы данных и их права, которыеmysqldump --databasesне переносит. Явно пересоздайте пользователя и пароль приложения на новом сервере, а затем не забудьте, что строка подключения в конфигурации должна им соответствовать.Если ваше приложение использует SQLite, файл и есть база данных, и здесь действует то же правило — не копируйте его вживую. Используйте
sqlite3 app.db ".backup /root/mig/app.db"— это безопасно снимает согласованную копию. - Поднимите стек и отрепетируйте всё через подмену в hosts-файле
На новом сервере теперь есть и файлы, и данные. Запустите всё и протестируйте под настоящим именем хоста — пока весь остальной мир по-прежнему спокойно пользуется старым сервером. Это самый ценный приём во всей процедуре, и стоит он одну строку.
На своём собственном ноутбуке добавьте IP нового сервера в
/etc/hosts(илиC:\Windows\System32\drivers\etc\hosts):203.0.113.10 example.com www.example.comТеперь ваш браузер резолвит настоящий домен в новый сервер, и больше ничей — нет. Каждый URL корректен, каждый домен cookie совпадает, каждый редирект и callback-путь ведёт себя так же, как и после переключения — и это именно то, что тестирование на временном поддомене вроде
new.example.comникогда не поймает, потому что добрая половина того, что ломается при миграции, зависит от имени хоста.Для одной быстрой проверки без правки чего-либо, то же самое умеет
curl, прямо инлайн:curl -sSI --resolve example.com:443:203.0.113.10 https://example.com/ curl -sS --resolve example.com:80:203.0.113.10 http://example.com/ -o /dev/null -w '%{http_code}\n'Пройдитесь по всему приложению, а не только по главной странице: войдите, отправьте форму, загрузите файл, вызовите письмо сброса пароля, откройте админ-панель, дёрните endpoint, который вызывает платёжный процессор. Затем прочитайте логи ошибок, даже если всё выглядело нормально — отсутствующие расширения PHP, неверные права на каталог кеша и ещё не существующий пользователь базы данных — всё это проявится там раньше, чем на экране.
Не забудьте потом убрать строку из hosts. Каждый забывает это хотя бы раз, а потом двадцать минут гадает, почему откат как будто не сработал.
- Настройте TLS на новом сервере, прежде чем что-либо переключать
Сертификаты привязаны к именам, а не к IP-адресам, так что сама по себе миграция не делает недействительным уже имеющийся у вас сертификат. Ломается именно выпуск: обычная проверка HTTP-01 просит Let's Encrypt забрать файл по порту 80 с того самого имени, которое сертифицируется, а это имя всё ещё указывает на старый сервер. Вся сложность — в этой курице и яйце, и есть три чистых способа из неё выбраться.
Скопировать уже имеющиеся сертификаты. Проще всего и обычно правильно. Приватный ключ и цепочка переносятся как любой другой файл:
rsync -aHAX -e 'ssh -i /root/.ssh/id_migrate' \ /etc/letsencrypt/ root@newbox:/etc/letsencrypt/Новый сервер сразу же сможет отдавать действительный TLS, а продление заработает само по себе, как только на него укажет DNS. Убедитесь, что там включён таймер:
systemctl list-timers | grep certbot.Использовать проверку DNS-01. Доказывает контроль над доменом через TXT-запись вместо HTTP-запроса, так что работает и с сервера, на который пока никто не указывает. Идеально, если нужен по-настоящему независимый сертификат на новом сервере ещё до переключения, и единственный вариант для wildcard-сертификата.
Выпустить уже после переключения. Рабочий вариант, но он оставляет промежуток, когда сайт уже живёт на новом IP без сертификата, а для всего, что использует HSTS, это не предупреждение, которое посетитель может кликнуть и пропустить. Разумно только для совсем нового домена, на который ещё никто не заходил.
Что бы вы ни выбрали, проверьте это напрямую по новому IP ещё до переключения, тем же приёмом с
--resolve:curl -sSI --resolve example.com:443:203.0.113.10 https://example.com/ | head -1 echo | openssl s_client -connect 203.0.113.10:443 -servername example.com 2>/dev/null \ | openssl x509 -noout -subject -datesПроверьте даты и то, что subject покрывает все обслуживаемые вами имена, включая
www. А если вы используете HSTS с долгим max-age, относитесь к сертификату как к единственной вещи, которая обязана быть в порядке ещё до переключения, а не после — этот заголовок — обещание, которое вы уже дали каждому вернувшемуся посетителю. - Переключение: заморозьте записи, финальная дельта, смена A-записи
Десять минут реальной работы — и единственная часть всего процесса, где тикают часы. Делайте это утром, в своём часовом поясе, в день, когда вы больше ничем не заняты. Никогда — в пятницу.
Заморозка. Переведите старое приложение в режим обслуживания или «только для чтения». Остановите воркеры, обработчики очередей и cron-задачи — всё, что пишет данные без участия браузера. С этого момента на старом сервере больше ничего не записывается, и именно это делает всё, что дальше, безопасным:
# on the OLD server systemctl stop app-worker.service systemctl stop cron touch /var/www/maintenance.flagФинальная дельта. Ещё один rsync, теперь уже с
--delete, чтобы приёмник в точности совпал с источником, и ещё один дамп. Раз первый проход был сделан несколько дней назад, этот переносит совсем немного и занимает секунды:rsync -aHAXx --numeric-ids --delete --exclude-from=/root/mig/excludes \ -e 'ssh -i /root/.ssh/id_migrate' /var/www/ root@newbox:/var/www/ mysqldump --single-transaction --quick --routines --triggers --events \ --default-character-set=utf8mb4 --databases appdb | zstd -T0 \ | ssh -i /root/.ssh/id_migrate root@newbox 'zstd -dc | mysql'Проверка. Количество строк на обеих сторонах, ещё один проход по приложению через подмену в hosts-файле, и снимите флаг обслуживания с нового сервера.
Переключение. Смените
A-запись на новый IP. Смените иAAAA-запись тоже — это самая частая причина, по которой переключение срабатывает только наполовину. Проверьте через резолвер, который вы не контролируете:dig +short example.com A @1.1.1.1 dig +short example.com AAAA @1.1.1.1Затем следите за обеими машинами. Трафик должен появиться на новой в течение минуты-двух и сойти на нет на старой в течение следующих пяти:
tail -f /var/log/nginx/access.log # on both, side by sideОставьте старое приложение в режиме обслуживания, а не выключайте его. Отставший запрос, который до него всё же доберётся, увидит вежливую страницу вместо ошибки соединения, а у вас останется доступная машина для отката, который вам, скорее всего, не понадобится.
- Понаблюдайте неделю, а затем правильно выведите старый сервер из эксплуатации
Миграция не заканчивается в момент, когда резолвится DNS. Она заканчивается, когда проходит полный цикл биллинга и cron-задач без единого сюрприза.
В первые часы следите за уровнем ошибок, а не за аптаймом — сервер может быть полностью доступен и при этом отдавать 500 на треть запросов:
journalctl -u nginx -u php8.4-fpm -f awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | headЗатем пройдитесь по короткому списку — вот что на самом деле ломается после переезда, и ничто из этого не заявляет о себе само: cron-задачи и systemd-таймеры, работающие на новом сервере (сверьте
systemctl list-timersс инвентаризацией из шага два); исходящая почта доходит и не попадает в спам; запланированные бэкапы указывают на новый сервер, а не всё ещё архивируют старый; любая третья сторона с вашим IP-адресом в allowlist — платёжный процессор, API, файрвол партнёра; и ежемесячная задача, которая доказывает свою работоспособность только первого числа.Оставьте старый сервер включённым, в режиме обслуживания, нетронутым, как минимум на неделю. Это ваш откат и ваша эталонная копия, и она стоит больше тех нескольких долларов, в которые обходится. Затем закройте вопрос по порядку: смените все секреты, которые он мог прочитать, сделайте один финальный зашифрованный архив на третью площадку, перезапишите каталоги приложения и базы данных, запустите провайдерскую процедуру уничтожения или переустановки, и только потом отменяйте подписку — предварительно проверив дату продления, потому что лишний месяц страховки на случай отката обычно более выгодный размен.
И наконец, верните TTL обратно к 3600, раз вам больше не нужна пятиминутная обратимость, и обновите документацию, которую вы ведёте, новым адресом. Вы же сами, но позже, в какие-нибудь два часа ночи, будете очень благодарны, что руководство совпадает с реальным сервером.
Почта не переезжает вместе со всем остальным
Если старый сервер отправлял почту — хотя бы просто письма для сброса пароля — считайте это второй, более медленной миграцией, идущей параллельно с первой. Доставляемость — это репутационная система, а репутация привязана к IP-адресу и домену, а не к софту, который вы перенесли.
Четыре записи решают, прочитают вашу почту или отбросят, и в трёх из них есть то, что меняется вместе с сервером:
- SPF перечисляет, кому разрешено отправлять почту от имени вашего домена. Если в вашей записи есть литерал
ip4:, теперь он неверен. Добавьте новый IP до переключения и уберите старый спустя неделю после — несколько дней с двумя разрешёнными адресами ничего не стоят и перекрывают переходный период. - DKIM подписывает письмо приватным ключом. Перенесите ключ вместе с остальной конфигурацией — и селектор продолжит работать как ни в чём не бывало. Сгенерируете новый ключ — придётся публиковать новый селектор и ждать, пока он разойдётся, так что переносите старый, если только нет особой причины поступить иначе.
- DMARC сообщает получателям, что делать, если первые две проверки провалились. Если у вас стоит
p=reject, сломанный на время окна SPF — это не предупреждение, это тихое удаление письма. На неделю миграции стоит рассмотреть переход наp=none, а потом вернуть как было. - PTR — это та самая обратная запись, о которой шла речь выше. У нового IP она универсальная, пока вы не запросите свою, а несколько крупных провайдеров попросту отказываются принимать почту с универсальных обратных имён.
Честные ожидания таковы: у совершенно нового IP репутации нет вообще никакой, а это не то же самое, что хорошая репутация. Объём отправки наращивается днями, а не часами. Если почта для вас критична, держите старый сервер живым и отправляющим ещё неделю после переезда, вместо того чтобы переключаться одним махом — а если критична для бизнеса, то выделенный релей — решение получше, чем любая из двух машин.
Как откатиться, не сделав хуже
Смысл плана отката не в том, что вы рассчитываете его применить. А в том, что его наличие позволяет переключаться спокойно в 10 утра, а не нервно в 2 ночи, а спокойствие — вот что на самом деле предотвращает ошибки.
Ваш откат прост, и он остаётся простым ровно до тех пор, пока старая база данных всё ещё главная: верните A-запись обратно. При TTL в 300 секунд вы восстанавливаетесь за пять минут. Это окно — между переключением и первой записью, существующей только на новом сервере — и есть ваша бесплатная отмена, и именно поэтому старый сервер остаётся включённым, нетронутым и нестёртым как минимум неделю.
Всё портит split-brain: записи попадают сразу на обе машины. Теперь ни одна из баз данных не верна, а сверять их вручную хуже, чем любой простой, которого вы пытались избежать. Есть три привычки, которые предотвращают это полностью:
- Переведите старое приложение в режим только для чтения или обслуживания в самом начале окна, вместо того чтобы полагаться на DNS в том, что трафик уже не идёт. DNS — это подсказка; остановленный сервис — это факт.
- После переключения следите за логом доступа старого сервера, а не нового. Запросы, которые всё ещё туда приходят, — это ваши отстающие клиенты, и когда этот ручеёк иссякнет, миграция действительно закончится.
tail -f /var/log/nginx/access.log— весь необходимый инструмент. - Как только вы приняли первую настоящую запись на новом сервере, откат — это уже не смена DNS — это восстановление из бэкапа. Решайте осознанно, когда пересекаете эту черту, и проговаривайте это вслух, если вас двое.
Ещё до начала установите себе стоп-правило: если новый стек не заработал корректно, скажем, за тридцать минут, вы возвращаете запись обратно, забираете вечер назад и чините всё уже без тикающих часов. Миграции идут плохо, когда люди продолжают двигаться вперёд только потому, что откат ощущается как поражение. Это не поражение; это дешёвый вариант, доступный строго ограниченное время.
Вывод из эксплуатации: сменить ключи, стереть, проверить, и только потом отменить
Через неделю после переключения старый сервер — это полная, работающая, никем не присматриваемая копия ваших данных на инфраструктуре, на которую вы перестали обращать внимание. К тому же на этот момент это наименее пропатченная машина из всех, что у вас есть. Доведите дело до конца в таком порядке:
- Смените всё, что мог прочитать старый сервер. Секреты приложения, API-токены, пароли от баз данных, учётные данные SMTP, секреты подписи вебхуков, любые пароли RPC кошелька. Считайте, что всё это уже раскрыто — доказать обратное вы не можете. Если для миграции вы сгенерировали отдельный SSH-ключ, самое время убрать старые авторизованные ключи.
- Сделайте один финальный архив — зашифрованный снапшот restic или Borg на площадку, которая не является ни одной из этих двух машин. Он понадобится вам ровно один раз, шесть недель спустя, ради файла, о котором никто уже не помнил.
- Перезапишите данные. На VPS вы не можете проверить физический носитель, так что делайте что можете:
shredили перезапишите каталоги приложения и базы данных, а затем запустите провайдерскую процедуру переустановки или уничтожения. Именно шифрование данных на диске с самого начала делает это дешёвым; без него вы полагаетесь на чужую политику удаления. - Проверьте, и только потом отменяйте. Убедитесь, что новый сервер отдавал вообще всё в течение полной недели, включая ежемесячный cron, о котором никто не вспоминает, и что на старом сервере уже ничего не резолвится. Только потом отменяйте — и сначала проверьте дату продления, потому что заплатить за лишний месяц страховки на случай отката часто разумнее, чем сэкономить восемь долларов.
Сам аккаунт удаляйте в последнюю очередь, и только когда вы полностью уверены. Именно там обычно живут тикеты поддержки, счета и какой-нибудь забытый субсервис, а аккаунт, в который вы уже не можете войти, — неудобное место для того, чтобы обнаружить DNS-запись, которую вы забыли перенести.
Расписание, которое работает
Растянутое на неделю, всё это не вызывает стресса. Сжатое в один вечер — вызывает, и ещё как.
- День −7. Снизьте все TTL до 300. Проведите инвентаризацию старого сервера. Закажите новый и проверьте его IP, его rDNS и его маршрут.
- День −5. Защитите новый сервер. Установите стек. Первый полный
rsync, и он самый медленный — каждый последующий проход переносит только дельту. - День −3. Восстановите дамп базы данных на новом сервере и поднимите приложение. Протестируйте всё целиком через подмену в hosts-файле. Почините всё, что сломано, пока на кону ничего нет — а что-нибудь да сломается.
- День −1. Выпустите TLS на новом сервере. Проверьте цепочку сертификатов и, если вы используете HSTS, убедитесь, что он не превратит маленькую ошибку в невозможность даже кликнуть дальше. Добавьте новый IP в SPF. Ещё раз проверьте, что все TTL действительно снизились.
- День 0, утро. Режим обслуживания на старом сервере. Финальная дельта-синхронизация. Финальный дамп и восстановление. Проверка количества строк. Переключите A-запись — и AAAA. Следите за обоими логами доступа.
- Дни +1…+7. Старый сервер остаётся включённым, нетронутым. Следите за логами, почтой и репутацией нового IP. По возможности дайте ежемесячным задачам отработать хотя бы раз.
- День +7. Смените секреты, сделайте финальный архив, сотрите данные, проверьте, отмените подписку.
Единственный лучший предсказатель скучной миграции — то, что шаг один случился за неделю до шага пять. Почти всё, что идёт не так при переезде сервера, — это TTL, который в одиннадцать вечера всё ещё равен 86400.
Часто задаваемые вопросы
Может ли миграция VPS вообще пройти без простоя?
Буквально нулевой простой, с постоянным приёмом записей на обеих машинах, требует репликации и общей или кластерной базы данных — это по-настоящему сложная задача, и решать её ради одного сервера неправильно. Чего можно надёжно достичь — это того, что ни один посетитель не увидит ошибку и ни одна запись не потеряется: короткое окно «только для чтения» во время финальной синхронизации, при TTL DNS, уже сниженном до 300 секунд, так что само переключение займёт минуты. На практике это одна-пять минут страницы обслуживания, что для почти любого сайта неотличимо от нуля и куда безопаснее альтернативы.
Сколько на самом деле занимает распространение DNS?
Никакого распространения не существует. Ничего никуда не проталкивается — резолверы просто кешируют вашу запись на число секунд, указанное в TTL, и спрашивают заново, когда срок истекает. Так что честный ответ — «столько, сколько действовал TTL на момент изменения». При TTL в 300 секунд, выставленном неделей раньше, практически все окажутся на новом адресе в течение пяти минут. При 86400, которые многие регистраторы всё ещё ставят по умолчанию, некоторые резолверы будут слать трафик на старый сервер целые сутки. Именно поэтому снижение TTL — это шаг первый, а не шаг девятый.
Можно ли сохранить свой IP-адрес при смене хостинга?
Только если вы сами владеете адресным пространством и можете анонсировать его через нового провайдера, а это означает членство в RIPE или ARIN с собственной аллокацией — реалистично для компании, но не для одного сервера. Для всех остальных новый хостинг означает новый IP, и именно поэтому его репутацию и обратный DNS нужно проверять до переключения, а не после. У нас адреса проверяются по Spamhaus и ещё более чем сотне других списков и выдаются из сегментированных по риску пулов, а не достаются по наследству от предыдущего владельца, но самостоятельная проверка занимает пять минут и всегда того стоит.
Может, стоит вместо этого использовать образ диска или инструмент миграции от провайдера?
Если с обеих сторон один и тот же провайдер и один и тот же гипервизор, восстановление из образа — нормальный вариант, и заметно более быстрый. Между разными провайдерами это обычно ловушка: образ несёт с собой сетевые настройки старой машины, модули ядра, драйверы и предположения о железе, и вы проведёте вечер, отлаживая систему, которая загружается в машину, которой больше не существует. Чистая установка плюс продуманный перенос данных и конфигурации даёт вам сервер, который вы понимаете, и избавляет от накопившегося на старом мусора. Миграция — это самая дешёвая возможность оставить что-то позади, какая вам вообще представится.
А что если база данных, в которую пишут постоянно?
Два варианта, по возрастанию сложности. Простой — окно «только для чтения» из шага девять: дамп с --single-transaction у активной, но обычной по размеру базы занимает от секунд до пары минут, и страница обслуживания на такое время — честный размен. Основательный вариант — репликация — настройте новый сервер как реплику заранее, за несколько дней, дайте ей оставаться синхронизированной, а затем повысьте её до основной прямо в окне переключения. Это сокращает заморозку до секунд, ценой заметно более сложной настройки. Выбирайте второй вариант, только если первый неприемлем, и никогда не импровизируйте его в ночь переезда.
Повредит ли смена хостинга позициям сайта в поиске?
Сама по себе — нет. Google индексирует имена хостов, а не IP-адреса, и переезд, при котором URL и контент остаются прежними, по сути незаметен. А вот что действительно стоит вам позиций — это то, что обычно сопровождает неудачную миграцию: страницы, отдающие 5xx, пока их посещают краулеры, ошибка сертификата, robots.txt, случайно приехавший из staging-копии с Disallow: / внутри, или редиректы, которые незаметно поменяли форму. Сразу после переключения проверьте robots.txt, канонические теги и горстку настоящих URL. Если сайт отдаёт на новом IP те же самые ответы, что и на старом, восстанавливаться не от чего.
Будут ли мои TLS-сертификаты работать на новом сервере?
Да — сертификаты выпускаются на доменные имена, а не на IP-адреса, так что перенос /etc/letsencrypt сразу же даёт действительный сертификат на новом сервере. Сложность не в действительности, а в продлении: проверке HTTP-01 нужно, чтобы домен указывал на ту же машину, которая продлевает сертификат, так что до переключения DNS продление там будет проваливаться. Скопируйте сертификаты до переключения, переключитесь, а затем убедитесь, что таймер продления успешно отрабатывает на новом сервере. Если нужен по-настоящему независимый сертификат ещё до переключения — или wildcard — используйте вместо этого проверку DNS-01.

