Можно взять новый сервер, запустить на нём синтетический тест процессора и диска, получить красивые числа и сказать: «Машина быстрая». Это испытание машины. Оно честно отвечает на вопрос, на что способно железо, но почти ничего не говорит о том, сможет ли на этом железе жить ваше приложение так, как оно жило на облачной платформе или в SaaS. Репетиция миграции устроена иначе: вы берёте не абстрактную нагрузку, а настоящие действия пользователя, прогоняете их на копии своих данных и смотрите, что изменится для человека по ту сторону экрана.

Разница примерно как между проверкой, что у хирурга острые инструменты, и репетицией самой операции на манекене. Инструменты важны, но решение «можно оперировать» принимают по ходу репетиции. Поэтому в этой статье тестовый VPS рассматривается как стенд для получения решения: переезжать, переезжать частично или остаться. Все описанные ниже опыты — это то, что вам предстоит выполнить самостоятельно; никаких готовых цифр здесь нет и быть не может, потому что результат зависит от вашего состава данных и вашего профиля нагрузки.

Сначала выберите операции, ради которых сервис существует

Приложение умеет много, но живёт обычно за счёт трёх-пяти действий. Для интернет-магазина это открытие карточки товара, поиск, добавление в корзину и оформление заказа. Для внутренней CRM — открытие списка сделок с фильтром, сохранение карточки клиента и выгрузка отчёта. Для сервиса документов — загрузка файла, открытие превью и поиск по содержимому. Остальные экраны (настройки профиля, справка, страница тарифов) можно не трогать: если они чуть замедлятся, бизнес этого не заметит.

Отбирайте операции по двум признакам: как часто их выполняют и сколько стоит ошибка. Частая и дешёвая операция вроде открытия списка влияет на общее ощущение скорости. Редкая, но дорогая, как оплата заказа или запись данных клиента, влияет на деньги и доверие. Хорошая рабочая выборка включает и те и другие. Запишите их коротко, глаголами, от лица пользователя: «найти заказ по номеру», «сохранить правку в карточке», «скачать отчёт за месяц».

Для каждой операции определите, что считать успехом. Это не только «вернулся код 200». Успех для поиска — что в ответе есть искомая запись. Для сохранения — что при повторном чтении данные совпали с отправленными. Для выгрузки — что файл открылся и в нём ожидаемое число строк. Без такой проверки вы будете измерять скорость ответа, который может быть быстрым именно потому, что он пустой или ошибочный.

Как выглядит одна запись опыта

Прежде чем что-то запускать, заведите журнал опытов. Один опыт — одна запись, и в ней должно быть достаточно сведений, чтобы через месяц повторить его и понять, почему результат отличается. Пример для условного сервиса заказов:

Опыт: 07
Операция: поиск заказа по номеру и открытие карточки
Стенд: тестовый VPS, приложение и БД на одной машине
Данные: копия базы от даты выгрузки, персональные поля обезличены
Кеш: холодный (после перезапуска приложения и БД)
Нагрузка: 20 виртуальных пользователей, 10 минут, по одному поиску в 2-5 секунд
Начало: дата и время запуска
Версия приложения: идентификатор сборки или коммита
Эталон: тот же сценарий на текущем SaaS, в согласованное окно
Что измерили: доля неуспешных ответов, задержка в медиане и в хвосте, потребление CPU и памяти на стенде
Наблюдения: что происходило на диске, были ли перезапуски процессов
Решение по опыту: пока не принято / принято / повторить

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

Данные и кеш: что должно совпасть, чтобы сравнение имело смысл

Самая частая причина «чудесного» ускорения на новом сервере — пустая или маленькая база. Запрос по таблице в тысячу строк и по таблице в десять миллионов — разные запросы, даже если текст у них один. Поэтому для репетиции нужна копия настоящих данных или, если это невозможно, набор, воспроизводящий их объём и распределение: сколько записей у самого крупного клиента, как часто встречаются длинные тексты, сколько файлов хранится и какого размера.

С персональными данными осторожность обязательна. Копия боевой базы на тестовом сервере — это ещё одно место, где эти данные лежат. Обезличьте то, что можно: имена, телефоны, почтовые адреса, содержимое свободных комментариев. Но сохраните форму данных: длину строк, долю пустых полей, связи между таблицами. Если вы заменили все имена одним словом «Иван», индексы по имени начнут вести себя иначе, чем на настоящих данных, и вы получите ложно оптимистичный результат. О том, как устроить такую копию для сайта, подробнее рассказано в материале про настройку staging-копии сайта.

Второй вопрос — загруженные файлы. Если в SaaS пользователи хранят вложения, а на VPS вы планируете держать их на локальном NVMe-диске, перенесите достаточную часть, чтобы оценить поведение диска под чтением и записью. Одна только база без файлов скрывает как раз то место, где диск начинает быть узким.

Холодный и тёплый кеш — это два разных опыта

Кеш бывает на нескольких уровнях: страницы файловой системы, буферы базы данных, кеш приложения, кеш объектов, HTTP-кеш перед приложением. Сразу после перезапуска всё это пусто, и первые запросы идут по самому медленному пути. Через несколько минут работы картина меняется. Если вы запустите нагрузку на только что поднятый стенд и сравните со SaaS, который работает неделями и прогрет, вы сравните разные состояния, а не разные платформы.

Решение простое: делайте для каждой операции два прогона и записывайте их отдельно. Первый — холодный, сразу после перезапуска служб. Это покажет, как поведёт себя система после обновления или перезагрузки сервера, когда пользователи уже пришли, а кеши ещё пусты. Второй — тёплый: после прогрева теми же операциями до стабильного состояния. Именно он ближе к обычному рабочему дню. Для решения о миграции важны оба: тёплый показывает типичное качество, холодный — цену каждого перезапуска.

Есть и обратная ловушка. Если вы запускаете один и тот же запрос с одним и тем же номером заказа тысячу раз, он окажется в кеше после первого раза, и вы измерите кеш, а не приложение. Разнообразьте входные данные: список номеров заказов, поисковых фраз, идентификаторов клиентов, взятый из реальных логов или выгрузки. Тогда и попадания, и промахи кеша будут похожи на настоящие.

Одинаковая нагрузка: как не сравнивать несравнимое

Нагрузка — это не только «сколько пользователей», но и как они действуют. Реальный человек делает паузы, читает экран, переходит между разделами. Если в тесте он без пауз долбит один адрес, вы создаёте нагрузку, которой ни у вас, ни у SaaS не будет. Опишите сценарий: последовательность действий, паузы между ними, долю разных операций. Например, из ста сеансов шестьдесят только читают, тридцать ищут и открывают карточки, десять что-то меняют.

Для воспроизводимого запуска подходит инструмент нагрузочного тестирования, в котором сценарий записан кодом и повторяется без ручных шагов. У k6 есть понятие threshold: это условие на метрику, например на задержку или частоту ошибок, и если оно нарушено, тест завершается с ненулевым кодом, как описано в документации Grafana k6. Это превращает прогон из картинки для разглядывания в проверку с однозначным исходом: прошёл или не прошёл. Там же сказано, что обычный check сам по себе статус завершения не меняет; чтобы проверка содержимого ответа могла провалить тест, нужен threshold на метрику checks. Для нашей задачи это важно: «ответ вернулся быстро, но пустой» не должно засчитываться как успех.

Минимальный пример сценария для поиска заказа (адреса и значения условные, подставьте свои):

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  vus: 20,
  duration: '10m',
  thresholds: {
    http_req_failed: ['rate<0.01'],
    http_req_duration: ['p(95)<800'],
    checks: ['rate>0.99'],
  },
};

export default function () {
  const res = http.get('https://test.example.com/orders/search?q=12345');
  check(res, {
    'статус 200': (r) => r.status === 200,
    'есть искомый номер': (r) => r.body.includes('12345'),
  });
  sleep(Math.random() * 3 + 2);
}

Пороги 1 % ошибок и 800 мс в хвосте — иллюстрация, а не рекомендация. Правильный порог берётся не с потолка, а из текущего поведения SaaS и из ожиданий пользователей, о чём ниже. В реальном сценарии вместо одного номера используйте список из ваших данных, иначе вернётесь к ловушке кеша.

Откуда брать пороги

Порог без опоры — просто красивое число. Опора бывает двух видов. Первая — эталон: тот же сценарий и та же нагрузка на текущем SaaS. Здесь есть оговорка, которую нельзя обходить: нагружать чужой боевой сервис можно только с разрешения владельца и в рамках его правил. Многие поставщики прямо запрещают нагрузочные испытания или требуют согласования. Если разрешения нет, эталоном служат ваши собственные наблюдения: метрики из панели сервиса, данные мониторинга на стороне клиента, замеры времени отклика из браузера или клиентских логов за обычные дни.

Вторая опора — требования пользователей. Если сотрудники жалуются, когда карточка открывается дольше двух-трёх секунд, то порог на хвост задержки для этой операции должен лежать заметно ниже, с запасом на сеть. Для операций записи, наоборот, часто важнее не скорость, а отсутствие потерянных и задвоенных записей, и порог по ошибкам там должен быть строже, чем по времени.

Если вы автоматизируете и проверку интерфейса, например полный путь «вошёл, нашёл, сохранил», полезно знать рекомендации Playwright для запуска на CI. Документация Playwright советует начинать с одного worker, чтобы прогоны были воспроизводимыми, а параллелизм включать на мощном собственном CI. Для репетиции это разумно: сначала убедитесь, что сценарий стабилен последовательно, потом ускоряйте. Там же описан sharding, то есть распределение набора тестов между несколькими задачами CI, и global timeout, который позволяет Playwright самому завершить зависший прогон и подготовить отчёт. Последнее полезно для стенда, который может остановиться на середине: вы получите отчёт, а не молчание.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации Россия, США, Франция и Великобритания. Оплата картой РФ и по СБП.

АРЕНДОВАТЬ СЕРВЕР

Измерять нужно не только ответ приложения

Пользователь видит один число: сколько он ждал. Но за этим числом стоит цепочка: сеть до сервера, обратный прокси, приложение, база данных, диск, очередь, внешние API. Если результат хуже, чем на SaaS, первый вопрос — где именно потеряно время. Без разделения вы либо отвергнете хороший сервер из-за плохо настроенной базы, либо примете плохую конфигурацию, потому что случайно повезло с сетью.

Практически это означает четыре слоя измерений, каждый из которых вы записываете в журнал опыта отдельно:

  • Со стороны клиента. Полное время запроса с точки зрения нагрузочного генератора, включая сеть. Запускайте его не с самого тестового сервера, а с отдельной машины или хотя бы из другого места, иначе вы не увидите сетевую часть.
  • Со стороны приложения. Время обработки запроса внутри приложения, если оно его логирует; число одновременных обработчиков; длина очереди задач.
  • Со стороны базы данных. Самые медленные запросы, ожидание блокировок, число соединений. Часто окажется, что один-два запроса дают большую часть задержки, и дело в индексе, а не в сервере.
  • Со стороны ресурсов машины. Загрузка CPU по ядрам, использование памяти и подкачки, скорость и очередь операций на диске. Это показывает, что именно ограничивает систему при росте нагрузки.

Отдельная история — внешние зависимости. Если приложение при каждом заказе обращается к платёжному шлюзу, службе доставки или почтовому сервису, то часть задержки определяет не ваш сервер, а чужой. Здесь важно не наращивать нагрузку на настоящие боевые API: у них могут быть лимиты, а запросы могут иметь побочные эффекты вроде реальных платежей или писем клиентам. Используйте тестовые режимы поставщика, где они есть, или заглушки с имитацией типичной задержки. И записывайте, что именно было заменено: результат со стендовой заглушкой нельзя читать как результат с настоящим сервисом.

Есть и вопрос о том, где живёт база. На SaaS вы не управляете деталями, а на VPS всё на вашей стороне. Если вы разместите приложение и базу на одной машине, они будут делить процессор, память и диск, и под нагрузкой начнут мешать друг другу. Это нормальный вариант для небольших проектов, но репетиция должна показать, как именно они конкурируют. Для этого в журнале и нужны данные по загрузке ресурсов параллельно с задержками: пик задержки, совпавший с пиком записи на диск, объясняет больше, чем сама задержка.

Как выбрать размер тестового VPS

Здесь легко начать с неправильного вопроса: «какой тариф даст нужную производительность». Тариф не обещает ёмкость; он задаёт набор ресурсов, а что вы получите на этом наборе, покажет опыт. В линейке VPS MAATRIX есть конфигурации от Ba с 1 vCPU, 2 ГБ RAM и 40 ГБ NVMe до Djed с 16 vCPU, 32 ГБ и 320 ГБ; между ними Uab (2 vCPU, 4 ГБ, 80 ГБ), Heka (4 vCPU, 8 ГБ, 160 ГБ) и Maat (8 vCPU, 16 ГБ, 240 ГБ). Это ориентир для планирования, а не пророчество о числе пользователей, которых выдержит каждая.

Разумная схема репетиции — идти снизу вверх. Начните с конфигурации, которая по вашим прикидкам должна быть близка к нужной, и посмотрите, что ограничивает систему: процессор, память или диск. Если упёрлись в память и началась подкачка, дело не в «медленности» сервера, а в нехватке RAM: следующий размер, скорее всего, изменит картину сильно. Если процессор загружен не полностью, а задержки растут, ищите ограничение в приложении или базе, и большая конфигурация не поможет. Такая диагностика дешевле, чем покупка запаса «на всякий случай».

Тестовый стенд удобен тем, что он временный. Публичное предложение MAATRIX описывает автоматическое создание VPS за пару минут после оплаты, root-доступ, NVMe-диски и процессоры AMD EPYC, а локации перечислены как UK, US, RU и FR; доступность конкретного образа и операционной системы в нужной локации уточняйте в форме заказа. Для репетиции стоит выбрать локацию, где будут находиться ваши пользователи, или хотя бы сравнить две: сетевая задержка до сервера входит в то самое число, которое видит человек, и от выбора страны размещения она зависит ощутимо.

Когда среднее лучше, а пик хуже, и наоборот

Теперь самое интересное: что делать, когда результаты неоднозначны. Допустим, вы получили (это условная иллюстрация, а не чьё-то измерение) такую картину для операции поиска. SaaS в среднем отвечает быстро, но в часы пик раз в несколько минут у отдельных пользователей возникают долгие паузы. Ваш VPS в среднем медленнее, но задержка почти не растёт при добавлении пользователей, и хвост остаётся узким. Какой вариант лучше?

Ответ зависит от того, как пользователи воспринимают задержку. Если сотрудник делает по сто поисков в день, то средняя скорость определяет его общее ощущение, но одна пауза в десять секунд запоминается сильнее ста быстрых ответов. Если операция запускается автоматически, например интеграцией по расписанию, то её хвост может вызывать таймауты и повторные запросы, а они уже создают лишнюю нагрузку и дубли. В обоих случаях хвост задержки и стабильность важнее небольшого выигрыша в среднем.

Поэтому всегда смотрите на распределение, а не на одно число. Полезно записывать медиану, 95-й процентиль и максимум, а при достаточном числе запросов и 99-й процентиль. Медиана говорит, как живёт типичный запрос; хвост — как живёт самый невезучий. Если медиана на VPS хуже, чем на SaaS, на двадцать процентов, а хвост лучше в два раза, вы меняете чуть более медленное «обычно» на гораздо более предсказуемое «всегда». Для интерфейса, где пользователь ждёт ответа, это чаще выгодная сделка. Для массовой пакетной обработки, где важна общая пропускная способность, ситуация может быть обратной.

Подобные сравнения полезно разбирать по операциям, а не в целом. Посмотрите, как может выглядеть сводка по итогам, если читатель вносит в неё свои измеренные значения:

Форма сводки по операциям (значения вносятся из ваших опытов)
ОперацияОшибки: SaaS / VPSМедиана: SaaS / VPSХвост p95: SaaS / VPSЧто ограничило стендРешение
Поиск заказазаписатьзаписатьзаписатьCPU, память, диск или БДпереносить / отложить
Сохранение карточкизаписатьзаписатьзаписатьблокировки, дискпереносить / отложить
Выгрузка отчётазаписатьзаписатьзаписатьпамять, очередьпереносить / отложить

Обратите внимание на последний столбец: решение принимается построчно. Возможно, поиск и просмотр стабильнее на вашем сервере, а тяжёлая выгрузка отчёта пока уступает. Тогда разумный ответ — не «переезжаем» или «остаёмся», а «переносим всё, кроме отчётов» или «сначала дорабатываем отчёты». Это не компромисс, а честное чтение результатов.

Как отличить закономерность от случайности

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

Второй источник ложных выводов — слишком короткие прогоны. Десять минут при двадцати пользователях выявят многое, но не покажут, что происходит через несколько часов: рост памяти из-за утечки, накопление файлов журналов, замедление после заполнения кеша. Для операций, критичных для бизнеса, выделите хотя бы один длинный прогон, пусть и с меньшей интенсивностью, и посмотрите, остаётся ли картина ровной. Если для зависших прогонов вы используете Playwright, помогает упомянутый ранее global timeout: он не даст долгому тесту повиснуть бесконечно и оставит отчёт.

Третий — совпадение условий. Если эталон на SaaS вы сняли в понедельник утром, а стенд измеряли в субботу ночью, разницу создаст не платформа, а время. По возможности запускайте обе стороны почти одновременно и записывайте условия в журнал: дата, время, версия приложения, состояние кеша, размер данных. Это именно та дисциплина, ради которой мы начали с формата записи опыта.

Что значит «не прошло» и что с этим делать

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

Помните, что стенд нужен не для получения зелёной галочки, а для понимания. Если после настройки и повторных прогонов операция по-прежнему не укладывается в порог, у вас есть три честных варианта. Первый — остаться на SaaS для этой операции и пересмотреть решение позже. Второй — перенести остальное, а проблемную часть оставить или вынести. Третий — изменить сам порог, но только если вы можете объяснить, почему пользователям это подходит; подгонять норму под результат, чтобы всё прошло, — путь к неприятному сюрпризу в первый рабочий день.

Если же всё прошло, это ещё не значит, что можно закрывать эксперимент. Определите заранее, что вы будете наблюдать после переключения, и при каких значениях вернётесь обратно. Репетиция даёт вам ожидания; боевая работа их подтверждает или опровергает. Пока данные на старой платформе доступны, возврат остаётся недорогим решением, и это снимает значительную часть напряжения при переезде.

Решение по операциям, а не рейтинг серверов

Итогом репетиции должен стать не рейтинг вида «VPS в целом лучше» или «SaaS в целом надёжнее», а короткий список решений, привязанных к тем операциям, с которых мы начали. Например: поиск и просмотр карточек переносим, потому что при тёплом кеше ошибок меньше порога, а хвост задержки уже, чем у текущего сервиса; сохранение переносим после проверки блокировок на повторном прогоне; выгрузку отчётов пока оставляем, потому что на стенде она упирается в память, и мы выбираем между увеличением конфигурации и переработкой запроса.

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