Перейти к содержимому
27 мар. 2025 г.·7 мин чтения

Автоскейлинг open-weight моделей без дёрганья кластера

Автоскейлинг open-weight моделей требует разных сигналов на рост и спад. Разберём очередь, пороги, окна и защиту от ложных всплесков.

Автоскейлинг open-weight моделей без дёрганья кластера

Почему кластер дёргается на всплесках

Кластер начинает дёргаться, когда принимает короткий всплеск за новый нормальный уровень нагрузки. Очередь запросов LLM выросла на минуту, автоскейлер увидел превышение порога и сразу добавил реплики. Через пару минут поток вернулся к обычному уровню, а новые инстансы только начали прогреваться.

В этом и ловушка. Для роста и для спада нельзя опираться на один и тот же сигнал с одинаковой логикой. На scale up нужен быстрый отклик, потому что пользователи уже ждут ответ. На scale down нужна пауза, иначе система начнёт убирать реплики при первой же передышке между пачками запросов.

С GPU это заметнее, чем с обычными сервисами. Холодный старт занимает время: нужно поднять процесс, загрузить веса, прогреть память, иногда восстановить маршруты и кэши. Пока новая реплика готовится, очередь продолжает расти. Автоскейлер снова видит рост и может добавить ещё инстансы. В этот момент кластер реагирует уже не на текущую нагрузку, а на картину с запаздыванием.

Дальше случается обратный перекос. Нагрузка уже спала, а запущенные реплики наконец вошли в строй. Метрики резко улучшаются, и система решает, что лишние узлы пора убрать. Если scale down настроен слишком рано, кластер тут же теряет тёплые реплики. Следующий короткий пик снова бьёт по очереди, и цикл повторяется.

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

Ранний scale down тоже дорогой. Вы теряете уже прогретую память, локальные кэши и запас на следующий пик. Для open-weight моделей это особенно неприятно: чем тяжелее модель, тем выше цена ошибки. Если скейлер не различает короткий всплеск и устойчивый рост, кластер всё время то догоняет нагрузку, то убегает от неё.

Хорошая реакция выглядит скучно: на рост система отвечает быстро, на спад медленно. Если обе стороны настроены одинаково, кластер почти всегда начинает качаться.

Какие сигналы смотреть рядом с очередью

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

Поэтому очередь лучше считать по каждой модели отдельно. У одной модели выше скорость генерации, у другой длиннее ответы, у третьей меньше свободных GPU-слотов. Общий график по всему кластеру это скрывает.

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

Не меньше пользы даёт состояние реплик. Смотрите не только на общее число инстансов, но и на то, сколько из них реально заняты и сколько простаивают. Бывает, что реплики есть, но они сидят на длинных ответах, и новую волну запросов уже некуда посадить.

Скорость прихода запросов тоже лучше держать в двух окнах: за 1 минуту и за 5 минут. Короткое окно ловит резкий всплеск, длинное показывает, это шум или новый уровень нагрузки. Если минутный rate вырос вдвое, а пятиминутный почти не сдвинулся, часто хватает переждать. Если растут оба, нагрузка меняет режим.

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

Обычно хватает четырёх связок: очередь по модели и время ожидания до первого токена; занятые и свободные реплики вместе со скоростью прихода запросов за 1 и 5 минут; средняя длина ответа рядом с долей длинных запросов; число запросов в очереди рядом с числом активных генераций.

Такая картина лучше показывает, что происходит на самом деле: краткий всплеск, медленное накопление хвоста или перекос по одной тяжёлой модели. Тогда scale up и scale down можно строить по поведению нагрузки, а не по одному шумному счётчику.

Когда поднимать реплики

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

Окно наблюдения для scale up лучше держать коротким. Часто хватает 15-30 секунд. Такое окно отсекает случайный шум, но не пропускает настоящий всплеск. Если смотреть на среднее за 2-3 минуты, кластер начнёт реагировать слишком поздно.

Один только размер очереди даёт слабый сигнал. Смотрите ещё и на её ускорение. Очередь из 20 запросов может быть нормальной, если она стоит на месте. Та же очередь уже тревожна, если полминуты назад было 6, потом 12, а теперь 20. Важен не сам факт роста, а его темп.

На практике для scale up обычно хватает нескольких условий. Очередь должна держаться выше порога хотя бы 20-30 секунд. Скорость роста очереди должна оставаться положительной. Прогноз по ожиданию должен подбираться ко времени старта новой реплики. И после добавления одной реплики система всё ещё не должна успевать разбирать хвост.

Время старта новой реплики надо учитывать отдельно. Если запуск занимает 90 секунд, триггер должен срабатывать до того, как пользователи проживут эти 90 секунд в очереди. Иначе решение формально верное, но бесполезное. Простая проверка помогает: если очередь растёт на 3 запроса в секунду, а новая реплика появится только через полторы минуты, за это время накопится ещё 270 запросов.

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

Когда убирать реплики

Самая частая ошибка - снимать реплики почти по тем же правилам, по которым вы их добавляете. Тогда кластер начинает метаться: очередь чуть выросла, вы подняли инстанс, очередь схлопнулась, инстанс сразу убрали, через минуту всё повторилось.

Для scale down нужен отдельный, более низкий порог и более длинное ожидание. Если scale up срабатывает быстро, то scale down должен быть скучным и медленным. Это обычная гистерезисная схема: поднимаем рано, убираем поздно.

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

Для scale down полезно проверять сразу несколько простых условий: очередь долго держится ниже нижнего порога, средняя загрузка GPU и токены в секунду тоже падают, новые запросы не приходят короткими волнами каждые 20-30 секунд, а после последнего scale up уже прошёл cool-down.

Cool-down после scale up обязателен. Новая реплика не помогает мгновенно: модель загружается в память, кэш набирается, балансировщик начинает распределять трафик с задержкой. Если в этот момент смотреть только на очередь, вы почти наверняка уберёте лишнее слишком рано.

Ещё одно простое правило: не снимайте последнюю тёплую реплику. Одна готовая к работе копия почти всегда дешевле, чем серия холодных стартов утром или после короткой паузы днём. Для больших open-weight моделей это особенно заметно: повторная загрузка весов и прогрев легко стоят пользователю лишних секунд.

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

Рабочая схема обычно выглядит так: scale up по очереди выше 8 запросов в течение 1-2 минут, scale down только если очередь ниже 2 и низкая загрузка держатся 10-15 минут. Числа у всех разные, но логика почти всегда одна: добавлять быстро, убирать осторожно.

Как настроить пороги и сглаживание

Упростите свой LLM-контур
Подключите единый API и оставьте команде меньше ручной обвязки.

Резкий scale up и такой же резкий scale down обычно появляются из-за одной ошибки: скейлер верит сырой метрике без задержки и без запаса. Для open-weight моделей это почти всегда плохая идея. Очередь меняется быстрее, чем поднимается новая реплика.

Порог на рост и порог на спад не должны совпадать. Если вы добавляете реплики при очереди выше 8, не убирайте их сразу, как только очередь вернулась к 7. Лучше дать запас: расти при устойчивом значении выше 8, а снижаться только когда очередь долго держится ниже 3-4. Такой разрыв между порогами убирает лишние качели.

Окна сглаживания тоже должны быть разной длины. На рост смотрите короткое окно, чтобы не пропустить всплеск. На спад берите длинное окно, чтобы кластер не схлопывался после минутного затишья. Часто работают такие ориентиры: для scale up окно 30-60 секунд, для scale down 5-10 минут. Для роста полезнее смотреть p95 длины очереди или времени ожидания, а для спада - медиану и долю времени с почти пустой очередью. Среднее лучше оставить фоном, а не триггером.

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

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

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

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

Пошаговая схема настройки

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

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

  1. Снимите метрики минимум за 7 дней. Отдельно отметьте обычные часы, пики и редкие всплески.
  2. Измерьте полный путь старта реплики: запуск контейнера, загрузку весов, прогрев модели и момент, когда реплика уже принимает трафик.
  3. Поставьте первый порог для scale up с учётом времени прогрева. Если реплика выходит в работу за 90 секунд, порог должен срабатывать раньше, чем пользователи упрётся в длинное ожидание.
  4. Для scale down задайте другой порог и более длинную задержку. Убирать реплики по тому же сигналу, что и добавлять, почти всегда плохая идея.
  5. Прогоните эти правила на логах прошлых пиков и посмотрите, где кластер добавил бы мощности вовремя, а где начал бы метаться туда-сюда.

Практичное правило простое: scale up должен реагировать быстрее, чем scale down. Например, рост очереди можно проверять по окну 60-90 секунд, а снижение нагрузки - по окну 10-15 минут. Тогда кластер не будет сжиматься после каждого короткого провала между пачками запросов.

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

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

Пример с вечерним пиком

Оставьте код как есть
Меняйте только base_url и не трогайте текущие интеграции.

В 18:00 трафик пошёл вверх. За 10 минут входящий поток вырос примерно вдвое, и это тот случай, где кластер легко начинает дёргаться. Новые реплики не появляются мгновенно, а очередь уже растёт.

Ситуацию портит смешанный трафик. Часть запросов - короткие чаты на пару десятков токенов. Другая часть - длинная генерация, которая держит GPU заметно дольше. Поэтому одна и та же длина очереди в 18:02 и в 18:08 может означать совсем разную нагрузку.

Для scale up в такой момент полезнее смотреть не на голое число запросов в очереди, а на то, как быстро очередь растёт и сколько живёт самый старый запрос. Если очередь прибавляет по 20-30% за минуту, а возраст головы очереди тоже растёт, кластер уже не успевает. Даже если средняя загрузка GPU пока выглядит терпимо, реплики лучше поднимать сразу.

Под вечерним пиком я бы дал приоритет четырём сигналам: скорости роста очереди за 2-3 минуты, возрасту самого старого запроса, p95 времени ожидания до старта генерации и числу активных длинных генераций.

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

После 18:10 входящий поток уже может пойти вниз, но кластеру рано расслабляться. В системе ещё висят старые длинные запросы, которые пришли на пике. Снаружи кажется, что буря прошла, а внутри GPU всё ещё заняты, и очередь не опустела.

Поэтому сигналы для scale down должны быть строже, чем для scale up. Снижать реплики стоит только когда несколько условий держатся вместе хотя бы 10-15 минут: новая нагрузка правда упала, очередь почти исчезла или стабильно сокращается, возраст головы очереди падает, занятость GPU и длина генераций тоже идут вниз.

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

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

Частые ошибки

Чаще всего автоскейлинг ломают не сложные алгоритмы, а слишком прямолинейные правила. Команда берёт длину очереди, ставит один порог и ждёт, что кластер сам успокоится. На практике такой контроллер реагирует на каждый шум и сам создаёт лишние движения.

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

Не меньше проблем даёт одинаковый порог для scale up и scale down. Если вы добавляете реплики при очереди 20 и убираете их тоже около 20, система начинает качаться туда-сюда. Нужен заметный разрыв между этими решениями. Например, рост можно включать после устойчивого превышения 30, а спад - только когда очередь долго держится ниже 10.

Ещё одна ловушка - забыть про время загрузки весов. Open-weight модель не поднимается мгновенно: контейнер стартует, веса читаются с хранилища, модель прогревает память, иногда проходит warm-up запрос. Если это занимает 2-5 минут, поздний scale up почти бесполезен.

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

И ещё одна ошибка - принимать единичный всплеск за новую норму. Один крупный batch, длинные промпты или разовый внутренний прогон легко рисуют новый тренд на 3-4 минуты. Сравните длину очереди с возрастом запросов, скоростью прихода и временем ответа. Если остальные метрики молчат, не меняйте размер кластера слишком резко.

Перед запуском полезно прогнать три сценария на вчерашнем трафике: короткий пик, ступеньку нагрузки и плавный спад. Если сигналы scale up и scale down ведут себя по-разному в каждом случае, настройка уже близка к рабочей.

Быстрый чек-лист перед запуском

Проверьте свой трафик на RU LLM
Прогоните свои запросы через один endpoint и оцените разные варианты без новой обвязки.

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

  • Порог на рост и порог на спад должны быть разными. Если оба решения живут рядом, например рост при очереди 12 и спад при 10, кластер начнёт ходить туда-сюда даже при обычном шуме.
  • После scale up нужен cool-down. Новые реплики не начинают помогать мгновенно: модель надо поднять, прогреть, иногда загрузить веса в GPU-память. Если в этот момент система снова оценивает очередь без паузы, она легко добавит лишние реплики.
  • Хотя бы одна тёплая реплика должна оставаться всегда. Иначе после спада вы сэкономите пару GPU-минут, но следующий запрос упрётся в холодный старт и очередь снова подскочит.
  • Очередь нужно считать по конкретной модели, а не по всему пулу сразу. Если у вас Qwen 3 перегружена, а соседняя модель простаивает, средняя цифра по кластеру скроет проблему и скейлер отреагирует поздно.
  • Проверьте настройки на данных вчерашнего пика, а не на спокойном часе. Нормальный тест показывает не только момент роста, но и то, как система ведёт себя через 10-15 минут, когда нагрузка спадает.

Быстрый тест тоже простой: возьмите вчерашний интервал с самым длинным хвостом очереди, прокрутите его в симуляции и посмотрите на три вещи. Сколько раз скейлер добавил реплики, как долго они оставались в работе и был ли повторный всплеск сразу после scale down.

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

Что делать дальше

Лучший способ успокоить автоскейлер - сначала сузить задачу. Не пытайтесь сразу настроить весь парк моделей. Возьмите одну open-weight модель, один тип железа и один понятный профиль трафика, например короткие чат-запросы в рабочие часы.

На таком наборе проще увидеть, где очередь растёт по делу, а где кластер сам создаёт шум. Это полезнее, чем сразу писать общие правила на все случаи. Общие правила почти всегда ломаются, когда в одном кластере смешиваются короткие и длинные запросы.

Дальше стоит сделать несколько приземлённых шагов: собрать 5-7 дней метрик по длине очереди, времени ожидания, загрузке GPU и числу активных реплик; посчитать цену лишней реплики за час и сравнить её со штрафом за задержку; записать пороги scale up и scale down в runbook для дежурной команды; провести один тест с искусственным вечерним пиком и проверить, как кластер возвращается в норму через 15-30 минут после спада.

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

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

Если команде нужен единый API и российский контур для LLM, часть платформенной рутины можно снять готовым слоем. Например, RU LLM даёт OpenAI-совместимый endpoint, маршрутизацию между моделями и хранение логов с биллингом внутри РФ. Пороги автоскейлинга это не настроит, но инфраструктурную схему делает заметно проще.

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

Часто задаваемые вопросы

Как понять, что кластер уже дёргается?

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

Почему нельзя скейлить только по длине очереди?

Очередь показывает только количество, но не тяжесть запросов. Два длинных ответа могут занять GPU сильнее, чем десять коротких, поэтому рядом лучше смотреть время до первого токена, возраст самого старого запроса и число активных генераций.

Какие метрики лучше смотреть рядом с очередью?

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

Когда поднимать новые реплики?

Scale up стоит включать рано, пока пользователи ещё не прожили всё время прогрева новой реплики в очереди. На практике команда часто берёт короткое окно в 15–30 секунд и проверяет не только размер очереди, но и её темп роста.

Почему вредно рано убирать реплики?

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

Какой cool-down ставить после scale up?

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

Нужно ли всегда держать одну тёплую реплику?

Да, в большинстве случаев это дешевле серии холодных стартов. Для тяжёлых open-weight моделей одна тёплая копия часто экономит больше времени и нервов, чем стоит её простой в тихий период.

Стоит ли смешивать batch и онлайн-запросы в одной очереди?

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

С чего начать настройку порогов?

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

Как проверить настройки до запуска в прод?

Прогоните правила на вчерашних пиках и посмотрите, сколько раз скейлер добавил и убрал реплики за час. Если видите два-три лишних цикла на одном и том же участке, увеличьте окно на scale down и только потом трогайте сами пороги.