Представьте, что тестовый переезд закончился, и кто-то в чате пишет: «Вроде всё работает». Это самая опасная фраза в миграциях. Она означает, что главная страница открылась, а что именно «всё» и для кого «работает» — не известно. Приёмка нужна затем, чтобы такая фраза никогда не понадобилась: решение о переезде принимается по заранее записанным действиям бизнеса, а не по ощущению.
Разберём это на условном сервисе. Пусть это небольшой интернет-магазин запчастей для велосипедов: приложение на Python, PostgreSQL, каталог с загруженными фотографиями, очередь фоновых задач, отправка писем и вызов внешнего API службы доставки. Магазин живёт на VPS в одной локации, а мы репетируем перенос в другую: у MAATRIX в публичном предложении перечислены UK, US, RU и FR. Все цифры ниже — придуманные для иллюстрации, это не измерения реального проекта.
Сначала — что приносит деньги, а потом — что можно проверить
Первая ошибка приёмки — начинать с технических пунктов: порт открыт, служба запущена, место на диске есть. Всё это нужно, но ничего не говорит о ценности. Начните с вопроса: какие действия пользователей и сотрудников, если они сломаются, остановят бизнес? Для нашего магазина получается короткий список:
- покупатель входит в аккаунт;
- находит товар через поиск и фильтры;
- оформляет заказ и получает подтверждение;
- заказ уходит в службу доставки, и приходит номер отправления;
- менеджер видит заказ в админке, а фоновые задачи (пересчёт остатков, письма) не накапливаются;
- после сбоя можно вернуть данные из копии.
Обратите внимание на глаголы. Не «работает база», а «заказ оформлен». Не «почта настроена», а «письмо пришло». Каждый пункт — завершённая операция с наблюдаемым результатом, и именно такие операции составляют приёмку.
«Сайт открылся» и «операция завершилась» — разные события
Ответ 200 на главную страницу доказывает немногое: веб-сервер жив, сертификат принят, статические данные отдаются. Он ничего не говорит о том, смогла ли база принять запись, дошла ли задача до воркера, ответил ли внешний API. Магазин после переезда легко может выглядеть идеально и при этом не принимать ни одного заказа: например, из-за пользователя базы, которого не оказалось на новой машине, или из-за адреса, который внешняя служба не пускает.
Поэтому для каждого сценария записывают три вещи: стартовое состояние, действие и подтверждение в нескольких местах. Для оформления заказа это выглядит так.
- Стартовое состояние: тестовый покупатель, тестовый товар с известным остатком, например 10 штук.
- Действие: вход, добавление в корзину, оформление.
- Подтверждение: в базе появилась строка заказа, остаток стал 9, в очереди не осталось зависшей задачи, статус заказа сменился после ответа службы доставки, покупателю пришло письмо.
Подтверждение в нескольких слоях важно, потому что заказ — цепочка. Веб-слой принял запрос, база записала, воркер забрал задачу, внешняя служба ответила, почтовый сервер отправил. Сбой на любом звене даёт «открывшийся сайт» и несостоявшуюся операцию.
Измеримые признаки: как превратить «быстро» в условие прохождения
Слово «быстро» в приёмке бесполезно. Нужны пороги. Инструменты нагрузочного тестирования для этого и придуманы: в Grafana k6 есть thresholds — условия прохождения теста по метрикам, например по задержке или доле ошибок, и если условие не выполнено, тест считается неуспешным (документация k6). Полезная сторона этой идеи шире самого инструмента: порог записывается заранее, и решение принимает не настроение, а красная или зелёная отметка.
Откуда брать числа? Не из примеров документации: значения оттуда не являются нормой для вашего сайта. Опорой служит ваша же текущая площадка. Прогоните сценарии на действующем сервере, получите базовую линию и от неё определите допуск. Пусть для нашего магазина на старой локации оформление заказа целиком занимает около 1,2 секунды, а доля ошибок в серии из 200 попыток равна нулю. Тогда допустимый порог можно записать так: ни одной ошибки на заказах и время оформления не хуже старой линии больше чем, скажем, на 30 %. Цифра 30 % условная: её выбирает владелец сервиса, прикидывая, что для покупателя ещё незаметно.
Признаки бывают двух видов. Бинарные: письмо пришло, номер отправления получен, пользователь с ролью администратора вошёл. Количественные: задержка, доля ошибок, глубина очереди. Для бинарных порог нулевой — любая неудача считается провалом сценария. Для количественных нужен допуск, и лучше смотреть не на среднее, а на хвост распределения, потому что покупателю неприятен не средний, а самый медленный заказ.
Один заказ до и после: почему цифры разные и что с этим делать
Центральный эксперимент репетиции: пройти один и тот же завершённый сценарий — вход, создание заказа, внешний вызов — до смены локации и после неё, и сравнить время. Сравнивать нужно не одно число «всё заняло 1,8 секунды вместо 1,2», а фазы. Иначе непонятно, что именно изменилось.
Утилита curl умеет разбивать запрос на стадии: time_namelookup, time_connect и time_appconnect показывают время от начала операции до завершения разрешения имени, TCP-соединения и TLS-рукопожатия, time_starttransfer включает обработку запроса сервером до первого байта, time_total — полную длительность. Принципиальная деталь: эти показатели накопительные, и складывать их как независимые этапы нельзя (руководство curl). Длительность самой фазы получают вычитанием соседних значений.
Разберём условный пример для запроса оформления заказа. Допустим, до переезда: DNS завершился на 0,02 с, TCP на 0,05, TLS на 0,11, первый байт на 0,60, всё закончилось на 0,65. После: 0,02, 0,12, 0,27, 0,72, 0,78. Вычтем соседние значения.
| Фаза | До | После |
|---|---|---|
| Разрешение имени | 0,02 | 0,02 |
| TCP-соединение (connect минус lookup) | 0,03 | 0,10 |
| TLS (appconnect минус connect) | 0,06 | 0,15 |
| Ожидание сервера (starttransfer минус appconnect) | 0,49 | 0,45 |
| Передача ответа (total минус starttransfer) | 0,05 | 0,06 |
Картина читается мгновенно: приложение работает так же, даже чуть быстрее, а выросли именно фазы установления соединения. Это следствие расстояния до клиента: каждое рукопожатие требует обмена пакетами, и чем дальше пользователь от сервера, тем дороже эти обмены. Серверная часть при этом не пострадала. Если бы вместо этого вырос интервал ожидания сервера, искать причину пришлось бы в приложении, базе или внешнем вызове, а не в сети.
Отсюда следует важный вывод для приёмки: результат зависит от того, откуда измерять. Замер с самого VPS показывает только работу приложения. Замер с ноутбука в офисе показывает путь одного человека. Сеть пользователя — отдельная переменная, и её нужно измерять оттуда, где живёт ваша аудитория. Если покупатели в основном в России, а новая локация — US или FR, прогоните сценарий с машин или из сетей, похожих на пользовательские, и запишите фазы отдельно. Две-три точки замера дают куда больше, чем сотня запросов из одной.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации Россия, США, Франция и Великобритания. Оплата картой РФ и по СБП.
Данные, внешние интеграции и сеть пользователя — три разных источника неприятностей
Когда после репетиции что-то не сошлось, полезно сразу понимать, к какому из трёх миров относится сбой. Они ломаются по-разному и лечатся по-разному.
Данные: то, что вы везёте с собой
Данные переезжают целиком: база, файлы, конфигурация. Для PostgreSQL логический экспорт делается через pg_dump: он создаёт согласованный экспорт одной базы, даже когда пользователи продолжают работать. Но есть ловушка: глобальные роли и tablespaces в такой экспорт не входят, для них предусмотрен pg_dumpall (документация pg_dump). Для приёмки это превращается в конкретный сценарий: после восстановления на новой площадке войти под служебным пользователем приложения и выполнить запись. Если роль не создана, приложение стартует, страницы со статикой открываются, а любая операция с базой падает. Именно здесь «сайт открылся» расходится с «заказ создан». Подробнее о самом переносе базы — в материале про миграцию базы данных между серверами.
Файлы проверяют иначе. Фотографии товаров легко потерять частично, и узнаешь об этом только по пустым карточкам. Критерий: количество файлов и контрольные суммы выборки на новой площадке совпадают с исходной, а три-четыре случайные карточки открываются с картинками.
Копии заслуживают отдельной строки приёмки: бэкап, который ни разу не разворачивали, остаётся предположением. Если вы используете restic, восстановить выбранный снимок можно в отдельный target и сначала проверить результат. При этом фильтры работают по-разному: --path отбирает снимки, а --include и --exclude ограничивают извлекаемые файлы, и подменять одно другим нельзя; для последнего снимка конкретного хоста и пути есть соответствующие фильтры (документация restic). Сценарий приёмки: поднять копию в пустую директорию, сверить число файлов и проверить, что дамп базы из этой копии действительно разворачивается. Критерий прохождения бинарный: восстановились и каталог, и база, и тестовый заказ в восстановленной базе виден.
Внешние интеграции: то, что вы не контролируете
Служба доставки, платёжный шлюз, почтовый провайдер, SMS — всё это живёт вне вашего сервера и реагирует на ваш новый адрес. Здесь нельзя принимать на веру, что вызов, работавший вчера, сработает сегодня с другого IP. Многие внешние сервисы держат списки разрешённых адресов, ограничивают запросы по IP или иначе относятся к зарубежным сетям, а у почты есть свои проверки отправителя. Заранее обещать, что сторонний API откроется по иностранному адресу, нельзя, поэтому такой вызов обязательно входит в приёмку отдельным сценарием.
Критерий для интеграции формулируют через результат: тестовый заказ получил номер отправления, а в журнале приложения есть ответ внешней службы с успешным статусом. Если провайдер просит добавить адрес нового сервера в разрешённые, это делается ещё до репетиции, иначе репетиция проверяет не миграцию, а скорость согласования с чужой поддержкой.
Отдельная тонкость — повторы. Оформление заказа и вызов службы доставки не лежат в одной транзакции базы: запись в вашей базе и запрос к чужому API — два независимых действия. Поэтому сценарий приёмки должен включать неприятный случай: что будет, если внешний вызов завершился ошибкой по тайм-ауту. Появится ли дубль заказа в службе доставки при повторной отправке? Закроет ли это механизм, который вы сами реализовали и проверили, а не предположение, что новый идентификатор попытки сам по себе всё решит? Критерий: после принудительного сбоя и повтора в доставке ровно один заказ, а у вас — одна запись со статусом «передан».
Сеть пользователя: то, чего вы почти не видите
Третий мир — путь от покупателя до сервера. Он включает задержку, провайдера, иногда блокировки определённых направлений. Исправить его настройкой приложения нельзя, но можно измерить и принять решение. В нашем условном замере выше выросли именно фазы соединения; для интернет-магазина с одним-двумя обменами на страницу это может быть приемлемо, а для интерфейса, который делает десятки последовательных запросов, — уже нет. Приёмка здесь записывается как допуск на фазы соединения из выбранных точек и как количество последовательных запросов в ключевом сценарии.
Что остановит переезд
Приёмка бессмысленна без заранее оговорённых условий остановки. Иначе после репетиции начнутся торги: «задержка выросла, но ничего критичного». Разделите отклонения на три уровня, пока эмоции спокойны.
- Стоп. Любой провал завершённого сценария с деньгами: заказ не создаётся, не вызывается платёжный или доставочный API, не восстанавливается копия, нет записи в базу под рабочей ролью. Переезд откладывается до исправления и повторной репетиции с нуля.
- Исправить до переключения. Письма попадают в спам, очередь растёт, часть картинок не копируется. Это не обязательно блокирует репетицию, но блокирует реальный перенос.
- Принять с записью. Рост фаз соединения в рамках допуска, небольшая разница в хвосте задержки. Фиксируется в отчёте, пересматривается через неделю после переезда.
Хороший признак зрелой приёмки — у каждого критерия указано, к какому уровню он относится. Тогда после репетиции остаётся пройти по таблице, а не спорить.
Ресурсы тестовой площадки и честность сравнения
Репетицию разумно проводить на VPS, близком по ресурсам к будущему боевому. У MAATRIX это NVMe-диски, процессоры AMD EPYC и root-доступ, а тарифы различаются по ресурсам: Ba — 1 vCPU, 2 ГБ RAM, 40 ГБ NVMe; Uab — 2 vCPU, 4 ГБ, 80 ГБ; Heka — 4 vCPU, 8 ГБ, 160 ГБ; Maat — 8 vCPU, 16 ГБ, 240 ГБ; Djed — 16 vCPU, 32 ГБ, 320 ГБ. Эти цифры — отправная точка для теста, а не обещание ёмкости: сколько заказов в минуту выдержит ваша связка, показывает только ваш замер. Поэтому в приёмку входит нагрузочный сценарий с порогами: например, серия оформлений заказа с допуском по доле ошибок и по хвосту задержки на выбранной конфигурации. Если пороги не проходятся, выбирается следующий уровень ресурсов и репетиция повторяется.
Одно условие честности: сравнивать старую и новую площадку нужно при сравнимом состоянии. Размер базы, прогретость кэшей, наличие фоновых заданий и время суток влияют на цифры. Запись, в каких условиях снят замер, так же важна, как сам замер. Без неё разница в 0,2 секунды может оказаться разницей между утренним затишьем и вечерним потоком, а не между локациями.
Образы и готовые приложения тоже не повод считать, что везде всё одинаково. В публичном каталоге MAATRIX предложены, например, n8n и Nextcloud с установкой, но точное наличие и операционная система проверяются в форме заказа, и одинаковая доступность образа во всех локациях не подразумевается. Если ваша схема опирается на готовый образ, проверьте его наличие именно в той локации, куда собираетесь, до начала репетиции.
Письма и фоновые задачи: тихие поломки
Самые коварные сбои после переезда — те, о которых никто не сообщает. Письмо не ушло — покупатель молчит. Задача по пересчёту остатков не выполнилась — товар продаётся при нулевом запасе. Планировщик не стартовал — ночной отчёт не пришёл. Поэтому для фоновых процессов критерии формулируются через наблюдаемые следы.
- Тестовое письмо доходит до ящика, а не только уходит из приложения, и не попадает в спам.
- Глубина очереди после серии заказов возвращается к нулю в разумный срок, который вы определили по старой площадке.
- Запланированная задача сработала по расписанию хотя бы один раз за время репетиции — для этого репетицию иногда нужно растянуть, а не пройти за час.
- Часовой пояс и локаль сервера совпадают с ожидаемыми: расписание, написанное под одну зону, молча сдвинется в другой.
Последний пункт напоминает, что смена локации — это ещё и смена окружения, а не только IP-адреса. Настройки времени, региональные параметры, исходящие адреса, обратные записи для почты — всё, что раньше казалось «данностью», на новой площадке нужно перепроверить.
Как записать результат репетиции
После прохождения сценариев нужен короткий документ, который читается без устных пояснений. Удобная форма — таблица: сценарий, критерий, результат на старой площадке, результат на новой, уровень отклонения, решение.
| Сценарий | Критерий | Результат | Решение |
|---|---|---|---|
| Вход покупателя | Сессия создана, личный кабинет открыт | Пройден | Принять |
| Создание заказа | Запись в базе, остаток уменьшен, письмо получено | Пройден | Принять |
| Вызов службы доставки | Получен номер отправления, нет дублей при повторе | Требует настройки списка адресов | Стоп до исправления |
| Восстановление из копии | Файлы и база развёрнуты, тестовый заказ виден | Пройден | Принять |
| Фазы соединения с клиентских точек | Рост в пределах согласованного допуска | Рост заметен, допуск выдержан | Принять с записью |
Рядом с таблицей стоит положить список ограничений, которые остались после репетиции: что не проверялось (например, пиковая нагрузка выходного дня), какие интеграции не тестировались с боевыми ключами, какие сценарии выполняются только раз в месяц. Это не оправдание, а карта рисков для первых дней после переключения: именно там нужно смотреть на журналы внимательнее.
Решение
Для нашего условного магазина после такой репетиции вывод получился бы простым и без драматизма. Вход и оформление заказа прошли, копия разворачивается, фазы соединения выросли в допустимых пределах, а внешний вызов службы доставки упал на списке разрешённых адресов. Значит, переезд пока не проводится: сначала согласование адреса с провайдером доставки, затем повторная репетиция целиком, а не только одного сценария. Когда все строки таблицы зелёные или имеют пометку «принять с записью», переносить уже не страшно: окно переключения нужно только для финальной синхронизации данных, а сомнения заранее сняты. Если же хотя бы один денежный сценарий красный, переезд откладывается, и это не потеря времени, а цена, которую вы платите на репетиции, а не в боевой ночи.