П/ВИН

Проверка доступности сайта из разных стран после переезда на Hetzner Helsinki

·9 мин чтения

Полтора месяца назад я начал переезд всей инфраструктуры с латвийского VPS на Hetzner в Хельсинки: 22 продакшен-приложения, 5 публичных API, мониторинг, бэкапы и пачка Telegram-ботов уехали в Финляндию. Сегодня финальный шаг - пройтись по чеклисту независимого аудита, убедиться что всё переехало нормально, и нажать кнопку «выключить старый сервер навсегда». А заодно мне нужна честная проверка доступности сайта из разных стран и регионов РФ: как мой реальный Next.js рендерится из Москвы, Питера, Новосиба и других городов после переезда в Хельсинки. Спойлер: старый сервер уже мёртв, всё работает, тесты пройдены. Но интересные сюрпризы по дороге всё-таки случились.

Зачем вообще переезжать

Латвийский провайдер был неплох: дешёвый, стабильный, без сюрпризов. Но за полгода эксплуатации накопились три причины, которые в сумме перевесили любую инерцию. Во-первых, ресурсов перестало хватать — 5.8 GB RAM, из которых 3.9 GB постоянно занято, плюс 1.4 GB активного свопа. Twenty CRM, Supabase, parser-hub, lobechat и десяток мелких сервисов делили память так плотно, что любой пик трафика превращался в OOM-killer-лотерею. Во-вторых, я хотел консолидации: все мои pet-проекты разбросаны по трём провайдерам, бэкапы делаются вручную, секреты хранятся в трёх разных .env-папках. В-третьих, Hetzner предлагал dedicated-сервер EX44 с Ryzen 9 3900 (12 cores), 128 GB ECC RAM и 2×1.92 TB NVMe RAID-1 за цену чуть выше моего латвийского VPS. Это в десять раз больше памяти и в двадцать раз больше диска.

Переезд я делал постепенно, app-by-app, через Dokploy — self-hosted PaaS на замену Vercel, который с июня 2025 заблокирован в России. Архитектура получилась двухслойной: Caddy на хосте обрабатывает 80/443 и SSL для всех доменов, проксирует по Host-header в Dokploy Traefik на внутреннем порту, а тот уже раздаёт трафик в Docker Swarm services. CI/CD собран на GitHub Actions + GHCR (Container Registry), с финальным шагом через SSH — потому что родной application.deploy Dokploy не делает force-pull для тега :latest и Swarm переиспользует закэшированный образ.

Чеклист аудита: 10 пунктов

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

1. DNS. 11 публичных доменов. Десять должны указывать на Hetzner, один — all.pashavin.ru — намеренно остаётся на старом фронте по другому маршруту. Всё совпало.

2. HTTPS. Прохожу curl -Is по всем доменам, ожидаю 200/307/401/404 — нормальные ответы. Никаких 5xx, таймаутов или ошибок SSL. Все 11 доменов отвечают корректно.

3. System services. На Hetzner крутится 17 systemd-сервисов: anti-api, crm-messenger, follower-check (с туннелем), пара рендереров, headless-API и прочее. Все в статусе active.

4. User services. После переезда обнаружил приятный сюрприз: дубликат kp-renderer в user-режиме (~/.config/systemd/user/), который тихо конкурировал с system-версией. Удалил, обновил документацию миграции.

5. Docker. 24 контейнера в running, ноль в restarting.

6. Gatus. Self-hosted мониторинг от TwiN. Расширил конфиг до 12 endpoints: добавил группу monitoring (mon-api, beszel, gatus self-check, logs), все 12 проверок зелёные на первом тике.

7. Telegram-боты. Проверил логи на 409-conflict ошибки за последние 30 минут — это типичный симптом, когда два инстанса бота конкурируют за long-poll. Ноль конфликтов значит, что старый сервер уже никого не забирает.

8. RAID. mdadm --detail показывает три зеркала в состоянии [UU] — оба диска живые.

9. Disk. 116 GB занято из 1.8 TB — 7%. Запас на годы вперёд.

10. WireGuard. Туннели до парсер-хабов и follower-check сервиса работают, пинг проходит.

Когда я нажал «отключить» на латвийском VPS, ping сразу показал 100% packet loss. Сервер мёртв. Всё, что должно было переехать — переехало.

Тесты доступности из России

Переезд в Финляндию — это всегда вопрос: как мой сайт работает из России? Hetzner Helsinki географически ближе к Москве, чем условный Frankfurt, но политически — это тот же EU. Я хотел получить реальную карту: с каких провайдеров и регионов сайт грузится быстро, где может быть деградация, где timeout. Идеально — без бюджета.

Pipeline A: Globalping

Первая находка — Globalping от jsDelivr. Open-source, REST API, 100 RU-проб в семи городах: Москва, Питер, Новосибирск, Таганрог, Краснодар, Казань, Уфа. Free-тариф — 250 тестов в час без регистрации. Поддерживает HTTP/ping/traceroute/DNS, выдаёт TTFB и status code. Главный минус — это не браузерный тест, чисто HTTP-уровень. Но для проверки «дойдёт ли пакет и за сколько» этого достаточно.

API элементарный: POST с описанием measurement (target, type, locations), затем polling до получения результатов. Один нюанс — anonymous tier ограничен 50 пробами на запрос, не 100, как написано в документации. Узнал я это, разумеется, методом 400 Bad Request.

Pipeline B: Playwright + free proxies

Для браузерного теста — Playwright Chromium headless через бесплатные RU-прокси из public-листов. Идея: для каждого живого прокси открыть https://test.pashavin.ru/, замерить FCP, LCP, DOM Content Loaded, console errors. Реалистично-выглядящая картина того, как пользователь у Билайна или МТС видит сайт.

Реальность ударила в лицо: 16 alive HTTP-прокси из листа, и ни один не поддерживает CONNECT-метод для HTTPS. Все дают ERR_EMPTY_RESPONSE на TLS-handshake. Это нормально для бесплатных публичных прокси — они расчитаны на скрапинг плоского HTTP, а не на туннелирование зашифрованного трафика. Результат через прокси оказался нерепрезентативным, но это сам по себе data point: free RU-proxies для production-класса HTTPS-тестов не годятся, нужны residential-прокси или платные API.

Тестовая площадка: test.pashavin.ru

Чтобы прогон был честным, я развернул на Hetzner реальный Next.js — feberra-landing-page, статичный лендинг, который умеет рендерить SSR. Pull образа с KZ-сервера через SSH-pipe (227 MB), запуск через docker run на внутреннем порту, добавление Caddy-блока:

test.pashavin.ru {
    reverse_proxy 127.0.0.1:3099
    header Server-Timing "app;desc=feberra-test"
}

SSL Caddy получил автоматически через Let's Encrypt. Локально — 200 OK на 31 мс. Перехожу к замерам.

Результаты прогона

Globalping выдал 49 успешных проверок из 50 (одна Penza с ER-Telecom/Dom.ru ушла в timeout). Распределение по городам:

ГородOKAvg TTFB
Москва27/2736 ms
Санкт-Петербург11/1121 ms
Новосибирск5/597 ms
Казань2/255 ms
Краснодар2/248 ms
Уфа1/139 ms
Таганрог1/156 ms
Пенза0/1timeout

21 мс TTFB из Питера до Хельсинки — это лучше, чем многие сайты у крупных VPS внутри России. 36 мс из Москвы — на уровне локальной сети. p95 latency по всем 49 пробам — 178 мс.

Playwright локально (Hetzner→Hetzner) показал TTFB 46 мс, FCP 200 мс, networkidle 877 мс, ноль console errors, ноль failed requests. Реальный Next.js, реальный SSL, реальная отдача.

Дополнительно я прогнал check-host.net на тот же URL — 3/3 RU-нод OK (Moscow 208 мс, СПб 96 мс, Ekb 242 мс), ping из Питера 9 мс. Все три независимых инструмента сходятся: сайт из России грузится корректно и быстро.

Один сюрприз: Penza timeout

Единственный фейл — нода в Пензе на ER-Telecom/Dom.ru. Это не блокировка, скорее routing issue или peering-проблема между конкретным региональным ISP и Hetzner. Воспроизвести стабильно я не смог: повторные прогоны показывали то OK, то timeout. Решил не паниковать — 1 из 50 в одном городе при первом замере это статистический шум, а не системная деградация.

Зато из этого получилось понимание: если хочется ловить такие региональные блипы заранее, нужно ставить периодический мониторинг через тот же Globalping API в Gatus, а не разовый прогон. Положил в backlog.

Что я понял про переезды

Главный урок этого переезда — аудит важнее самой миграции. Сам процесс переезда занял три недели вечеров, по 2-3 часа за раз. А вот финальный чеклист, который я прошёл за час, поймал реальный артефакт (дубликат kp-renderer как user-unit), который тихо ел ресурсы и при отключении KZ мог бы внезапно остановить рендер. Без аудита я бы выключил Латвию и удивлялся через сутки, почему один из сервисов лежит. Чеклист на десять пунктов — это копеечная страховка против фейла на 1 AM в субботу.

Второй урок — тестировать инфраструктуру с позиции пользователя, а не сервера. На Hetzner всё работает, в логах всё зелёно, healthcheck из самого Hetzner отдаёт 200. Но пока я не прогнал реальные RU-ноды через Globalping, я не знал, как сайт выглядит с точки зрения москвича на Билайне. Оказалось — отлично. Но мог бы оказаться и плохо: если бы Hetzner попал в чей-то блок-лист или peering-таблицу одного из крупных RU-ISP. Проверка из инфраструктуры РФ — это не paranoia, это базовая гигиена для любого проекта с русскоязычной аудиторией.

Третий урок — бесплатные инструменты решают 80% задач. Globalping покрыл breadth-тест по RU-провайдерам полностью бесплатно. Playwright + free-proxy сценарий не сработал из-за HTTPS, но это сам по себе полезный вывод: для глубоких браузерных тестов из реальных RU-ISP бесплатных опций нет, и это надо учитывать в планировании. Если бы я начинал с расчётом «куплю Bright Data за $30», я бы не открыл для себя Globalping — а это инструмент, который я теперь буду использовать на каждом deploy.

Четвёртый урок про инфраструктурный консолидейшн. До переезда у меня было три VPS у трёх провайдеров, бэкапы вручную, секреты в трёх местах, мониторинг частичный. После переезда — один dedicated на Hetzner, один резервный на KZ для legacy, единый Caddy с SSL для всех 22 доменов, daily-бэкапы с retention 7 дней в /opt/backups, Gatus отслеживает 12 endpoints, Beszel показывает живые метрики. Это не просто экономия денег (хотя и это тоже): это снижение когнитивной нагрузки. Я больше не помню три набора credentials, не путаю, на каком сервере крутится какой проект, не дрожу за бэкапы. Один сервер, который можно держать в голове целиком, — это огромный психологический апгрейд.

Пятый урок — никогда не недооценивай «дубликаты и забытые юниты». После любой миграции всегда останутся артефакты: старая конфигурация, второй systemd-юнит, забытый cron, висящий в /etc/cron.d, докер-контейнер, который ты ручную запустил месяц назад и не убил. Финальный аудит — это шанс их выловить, пока миграционный контекст ещё в голове. Через месяц ты уже не вспомнишь, зачем было два рендерера.

Латвийский сервер мёртв. Hetzner живёт, мониторится, бэкапится и отвечает из России за 21-36 миллисекунд. Это успешное завершение переезда — и неплохой задел на следующий цикл оптимизаций.

Паша Вин
Паша Вин

AI-инженер, предприниматель, маркетолог. Основатель feberra.com и x10seo.ru. 13 лет в перфоманс-маркетинге, 3 года в системной интеграции AI в бизнес.