Налаштування балансування навантаження між GPU-інстансами
Як відсутність балансування GPU вбиває latency LLM-сервісу
Уявіть: ви запустили чотири GPU-інстанси з vLLM, але 80% запитів йде на перший сервер. Інші простоюють, а користувачі скаржаться на таймаути. Причина — не налаштоване балансування навантаження. Для LLM це критично: один довгий запит на 4000 токенів може заблокувати сервер на хвилину, поки інші — idle. У результаті p99 latency злітає до 30 секунд, а утилізація GPU падає до 25%. Типовий кластер з 4 GPU без балансування втрачає до 50% пропускної здатності.
Грамотне балансування дозволяє скоротити витрати на GPU-інфраструктуру до 40% за рахунок рівномірної утилізації. Середня економія GPU-годин після впровадження — 30% за тієї ж навантаження. P99 latency знижується в 1.7 рази порівняно з Round Robin. Наприклад, для кластера з 4 GPU економія становить до $5000 на місяць. Якщо ви зіткнулися з подібними проблемами, зв'яжіться з нами — ми підберемо оптимальну конфігурацію під ваш сценарій.
Порівняння алгоритмів балансування для LLM
| Алгоритм | Принцип роботи | Придатність для LLM | Недоліки |
|---|---|---|---|
| Round Robin | По черзі | Низька | Ігнорує завантаження: довгий запит перевантажує сервер |
| Least Connections | Мінімум активних з'єднань | Середня | Не враховує довжину запитів (токенів) |
| Least Pending Tokens | Мінімум токенів у черзі | Висока | Вимагає збору метрик з кожного бекенда |
| Custom (метрики GPU) | За завантаженням VRAM/GPU | Середня | Залежить від моніторингу, складніше в реалізації |
Документація vLLM: метрики для балансування
Least Pending Tokens — оптимальний вибір для сервісів з різнорідним навантаженням. Він використовує Prometheus-метрики vLLM (vllm:num_requests_waiting), щоби вибирати найменш завантажений інстанс. Наш досвід показує, що Least Pending Tokens кращий за Round Robin в 1.7 рази за p99 latency.
Приклад: Nginx з health checks і кастомний балансувальник
Нижче — базова конфігурація Nginx для upstream з чотирьох vLLM-серверів, з active health checks і таймаутами для streaming.
Конфігурація Nginx
upstream vllm_cluster { least_conn; server 10.0.1.10:8000 max_fails=3 fail_timeout=30s weight=1; server 10.0.1.11:8000 max_fails=3 fail_timeout=30s weight=1; server 10.0.1.12:8000 max_fails=3 fail_timeout=30s weight=1; server 10.0.1.13:8000 max_fails=3 fail_timeout=30s weight=1; keepalive 100; keepalive_requests 1000; keepalive_timeout 60s; } server { listen 443 ssl http2; server_name llm-api.internal; location /v1/ { proxy_pass http://vllm_cluster; proxy_http_version 1.1; proxy_set_header Connection ""; # Timeout для довгих streaming відповідей proxy_read_timeout 600s; proxy_send_timeout 600s; proxy_connect_timeout 5s; # Streaming: вимикаємо буферизацію proxy_buffering off; proxy_cache off; chunked_transfer_encoding on; # Circuit breaker proxy_next_upstream error timeout http_500 http_502 http_503; proxy_next_upstream_tries 2; proxy_next_upstream_timeout 10s; } location /health { proxy_pass http://vllm_cluster/health; } } Якщо потрібен більш інтелектуальний вибір бекенда — пишемо кастомний балансувальник на FastAPI, що опитує метрики в реальному часі.
from fastapi import FastAPI, Request import httpx import asyncio class LLMLeastPendingBalancer: def __init__(self, backends: list[str]): self.backends = {url: {"pending": 0, "healthy": True} for url in backends} self.client = httpx.AsyncClient(timeout=300) async def get_backend(self) -> str: """Вибираємо backend з найменшою кількістю pending токенів.""" healthy = {url: info for url, info in self.backends.items() if info["healthy"]} if not healthy: raise RuntimeError("No healthy backends") metrics = await self._fetch_metrics(list(healthy.keys())) best = min(metrics.items(), key=lambda x: x[1].get("vllm_num_requests_waiting", 0)) return best[0] async def _fetch_metrics(self, backends: list[str]) -> dict: tasks = [self._get_backend_queue(url) for url in backends] results = await asyncio.gather(*tasks, return_exceptions=True) return {url: result for url, result in zip(backends, results) if not isinstance(result, Exception)} async def _get_backend_queue(self, url: str) -> dict: response = await self.client.get(f"{url}/metrics") for line in response.text.split('\n'): if line.startswith('vllm:num_requests_waiting'): return {"vllm_num_requests_waiting": float(line.split()[-1])} return {"vllm_num_requests_waiting": 0} async def forward(self, request: Request) -> httpx.Response: backend = await self.get_backend() url = f"{backend}{request.url.path}" self.backends[backend]["pending"] += 1 try: return await self.client.request( method=request.method, url=url, content=await request.body(), headers=dict(request.headers) ) finally: self.backends[backend]["pending"] -= 1 app = FastAPI() balancer = LLMLeastPendingBalancer(["http://gpu1:8000", "http://gpu2:8000", "http://gpu3:8000"]) @app.api_route("/v1/{path:path}", methods=["GET", "POST"]) async def proxy(path: str, request: Request): return await balancer.forward(request) Чому sticky sessions критичні для LLM?
Якщо ваша LLM використовує KV-кеш prefix reuse (наприклад, загальний system prompt у чат-боті), без липких сесій кожен запит може потрапити на інший сервер — кеш марний. Рішення — consistent hashing за префіксом та sticky sessions.
def get_backend_by_prefix(prompt: str, backends: list[str]) -> str: prefix_hash = hashlib.md5(prompt[:256].encode()).hexdigest() idx = int(prefix_hash, 16) % len(backends) return backends[idx] Застосування sticky sessions збільшує cache hit ratio на 30-50%, скорочуючи latency на 20%. Без них типовий сервіс із загальним system prompt втрачає до 60% ефективності кешу.
Типові помилки при балансуванні GPU
- Використання Round Robin для LLM — призводить до нерівномірного завантаження.
- Відсутність health checks — трафік йде на впавший сервер.
- Ігнорування streaming-таймаутів — клієнти отримують 502 помилки при довгих генераціях.
- Неправильна конфігурація proxy_buffering — збільшує latency.
- Відсутність failover GPU — при збої одного інстанса весь трафік втрачається.
Як налаштувати health checks для GPU-інстансів?
| Метод | Інструмент | Складність | Особливості |
|---|---|---|---|
| Пасивні (nginx) | max_fails, fail_timeout | Низька | Не потребує додаткових налаштувань |
| Активні (nginx plus) | health_check | Висока | Точно визначає стан, але платний |
| Кастомні | HTTP /metrics | Середня | Працює тільки з vLLM та сумісними двигунами |
Що входить у налаштування балансування під ключ
- Аналіз сценаріїв навантаження (кількість запитів, довжина токенів, вимоги до latency).
- Вибір алгоритму та стеку (Nginx, кастомний балансувальник, Envoy).
- Налаштування health checks, circuit breaker, таймаутів.
- Реалізація sticky sessions (якщо потрібен KV-кеш).
- Інтеграція з моніторингом (Prometheus + Grafana дашборди).
- Документація з експлуатації та Playbook для інцидентів.
Процес роботи
- Аналітика — збір метрик поточної інфраструктури, профілювання запитів.
- Проектування — архітектура балансування, вибір алгоритмів, схема failover.
- Реалізація — розгортання конфігів або написання кастомного модуля.
- Тестування — навантажувальне тестування з вимірами p50/p99/p999 latency.
- Деплой — поетапний rollout з canary-релізом.
Строки та вартість
Базова настройка на Nginx — від 1 дня. Кастомний балансувальник з підтримкою Least Pending Tokens — від 3 до 5 днів. Вартість розраховується індивідуально, виходячи зі складності інфраструктури та вимог до відмовостійкості. Гарантуємо стабільність сервісу після впровадження — наші інженери з 5+ років досвіду в ML-інфраструктурі виконують роботу під ключ. Типовий ROI впровадження — 6 місяців.
Моніторинг розподілу навантаження
Після впровадження відстежуйте: розподіл RPS (має бути рівномірним ±20%), queue depth на кожному бекенді, error rate, latency p99. Налаштуйте алерт: «один бекенд приймає >80% трафіку» — сигнал про збій. При правильному налаштуванні p99 latency знижується до 5 секунд, а утилізація GPU підвищується до 95%. Cache hit ratio досягає 70% при використанні sticky sessions. Ми також навчаємо команду роботі з дашбордами.
Зв'яжіться з нами для попереднього аудиту — ми оцінимо поточну конфігурацію та запропонуємо оптимальне рішення. Замовте консультацію — допоможемо з вибором стратегії балансування для вашого GPU-кластера.







