MAATRIX / Блог / Один клиент даёт 80% нагрузки: когда выносить его на отдельный сервер

Один клиент даёт 80% нагрузки: когда выносить его на отдельный сервер

MAATRIX

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

Как крупный клиент превращается в шумного соседа внутри вашей же инфраструктуры

Термин «шумный сосед» обычно применяют к чужой инфраструктуре — к соседям по shared-хостингу или по гипервизору VPS, где чужая активность бьёт по вам, а причину не видно (механика разобрана в статье про соседей по shared-хостингу). У мультиклиентного B2B/SaaS-проекта та же механика возникает внутри собственной инфраструктуры, только с обратным знаком ответственности: сосед не чужой и невидимый, а свой собственный, платящий клиент, чью нагрузку вы обязаны понимать и объяснять.

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

Стоит различать два сценария, потому что они требуют разных действий:

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

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

Что это значит для остальных клиентов: риски «как есть»

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

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

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

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

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

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

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

Арендовать VPS

Квотирование и rate limiting: простое решение первого порядка

Прежде чем инвестировать в выделение отдельного сервера, разумно спросить: а нельзя ли просто не дать одному клиенту забирать больше своей справедливой доли ресурсов? Это и есть смысл квотирования и rate limiting на уровне приложения — ограничить потребление на стороне логики, не трогая инфраструктуру.

Механика rate limiting подробно разобрана в статье как работает rate limiting: считается число запросов от источника за окно времени, и всё сверх порога либо отклоняется с HTTP 429, либо ставится в очередь с задержкой. Для B2B/SaaS-проекта источником для лимита должен быть не IP (у крупного клиента запросы могут идти с одного корпоративного шлюза, и IP-лимит либо не сработает, либо случайно заденет чужой трафик за тем же NAT), а идентификатор клиента — API-ключ, tenant ID, ID организации.

Пример на уровне nginx с ключом по заголовку, который проставляет API-гейтвей после аутентификации:

limit_req_zone $http_x_tenant_id zone=per_tenant:10m rate=50r/s;

location /api/ {
    limit_req zone=per_tenant burst=100 nodelay;
    limit_req_status 429;
    proxy_pass http://backend;
}

Цифры rate и burst — не универсальные значения, а параметры, которые нужно подбирать под профиль конкретного API и нормальную нагрузку реальных клиентов, иначе легко случайно ограничить и здоровый трафик.

На уровне приложения то же самое реализуется точнее — с учётом не только числа запросов, но и «веса» операции (тяжёлый экспорт стоит дороже простого GET), обычно алгоритмом токенного ведра (token bucket) на клиента:

def check_quota(tenant_id, cost=1):
    bucket = get_bucket(tenant_id)
    bucket.refill()
    if bucket.tokens < cost:
        raise QuotaExceeded(tenant_id, retry_after=bucket.time_to_refill(cost))
    bucket.tokens -= cost

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

Важно спроектировать лимит так, чтобы он не стал новым источником инцидентов — жёсткая стена без предупреждения выглядит для клиента как поломка сервиса. Принцип «предупреждать заранее, а не обрывать резко» разобран в статье про квоту, которая предупреждает, а не роняет сервис — те же пороги применимы и здесь, только вместо квоты на диск — квота на частоту запросов или на долю ресурсов.

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

Когда квот уже недостаточно и пора думать об изоляции

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

Лимит бьёт по легитимной нагрузке. Если 80% реальной, оплаченной нагрузки формирует один клиент, а не аномалия на его стороне, rate limit, достаточный для защиты остальных, начинает мешать именно ему — тому, ради кого продукт существует в текущем масштабе. Квотирование превращается из инструмента справедливости в инструмент, который душит самого важного пользователя.

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

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

Достигнут технический потолок общего сервера. Если совокупная нагрузка даже после квотирования упирается в реальный потолок CPU, памяти или I/O, обслужить всех можно либо апгрейдом железа под всех сразу, либо выносом самого тяжёлого потребителя отдельно.

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

Выделение на отдельный сервер: что даёт и чего стоит

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

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

Чего это стоит на практике — и здесь важно быть честным, а не продавать изоляцию как решение без побочных эффектов:

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

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

Критерий решения: платит клиент за выделение или нет

После разбора рисков «как есть» и вариантов решения главный практический вопрос — не «а не пора ли уже», а «кто оплачивает эту сложность». Здесь есть простой рабочий критерий.

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

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

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

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

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

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

Арендовать VPS

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

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

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

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

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

Как объяснить крупному клиенту, что для него вводят лимиты, если раньше их не было?

Стоит формулировать это как защиту предсказуемости сервиса для всех клиентов, включая его самого, и предлагать выделенный тариф как альтернативу лимитам, а не наказание. Большинство крупных клиентов воспринимают отдельную инфраструктуру с гарантированной производительностью как апгрейд.

Что если крупных клиентов, дающих непропорциональную нагрузку, несколько, а не один?

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

Нужно ли выносить клиента на отдельный физический сервер или хватит VM на том же хосте?

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

Есть риск, что после выделения клиент переполнит и свой отдельный сервер?

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

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

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

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