lenec ru

← все посты

Перенос VPS с одного хостера на другой без даунтайма

18K

Перенос боевого сервиса с одного хостера на другой — задача, которую делают редко, но каждый раз с замиранием сердца. У меня в этом году было два таких переезда: с зарубежного хостера на Selectel и между двумя российскими провайдерами. Расскажу схему, по которой получалось без даунтайма (или с минимальным окном в пару секунд).

Контекст: Linux-сервер с Node-приложением, Postgres, nginx, статикой. На целевой стороне — пустой VPS той же или большей конфигурации.

План на бумаге

Любой переезд я начинаю с плана, который проговариваю вслух. Минимум:

  1. Что переносим: какие сервисы, какие данные, какие cron, какие сертификаты.
  2. Куда: новый IP, домен, DNS-провайдер.
  3. Как: какие шаги в каком порядке.
  4. Что меняем DNS-ом: TTL надо снизить заранее.
  5. Откат: если что-то пошло не так, как возвращаемся на старый сервер.

На листочке это занимает минут 20, и за эти 20 минут обычно вылезает 2–3 пункта, о которых забыл.

Шаг 1: TTL DNS в минимум

За 24–48 часов до миграции снижаю TTL у A-записей на 60–120 секунд. Это значит, что когда я в момент миграции переключу IP, клиенты увидят новый адрес быстро.

Если этого не сделать, TTL обычно 3600 секунд (час) или 86400 (сутки). Разводка трафика в этом случае растягивается, и часть пользователей будет ходить на старый IP до конца окна.

В Cloudflare, Yandex DNS, Selectel DNS этот шаг занимает минуту: открываешь зону, у каждой записи правишь TTL.

Шаг 2: подготовка целевого сервера

На новом хостере поднимаешь VPS, устанавливаешь всё, что нужно: Node, Postgres, nginx, certbot, fail2ban, systemd-юниты твоих сервисов. Не запускаешь приложение с реальным DNS — но всё должно быть готово.

Я для этого держу Ansible-плейбук или просто bash-скрипт, который по чистой Ubuntu разворачивает мою стандартную обвязку. На новом сервере прохожу его, и через 30 минут есть «зеркало» старого окружения.

Шаг 3: первый базовый dump БД

Делаешь полный dump Postgres со старого сервера, копируешь на новый, восстанавливаешь.

ssh old-server 'pg_dumpall -U postgres' | psql -U postgres -h new-server

На больших базах не делай через ssh-pipe — ловишь обрывы. Лучше:

ssh old-server 'pg_dumpall -U postgres > /tmp/dump.sql'
rsync -a old-server:/tmp/dump.sql ./dump.sql
rsync -a ./dump.sql new-server:/tmp/dump.sql
ssh new-server 'psql -U postgres -f /tmp/dump.sql'

На новом сервере БД готова с актуальными данными на момент dump-а.

Шаг 4: репликация изменений

Между моментом dump-а и моментом переключения IP старая БД продолжает принимать записи. Если тупо перенести dump и через час переключить — данные за этот час потеряются.

Варианта два:

Логическая репликация (рекомендую)

На старом сервере поднимаешь логическую публикацию, на новом — подписку. С момента подключения новый сервер получает все изменения старого почти в реальном времени.

-- на старом
ALTER SYSTEM SET wal_level = 'logical';
SELECT pg_reload_conf();
CREATE PUBLICATION migrate_pub FOR ALL TABLES;

-- на новом
CREATE SUBSCRIPTION migrate_sub
  CONNECTION 'host=<old_ip> port=5432 user=replicator password=...'
  PUBLICATION migrate_pub;

Когда статус подписки streaming и нет lag-а — данные синхронизированы. В момент переключения нужно будет только остановить запись на старом, дождаться, пока репликация догонит, и переключить.

Только last-mile перенос

Если логическая репликация невозможна (старый сервер на устаревшей версии Postgres, нет доступа), используется план «остановить запись + быстрый dump в окне». Минус — окно с downtime.

Шаг 5: данные приложения

Файлы пользователей, логи, конфиги — всё, что не БД. Использую rsync с инкрементальным режимом:

# первый полный sync
rsync -avz --progress old-server:/opt/myapp/uploads/ /opt/myapp/uploads/

# периодически догонять
rsync -avz --progress old-server:/opt/myapp/uploads/ /opt/myapp/uploads/

В момент миграции делаешь финальный rsync, и файлы ровно те же.

Шаг 6: поднимаем приложение на новом сервере на тестовом домене

На новом сервере прописываешь nginx с временным доменом (например, new.example.ru), указываешь его на новый IP. Прогоняешь все основные сценарии: логин, регистрация, оплата, отправка письма. Проверяешь, что приложение видит БД, S3, кэш, всё настроено.

Это критический шаг. Не переключай DNS, пока на новом сервере не работает приложение в полном объёме.

Шаг 7: подготовка nginx обоих серверов

Сейчас оба сервера слушают одинаковый домен. Старый — реально, новый — пока на временном домене. Когда переключим DNS, новый начнёт принимать продакшен-трафик.

Сертификаты:

  • На старом сервере остаётся валидный сертификат — он будет жить до момента переключения.
  • На новом нужно либо заранее выпустить сертификат через DNS-01 challenge (тогда HTTP-validation не нужен), либо после переключения DNS быстро прогнать certbot.

Я предпочитаю DNS-01: cert уже валиден на новом сервере, при переключении пользователи увидят правильный сертификат сразу.

Шаг 8: момент истины

Когда всё готово, делаешь миграцию. Порядок строгий:

  1. На старом сервере переводим приложение в read-only (или останавливаем). Запись прекращается.
  2. Ждём, пока логическая репликация Postgres догонит (несколько секунд обычно).
  3. Финальный rsync файлов.
  4. На новом сервере делаем ALTER SUBSCRIPTION migrate_sub DISABLE, чтобы он стал самостоятельным.
  5. Стартуем приложение на новом сервере под продовым доменом.
  6. Меняем A-запись DNS со старого IP на новый.

За счёт низкого TTL пользователи начнут уходить на новый сервер за минуту-две. Все, кто резолвил DNS до переключения, ещё какое-то время будут ходить на старый — он остался в read-only и обслуживает чтение.

Шаг 9: «двойной режим» 24 часа

Старый сервер не выключаю сразу. Держу его 24–48 часов в read-only режиме (или с проксированием на новый), смотрю на:

  • Сколько запросов ещё прилетает на старый.
  • Нет ли запросов, которые требуют записи.
  • Нет ли расхождений в данных.

Когда трафик на старом ушёл в ноль — выключаю. На этом шаге, если что-то с новым сервером пошло не так, можно вернуться: переключить DNS обратно, реактивировать запись, разобраться.

Шаг 10: пост-миграционная проверка

  • Все cron-задачи и BullMQ-воркеры работают на новом сервере.
  • Бэкапы БД крутятся (новый бэкап после переезда — обязательно).
  • Логи и мониторинг показывают данные. Не было упущенного шага «настроить мониторинг».
  • Письма уходят (SMTP, DKIM, SPF — для нового IP).
  • Webhooks от внешних сервисов приходят на новый IP.
  • Сертификаты живые и обновляются.

Подводные камни

SMTP и репутация IP

Если ты сам шлёшь почту с сервера, новый IP не имеет репутации. Письма могут уходить в спам. Решение: либо использовать внешний SMTP-сервис (Postmark, Resend), либо заранее греть IP. На больших объёмах рассылок переезд почтового сервера — это отдельный квест.

SPF/DKIM/DMARC

В DNS-зоне записи SPF могут содержать старый IP. Перед миграцией проверь:

dig TXT example.ru +short
dig TXT _dmarc.example.ru +short

Если в SPF указан конкретный IP старого сервера — обнови до миграции, иначе отправка почты сломается.

Несовместимость версий Postgres

Логическая репликация требует, чтобы версия source была не старше target. Если ты переезжаешь со старого Postgres 13 на новый 17 — это работает. С 17 на 13 — нет, надо сначала апгрейдить старый.

Webhook providers

Платёжные провайдеры, мессенджеры, почтовые сервисы — у всех есть webhook на твой URL. Они кэшируют DNS на разное время. После переезда я ловил «недоставленные webhook» от одного банка ещё пять минут после смены IP, потому что у них кэш был агрессивный.

Сертификаты с фиксированным CN

Если у тебя коммерческий сертификат с конкретным IP в SAN, его придётся перевыпускать. Let's Encrypt такого не делает — не проблема.

WireGuard и VPN-доступ

Если у тебя VPN на этот сервер, IP сменится. Убеди коллег обновить конфиги до миграции, иначе пара часов после переезда они будут стучаться в никуда.

Откат

Если что-то пошло не так, откат через DNS:

  1. Запись DNS возвращаешь на старый IP.
  2. Старый сервер из read-only переводишь обратно в полный режим.
  3. Подписку на новом сервере откатываешь, чтобы данные не разошлись.
  4. Разбираешься, что не так, готовишь план следующей попытки.

Откат при низком TTL занимает 1–5 минут. Главное — не паниковать и иметь чек-лист «как откатываемся», который написан до миграции, а не в момент инцидента.

Шпаргалка

  • За сутки — снизить TTL.
  • За день-два — поднять копию окружения, dump БД, начать репликацию.
  • В день миграции — финальный rsync, остановка записи, переключение DNS.
  • Сразу после — проверка работоспособности, бэкап на новом сервере.
  • Через сутки — выключение старого, если новый стабильно держит нагрузку.

Переезд — задача, которую делаешь одной командой только в учебниках. В реальности она занимает день полноценной работы и пару дней наблюдения. Но если подойти системно, downtime сводится к секундам, которые пользователи даже не замечают.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.