MAATRIX / Блог / Антипаттерн: нагрузочное тестирование на ноутбуке разработчика

Антипаттерн: нагрузочное тестирование на ноутбуке разработчика

MAATRIX

Перед релизом нужно проверить, выдержит ли сервис нагрузку — и самый быстрый способ, который приходит в голову, это открыть терминал на своём ноутбуке, поставить k6 или locust и запустить пару тысяч виртуальных пользователей прямо оттуда. Результат выглядит убедительно: графики, RPS, перцентили задержки — всё как положено. Проблема в том, что эти цифры почти никогда не описывают то, что происходит с сервисом под нагрузкой от реальных пользователей — они описывают то, что происходит между вашим Wi-Fi роутером и сервером, что тоже интересно, но не то, ради чего запускался тест.

В чём именно ошибка

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

Ноутбук разработчика этому требованию не соответствует почти никогда, сразу по нескольким независимым причинам:

  • домашний или офисный интернет ограничен по полосе и нестабилен по своей природе — соседи по подъезду, Wi-Fi-помехи, провайдерский NAT, VPN-туннель, через который часто идёт весь трафик из офиса;
  • ноутбук — это ещё и рабочая машина: браузер с полусотней вкладок, Slack, IDE, докер-контейнеры, антивирус — всё это конкурирует за CPU и память с процессом-генератором нагрузки;
  • ноутбук физически стоит там, где сидит разработчик, а не там, где сидят пользователи сервиса — задержка в замере получается не такой, какая будет у реального трафика.

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

Типичная схема "тест с ноутбука":

[Ноутбук: IDE + Slack + Docker + генератор нагрузки]
        │
        │  домашний Wi-Fi → провайдер → интернет
        │  (полоса ограничена, задержка гуляет,
        │   пакеты иногда теряются)
        │
        ▼
[Тестируемый сервис в облаке]

Результат теста измеряет ХУДШЕЕ из трёх узких мест:
CPU ноутбука, домашний канал, сам сервис —
и вы не знаете, какое из них сработало первым.

Ограниченная и нестабильная сеть искажает пропускную способность

Первое и самое очевидное искажение — генератор нагрузки просто не может отправить больше запросов, чем позволяет исходящий канал. Домашний тариф на десятки или сотни мегабит в секунду — это не то же самое, что канал дата-центра, где сервисы обычно связаны между собой сетью с полосой в единицы или десятки гигабит и предсказуемой задержкой внутри одного региона. Если тест упирается в потолок 50–100 запросов в секунду, а не в реальный лимит сервиса — вы узнали пропускную способность своего домашнего провайдера, а не производительность сервиса. О том, что «гигабитный» тариф на практике редко выдаёт заявленную скорость даже на одном файле, не говоря уже про десятки тысяч параллельных TCP-соединений, — в статье про то, почему гигабитный канал кончается на 40 мегабитах; механизм там применим и к исходящему трафику генератора нагрузки.

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

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

# быстрая проверка: сколько реально даёт исходящий канал прямо сейчас
# (не полагайтесь на цифру из договора с провайдером)
iperf3 -c iperf.example.net -t 30 -P 4

# и сравните с тем, что нужно тесту:
# 5000 RPS × средний размер запроса 2 КБ ≈ 10 000 КБ/с ≈ 80 Мбит/с
# только на исходящий трафик, без учёта ответов и служебных данных TCP

Если вычисленная потребность теста близка к тому, что показал iperf3, или тем более превышает это число, — тест гарантированно упрётся в канал раньше, чем в сервис, и полученные цифры бессмысленны как оценка реальной ёмкости системы.

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

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

Арендовать сервер

Ресурсы ноутбука конкурируют с генератором нагрузки

Второе искажение возникает даже при идеальном канале — просто потому, что ноутбук не выделенная машина, а рабочее место. Пока идёт тест, на нём одновременно крутится IDE с индексацией проекта, десяток вкладок браузера, Slack или Teams, локальные докер-контейнеры, антивирус, а иногда и второй тест, запущенный «на всякий случай» в соседнем терминале. Генератор нагрузки — это не лёгкий процесс: чтобы честно эмулировать тысячи параллельных пользователей, ему нужны собственные CPU-ядра, память под буферы соединений и стабильный доступ к сетевому стеку без конкуренции за него с другими процессами.

Когда генератор нагрузки запаздывает из-за того, что CPU занят чем-то ещё, он не может вовремя отправить следующий запрос по расписанию, которое задано в сценарии теста. Внешне это выглядит как «сервис не справляется» — растёт заявленная задержка, падает RPS — но на самом деле узкое место не на стороне сервиса, а на стороне самого генератора, который не успевает генерировать нагрузку с заданной интенсивностью. Это особенно коварно, потому что инструменты вроде k6 или locust обычно не сигнализируют явно о собственной перегрузке — они просто показывают итоговые цифры, которые выглядят как полноценный отчёт о производительности сервиса.

# k6 умеет показывать собственные метрики выполнения —
# проверяйте их наравне с метриками цели теста
k6 run --summary-trend-stats="avg,min,med,max,p(95),p(99)" script.js

# если в выводе high iteration_duration заметно превышает
# сумму http_req_duration по этапам сценария — часть времени
# уходит не на ожидание ответа сервера, а на то, что сам k6
# не успевает обрабатывать следующую итерацию

Практический признак этой проблемы — если во время теста открыть монитор ресурсов на самом ноутбуке (htop, Activity Monitor, диспетчер задач) и увидеть, что CPU генератора нагрузки уже упёрся в 100% на одном или нескольких ядрах, пока сервис-цель по своим собственным метрикам ещё далёк от предела — значит, тест измеряет мощность вашего ноутбука, а не сервиса.

Географическая близость искажает задержку

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

Разница в 5–10 мс сетевой задержки нелинейно влияет на итоговый результат теста сразу в двух местах. При сценарии "closed workload" (виртуальный пользователь ждёт ответа перед следующим запросом) заниженная задержка round-trip означает, что каждый виртуальный пользователь успевает сделать больше запросов в единицу времени, чем сделал бы реальный — итоговый RPS в отчёте оказывается завышен. А при высокой конкурентности заниженная задержка означает, что соединения на сервере закрываются быстрее, освобождая сокеты, память под буферы и воркеры быстрее, чем это происходило бы с реальными пользователями — то есть тест недооценивает пиковое потребление ресурсов сервером под тем же номинальным RPS.

Механизм здесь тот же самый, что в известном мифе о близости сервера к пользователю — разница лишь в том, что там речь о восприятии одного пользователя, а здесь та же логика искажает данные целого теста. Подробнее — в статье про миф «чем ближе сервер, тем быстрее». Отправная точка по реальным цифрам задержки между регионами, чтобы прикинуть, насколько занижен RTT в вашем случае, — таблица ориентиров по задержке между локациями.

Место запуска генератораRTT до сервераЧто искажается
Тот же датацентр/регион, что и сервер1–5 мсRPS завышен, потребление ресурсов сервером занижено
Домашний ноутбук в том же городе10–30 мсЧастично искажено, зависит от сценария (open/closed workload)
Домашний ноутбук в другой стране от сервера50–200+ мсТест может упереться в саму задержку раньше, чем в лимиты сервиса
Реальные пользователи (обычно распределены)Разброс по регионамТо, что тест должен был приблизить, но не приблизил

Правильный подход: отдельная машина рядом с целью

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

Практическая рекомендация — держать генератор либо в том же облачном регионе, что и целевая инфраструктура (если нужно измерить максимальную пропускную способность самого сервиса без магистральной задержки), либо в регионе, откуда реально приходит основная масса пользователей (если нужно измерить пользовательский опыт с реалистичным RTT). Это разные вопросы, и один тест редко отвечает на оба одинаково хорошо — стоит явно решить, что именно вы измеряете в конкретном прогоне, прежде чем выбирать локацию машины с генератором.

# пример: разворачиваем k6 на отдельном сервере рядом с целью
ssh root@load-generator.example.net

apt update && apt install -y gnupg2 curl
curl -s https://dl.k6.io/key.gpg | gpg --dearmor | tee /usr/share/keyrings/k6-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | tee /etc/apt/sources.list.d/k6.list
apt update && apt install -y k6

# запускаем тест в фоне и логируем результат отдельно от терминала —
# так отвал вашего домашнего соединения к серверу-генератору
# не оборвёт сам тест
nohup k6 run --out json=result.json script.js > k6.log 2>&1 &

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

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

Как понять, что тест уже искажён

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

  • Сравните заявленную полосу вашего интернет-канала с расчётной потребностью теста (RPS × средний размер запроса и ответа) — если они близки, канал наверняка стал узким местом раньше сервиса.
  • Проверьте загрузку CPU на самом ноутбуке во время теста — если процесс-генератор уже упирается в 100% на одном или нескольких ядрах, а метрики сервиса ещё не показывают деградации, тест измеряет мощность ноутбука, а не сервиса.
  • Оцените RTT между ноутбуком и целью и сравните его с ожидаемым RTT реальных пользователей — если разница больше 20–30 мс, при closed workload это заметно исказит итоговый RPS.
  • Запустите тот же сценарий с вдвое меньшей интенсивностью и посмотрите, снижаются ли ошибки и задержка пропорционально — если снижение непропорционально резкое, вероятно, при более высокой интенсивности вы упирались не в лимиты сервиса, а в один из трёх факторов выше.

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

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

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

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли вообще запускать нагрузочные тесты с ноутбука, если нужна только грубая прикидка?

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

Насколько мощным должен быть сервер под генератор нагрузки?

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

Что если у меня нет отдельного облачного региона рядом с продом — можно тестировать из другого региона?

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

Как быть с VPN, через который в компании обязателен весь трафик?

Для целей нагрузочного теста лучше исключение из туннеля именно для сервера-генератора (по согласованию с сетевым/безопасностным отделом) — иначе корпоративный VPN-шлюз почти наверняка станет незапланированным узким местом, которое попадёт в отчёт как «проблема сервиса».

Стоит ли использовать облачный сервис нагрузочного тестирования вместо своего сервера?

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

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →