Чому балансування GPU ламається під навантаженням
Черга завдань на кілька обчислювачів виглядає рівною лише на папері. У реальному потоці короткі запити до моделі перемішані з тривалими завданнями навчання, а частина з них надходить пачками від одного сервісу.
Поки балансування GPU орієнтується на кількість з’єднань, а не на фактичну зайнятість обчислювача, розподіл розходиться з реальним навантаженням.
Далі спрацьовує лавина: вузол, що отримав кілька важких завдань, тримає їх десятки хвилин, нові запити накопичуються в черзі, і час відгуку зростає нелінійно.
Сусідні обчислювачі в цей час простоюють — нерівномірне завантаження перетворюється на простій обчислювача, який оплачує замовник.
Типові схеми на кшталт кругового перебору або вибору вузла з найменшою кількістю активних запитів ламаються на цьому одразу: вони не бачать довжину черги, зарезервовану пам’ять і час виконання попередніх завдань.
Перевантаження вузла виникає не від напливу трафіку, а від того, що диспетчер не знає, скільки роботи вже прийнято кожним обчислювачем.
Звідси й знайомий симптом: середня зайнятість парку тримається на нормальному рівні, а час очікування окремих завдань зростає в рази. Нижче розбираємо, як це закривається — через контроль черги, ваги вузлів і маршрутизацію за фактичною зайнятістю, а не за лічильником підключень.
Ефект від розподілу навантаження: черга, простій, відмови
При статичному закріпленні завдань за конкретними прискорювачами завантаження розходиться: один вузол рахує на межі, другий простоює.
Черга зростає на перевантаженому вузлі й порожніє на вільному, а хвіст очікування задає найдовший запит попереду — короткі завдання стоять за довгими.
Керований розподіл знімає це на рівні планування.
Планувальник дивиться на вільну пам’ять обчислювача, тип профілю, пріоритет завдання й історію відмов вузла; ізоляція NVIDIA дозволяє нарізати один прискорювач на незалежні профілі, тому велика модель і потік дрібних інференсів не конкурують за той самий ресурс.
Вузол, що видає помилки або втратив зв’язок із сховищем моделей, виводиться з ротації до усунення причини, а не після падіння завдань.
Метрики змінюються передбачувано. Завантаження обчислювачів вирівнюється: замість розкиду 40–90% тримається 80–85% на всьому парку.
Час очікування в черзі скорочується в рази завдяки тому, що медіана перестає залежати від хвоста, а пропускна здатність зростає в 1,8–2 раза за тієї ж кількості прискорювачів.
Клас відмов теж зсувається: зникають помилки «завдання не запустилося через брак пам’яті» й таймаути через перевантаження вузла, залишаються поодинокі збої конкретного обчислювача, які гасяться повтором на сусідньому профілі.
Під піком система деградує м’яко — зростає час очікування низькопріоритетних завдань, але термінові продовжують проходити, замість того щоб валити весь вузол разом із чужими завданнями.
Режими балансування GPU: порівняння підходів
Режим розподілу вирішує, на який вузол із прискорювачем піде кожен запит, і саме від нього залежать утилізація парку та розкид часу відповіді. При невдалому виборі частина вузлів простоює, а черга накопичується на одному — хвіст затримок зростає швидше, ніж середнє навантаження.
Ми підбираємо режим під профіль навантаження: довжину контексту, частку критичних викликів, допустимий час очікування для фонових завдань. Нижче — підходи, які трапляються на практиці, та їхні обмеження.
| Режим | Коли підходить | Обмеження |
|---|---|---|
| Циклічний перебір | Однотипні короткі запити, вузли однакової потужності | Не враховує фактичну довжину завдань: один вузол накопичує повільні виклики, решта простоюють |
| За поточним навантаженням | Змішаний потік, різна довжина генерації | Потрібен постійний збір метрик із вузлів; при рідкому опитуванні рішення запізнюються |
| Спільна черга з пріоритетами | Критичні виклики поряд із фоновими пакетними завданнями | Низькопріоритетні запити чекають довше — потрібен контроль, щоб вони не голодували |
| Резервування вузлів | Сервіс із жорсткою вимогою до часу відповіді | Виділена частина потужностей простоює в години низького навантаження |
| Прив’язка завдань до вузлів | Великі моделі з довгим завантаженням ваг | Знижується гнучкість: вузол не можна швидко віддати під іншу модель |
На практиці частіше працює комбінація: пріоритетна черга для інтерактивних викликів плюс невеликий резерв вузлів під них, а важкі пакетні завдання йдуть у спільний пул.
Такий набір дає передбачуваний час відповіді без простоюючих потужностей, але перевіряється лише на реальному профілі запитів — тому ми знімаємо його до налаштування, а не після.
Етапи налаштування розподілу запитів між вузлами
Порядок робіт майже не залежить від того, які моделі обслуговуються: спочатку вимірюємо реальний потік запитів, потім проєктуємо розподіл, і лише потім вмикаємо його на бойовому трафіку.
Без картини потоків будь-яке балансування перетворюється на підбір ваг навмання, і перший же сплеск показує, що черга зростала не там, де її чекали.
- Аудит потоків завдань. Знімаємо профіль звернень за кілька діб: які запити короткі й часті, які важкі та рідкісні, де піки, скільки займає очікування в черзі й на яку частку відмов припадає перевантаження. На виході — таблиця класів завдань з їхньою вагою та допустимим часом відповіді.
- Схема розподілу. Вибираємо, що кладемо в основу рішення: кількість активних запитів на вузлі, фактичне завантаження пам’яті прискорювача або черга очікування. Для коротких завдань ваги одні, для довгих — інші: змішувати їх в одному пулі майже завжди означає, що важкий запит тримає місце, поки легкі простоюють.
- Налаштування пріоритетів і черг. Розділяємо потоки за класами, задаємо граничний час очікування й правила повторних спроб, описуємо поведінку при виході вузла з ладу — запит має йти на живий вузол, а не чекати відмови. Тут же фіксуємо, які завдання допускають зниження точності розрахунку заради швидкості.
- Перевірка під навантаженням. Проганяємо профіль, знятий на аудиті, із запасом у кілька разів і зі штучними піками: дивимося, як змінюється час відповіді на 95-му процентилі запитів, як вирівнюється завантаження вузлів і що відбувається при вимкненні одного з них.
- Передача експлуатації. Віддаємо схему розподілу, налаштовані пороги, дашборди з ключовими метриками та інструкцію з додавання нових вузлів — щоб розширення не вимагало перезбирання логіки з нуля.
Опишіть ваш потік завдань — повернемося з планом робіт та оцінкою.
Комплект конфігурацій, схем і регламентів
Роботи закінчуються не демонстрацією, а передачею комплекту, з яким ваша команда експлуатує балансування прискорювачів і змінює його без нас. Усе оформлено так, щоб новий інженер розібрався за документами, а не за листуванням.
- Конфігурація вузлів — значення параметрів розподілу запитів, лімітів пам’яті та пріоритетів черг, із позначкою, що можна змінювати вільно, а що пов’язано із сусідніми налаштуваннями.
- Схеми потоків запитів — шлях від вхідної точки до конкретного прискорювача, точки відмови та поведінка системи, коли вузол випадає з ротації.
- Регламент обслуговування — порядок планових робіт: виведення вузла, прогрів, повернення в роботу, оновлення профілів без зупинки сервісу.
- Політики доступу — ролі та права: хто змінює параметри балансування, хто бачить лише метрики, як фіксується кожна зміна.
- Документація по системі — архітектура, словник метрик, розбір типових відмов і послідовність дій при кожній.
- Набір перевірок — панель спостереження, пороги спрацювання та маршрути сповіщень для чергової зміни.
- Процедура відкату — як повернути попередній профіль конфігурації однією операцією і чим підтверджується, що повернення пройшло успішно.
Комплект живе разом із системою: зміни вносимо в ті самі документи й схеми, а не накопичуємо окремими листами.
Кейс: як скоротили простій обчислювачів утричі
До нас прийшла команда, яка обслуговувала кластер із різнорідними прискорювачами: частина машин рахувала навчання, частина — виведення моделей у реальному часі.
Планувальник Kubernetes розподіляв контейнери за залишковим принципом, і на двох вузлах із восьми постійно висіла черга, поки сусідні простоювали. Короткі запити до моделей чекали по кілька хвилин, а навчання зривало терміни.
Ми розвели пул вузлів за класами завдань, вивели зайнятість прискорювача й відеопам’яті в окремий ресурс планування та додали перевірку живості перед постановкою в чергу — обробник, що впав, перестав тримати за собою місце. Окремо переписали пріоритети: короткий запит до моделі більше не стає за багатогодинним навчанням.
Метрики до й після: простій обчислювачів скоротився втричі, час очікування в черзі — з шести хвилин до сорока секунд. Розкид завантаження вузлів стиснувся з 35–95% до 70–85%, тобто вирівнювання навантаження перестало залежати від того, куди планувальник поставив перше завдання. Частка запитів, доведених до відповіді без перезапуску, зросла до 96%.
Результат впровадження — той самий парк машин тепер тримає удвічі більше одночасних запитів, а додати прискорювачі можна без переписування логіки сервісів. Правила вирівнювання та пріоритетів залишилися в конфігурації з коментарями, щоб команда правила їх сама.
Чим розподіл запитів відрізняється від резервування вузлів?
Резервування вузлів — це про наявність запасного вузла поряд. Другий вузол тримають запущеним і синхронізованим з основним, але потік завдань усе одно йде в один — робочий. Резерв вмикається лише тоді, коли основний відвалився, і до цього моменту він не обробляє нічого.
Перемикання при збої закриває частину ризику: сервіс не падає цілком, а через паузу продовжує роботу. Але в цієї схеми є межа — на час перемикання запити чекають, частина довгих завдань обривається на середині, а подвоєння парку не дає приросту пропускної здатності.
Розподіл запитів працює на рівень вище. Потік завдань дробиться на порції та розкладається по всіх живих вузлах з урахуванням їхньої поточної зайнятості.
Відмова вузла не перериває потік: запити, які до нього не дійшли або не встигли завершитися, йдуть на сусідні, і відмовостійкість стає наслідком рівномірного завантаження, а не дублювання.
Щоб це працювало, потрібен облік зайнятості кожного вузла, довжина черги й час відповіді, перевірка живості та коректний повтор для перерваних завдань.
Окрема тонкість — довгі важкі запити: без ліміту на порцію вони займають вузол цілком, і черга накопичується навіть при вільних сусідах.
Межа проста. Резервування відповідає на питання «чи є куди переїхати», розподіл — «чи не простоює потужність і чи не накопичується черга». Простій резерву при нерівномірному навантаженні не лікується дублюванням: одна копія захлинається, друга нудьгує.
Часті технічні питання щодо розподілу навантаження
Балансувальник стає між сервісом і пулом прискорювачів: приймає запит, вибирає вузол за завантаженням пам’яті та довжиною черги, повертає результат коду, що викликав.
Тому перед впровадженням ми розбираємо чотири типових сумніви інженерів — сумісність із поточною схемою, метрики контролю, поведінку при відмові вузла та обсяг правок у застосунку.
Чи сумісно це з нашою поточною схемою?
Так, якщо застосунок звертається до моделі по мережі, а не тримає її всередині свого процесу. Ми описуємо точки підключення та зберігаємо попередні адреси: сервіс не знає, що вузлів стало більше, — маршрут змінюється на боці балансувальника.
Якими метриками контролюється розподіл?
Частка запитів на кожному вузлі, глибина черги очікування, час до першої відповіді та кількість повторів. Розкид завантаження між вузлами вище 10-15% — сигнал, що ваги або поріг перегріву виставлені неправильно.
Що відбувається при відмові вузла?
Перевірки живості виводять вузол із ротації за секунди, а запити, що вже пішли на нього, повторюються на живому вузлі. Код, що викликає, отримує відповідь, а не помилку — але лише якщо повторний виклик не створює дубля, тому обробку повторів ми перевіряємо окремо.
Чи потрібно змінювати застосунок і як проходить міграція?
Правки мінімальні: адреса звернення та підтвердження повторів. Переводимо поступово — спочатку частка запитів, потім весь обсяг, з можливістю повернути попередній маршрут одним налаштуванням. Простій на час перемикання не потрібен.
Обговоримо вашу задачу: розподіл навантаження та відмовостійкість
Щоб повернутися з оцінкою та планом, а не із загальними словами, нам потрібен зріз вашої поточної схеми: як запити потрапляють на прискорювачі, де виникає черга — на вході балансувальника, у черзі завдань, через різне завантаження інстансів або через відмови окремих вузлів.
Далі разом фіксуємо постановку задачі та межі: що вважаємо успіхом — цільове завантаження прискорювачів, час відповіді під піком, поведінку системи при випадінні одного інстанса.
- Постановка задачі — що саме балансуємо і за якою ознакою: довжина запиту, тип моделі, зайнятість пам’яті, пріоритет клієнта.
- Вихідні дані — поточна схема розгортання, режим масштабування, логи й метрики за період роботи, відомі збої та їхні наслідки.
- Оцінка та план робіт — які зміни зачіпають лише балансувальник, а де потрібне перезбирання черги або перерозподіл інстансів.
- Терміни етапів — черговість: спочатку знімаємо метрики, потім вводимо правила розподілу, далі перевіряємо поведінку під навантаженням.
- Формат співпраці — хто на вашому боці дає доступи до середовища й метрик, як узгоджуємо вікна змін.
- Критерії приймання — числові пороги завантаження й часу відповіді, за яких роботу вважаємо завершеною.
Опишіть задачу у вільній формі та додайте те, що є з метрик, — повернемося з оцінкою, планом робіт і термінами етапів.







