Балансировщик нагрузки: как работает и зачем нужен
Балансировщик нагрузки (Load Balancer) — это компонент распределённой системы, который распределяет входящие запросы между несколькими серверами. Он стоит «перед» серверами приложений и решает три задачи: распределение нагрузки (чтобы один сервер не перегружался), обеспечение отказоустойчивости (если один сервер упал — запросы идут на другие) и масштабирование (добавил сервер — балансировщик автоматически направляет на него часть трафика).
Без балансировщика любая распределённая система упирается в single point of failure — один сервер БД, один веб-сервер, одна точка отказа. С балансировщиком появляется возможность горизонтального масштабирования: вместо одного мощного сервера — десять обычных, которые вместе обслуживают ту же нагрузку.
О чём эта статья
Эта статья — практический гайд по балансировщикам нагрузки для backend-разработчиков, DevOps/SRE-инженеров и архитекторов. Разберём:
- Что такое Load Balancer и зачем он нужен
- L4 vs L7 балансировка — на каком уровне работает и что умеет
- Алгоритмы распределения трафика (Round Robin, Least Connections, IP Hash и др.)
- Инструменты — Nginx, HAProxy, Envoy, AWS ALB, Yandex ALB
- Практические конфигурации Nginx и HAProxy с примерами
- Health checks, sticky sessions, SSL termination — продвинутые сценарии
- Типичные ошибки настройки и эксплуатации
Для кого статья: backend/fullstack-разработчики, DevOps/SRE-инженеры, системные администраторы, архитекторы. Подойдёт всем, кто разворачивает и поддерживает production-системы.
Источники: официальная документация Nginx, HAProxy, AWS ALB, Yandex Application Load Balancer; книга «Site Reliability Engineering» (Google); опыт инженеров FAANG из открытых лекций и блогов.
Аналитика спроса: «балансировщик нагрузки» — 549 показов/мес в Яндекс, «load balancer» — 526, «HAProxy» — 4 562, «Nginx балансировка» — 86.
Зачем нужен балансировщик нагрузки
Три ключевые задачи
1. Распределение нагрузки
Без балансировщика один сервер получает 100% запросов. С балансировщиком — 10 серверов получают по 10% каждый. Если сервер начинает тормозить, балансировщик видит это через health check и перестаёт слать на него трафик.
2. Отказоустойчивость (High Availability)
Сервер упал? Балансировщик заметит через 1–2 секунды (по health check) и перенаправит запросы на оставшиеся. Пользователь ничего не заметит (кроме возможного кратковременного замедления).
3. Горизонтальное масштабирование
Добавили новый сервер в пул — балансировщик автоматически начинает слать на него часть трафика. Убрали — перестаёт. Без ручного переконфигурирования.
Что балансировщик НЕ делает
- Не защищает от DDoS. Для этого нужны специализированные решения (Cloudflare, AWS Shield, Akamai).
- Не кэширует контент. Это задача CDN.
- Не разбирает логику приложения. Это задача сервера приложений.
Когда балансировщик НЕ нужен
| Ситуация | Нужен ли LB |
|---|---|
| Монолит на 1 сервере с 100 пользователями | Нет, оверкилл |
| 2–3 сервера для резервирования | Да, даже для HA |
| 10+ серверов | Обязательно |
| Глобальная система с GeoDNS | Обязательно + Geo Load Balancing |
L4 vs L7: на каком уровне работает
Балансировщики различаются по уровню OSI, на котором они анализируют трафик.
L4 — Transport Layer (TCP/UDP)
Что умеет:
- Видит IP-адреса клиента и сервера
- Видит TCP-порты
- Распределяет трафик по соединениям (connection-level)
Что НЕ умеет:
- Не видит HTTP-заголовки (URL, cookies, content type)
- Не может маршрутизировать по URL (
/api/*→ одна группа,/static/*→ другая) - Не понимает HTTP-методы
Пример: HAProxy в режиме TCP, AWS NLB (Network Load Balancer), Nginx stream module.
Когда выбирать:
- Высокая производительность (миллионы пакетов/сек)
- Протоколы без HTTP (gRPC через TCP, PostgreSQL, Redis)
- Игровые серверы (UDP)
L7 — Application Layer (HTTP/HTTPS)
Что умеет всё, что L4, плюс:
- Видит HTTP-заголовки, URL, cookies, методы
- Маршрутизация по URL:
/api/v1/*→ серверы API,/static/*→ CDN/S3 - Маршрутизация по заголовкам:
Host: api.example.com→ API-серверы,Host: admin.example.com→ админка - Терминация SSL (расшифровка HTTPS на балансировщике)
- Модификация запросов/ответов (добавление заголовков, rewrite URL)
Пример: HAProxy в режиме HTTP, Nginx, Envoy, AWS ALB (Application Load Balancer), Yandex ALB.
Когда выбирать:
- HTTP/HTTPS — самый частый случай
- Нужна маршрутизация по URL
- Нужна терминация SSL
- Нужна A/B-тестирование по заголовкам
Сравнение L4 и L7
| Параметр | L4 | L7 |
|---|---|---|
| Производительность | Очень высокая (млн пакетов/сек) | Ниже (нужен парсинг HTTP) |
| Маршрутизация по URL | Нет | Да |
| Терминация SSL | Обычно нет (проброс TCP) | Да (нативно) |
| Видимость запроса | Минимальная (IP:port) | Полная (URL, headers, cookies) |
| Когда выбирать | TCP/UDP, гейминг, max performance | HTTP, микросервисы, маршрутизация |
Алгоритмы распределения трафика
Статические алгоритмы (без учёта состояния)
Round Robin — запросы по очереди: 1 → серверу A, 2 → серверу B, 3 → серверу C, 4 → серверу A.
Плюсы: прост, справедлив для одинаковых серверов. Минусы: не учитывает нагрузку. Мощный сервер получит ту же долю, что и слабый.
Weighted Round Robin — то же самое, но с весами. Сервер с весом 3 получит в 3 раза больше запросов, чем с весом 1.
Динамические алгоритмы (с учётом нагрузки)
Least Connections — запрос идёт на сервер с наименьшим числом активных соединений.
Плюсы: лучше для долгих соединений (WebSocket, загрузка файлов). Минусы: не учитывает реальную нагрузку CPU/RAM.
Least Response Time — запрос идёт на сервер с минимальным временем отклика.
Плюсы: оптимальное распределение по реальной производительности. Минусы: требует активного измерения, может «раскачивать» систему.
Хеш-алгоритмы
IP Hash — запрос клиента направляется на сервер по хешу IP-адреса. Один клиент всегда попадает на один сервер (при условии, что пул не изменился).
server_index = hash(client_ip) % len(servers)
Плюсы: идеально для кэша в памяти приложения (один клиент = один сервер = кэш работает). Минусы: неравномерное распределение, проблемы при добавлении/удалении серверов.
URL Hash — то же самое, но по URL. Полезно для кэширования контента.
Consistent Hashing — улучшенная версия хеша, минимизирующая перераспределение при изменении пула серверов. Используется в мемкешед-системах.
Какой алгоритм выбрать
| Сценарий | Алгоритм |
|---|---|
| Одинаковые stateless-серверы | Round Robin |
| Серверы разной мощности | Weighted Round Robin |
| Долгие соединения (WebSocket, загрузка) | Least Connections |
| Кэш в памяти приложения (сессии) | IP Hash |
| Memcached/Redis пул | Consistent Hashing |
Инструменты балансировки
Nginx — самый популярный веб-сервер с балансировкой
Сильные стороны: огромная экосистема, документация, легко настроить базовый случай.
Слабые стороны: нет active health checks из коробки (только через плагины), ограниченная поддержка UDP.
Базовая конфигурация:
upstream backend {
# Round Robin (по умолчанию)
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Weighted upstream:
upstream backend {
server 10.0.1.10:8080 weight=3; # в 3 раза больше запросов
server 10.0.1.11:8080 weight=2;
server 10.0.1.12:8080 weight=1;
}
Least Connections:
upstream backend {
least_conn;
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
IP Hash (sticky sessions):
upstream backend {
ip_hash;
server 10.0.1.10:8080;
server 10.0.1.11:8080;
}
Health checks (через плагин или location):
upstream backend {
server 10.0.1.10:8080 max_fails=3 fail_timeout=30s;
server 10.0.1.11:8080 max_fails=3 fail_timeout=30s;
server 10.0.1.12:8080 max_fails=3 fail_timeout=30s;
}
server {
location /health {
proxy_pass http://backend;
access_log off;
}
}
HAProxy — профессиональный L4/L7 балансировщик
Сильные стороны: активные health checks из коробки, отличная статистика через веб-интерфейс, очень высокая производительность.
Слабые стороны: менее дружелюбен для новичков, чем Nginx.
Базовая конфигурация (haproxy.cfg):
global
maxconn 50000
log /dev/log local0
defaults
mode http
timeout connect 5s
timeout client 30s
timeout server 30s
option httplog
frontend web
bind *:80
default_backend backend_servers
backend backend_servers
balance roundrobin
option httpchk GET /health
http-check expect status 200
server web1 10.0.1.10:8080 check inter 2s
server web2 10.0.1.11:8080 check inter 2s
server web3 10.0.1.12:8080 check inter 2s
Алгоритмы балансировки в HAProxy:
balance roundrobin # Round Robin
balance leastconn # Least Connections
balance source # IP Hash
balance uri # URL Hash
balance url_param # по параметру URL
balance hdr(Host) # по HTTP-заголовку
Активный health check:
backend backend_servers
option httpchk
http-check send meth GET uri /health hdr Host example.com
http-check expect status 200
server web1 10.0.1.10:8080 check inter 2s rise 3 fall 2
server web2 10.0.1.11:8080 check inter 2s rise 3 fall 2
rise 3 — сервер считается здоровым после 3 успешных проверок, fall 2 — умершим после 2 неудач.
Статистика:
listen stats
bind *:8080
stats enable
stats uri /stats
stats refresh 5s
stats auth admin:password
Откройте http://your-server:8080/stats — увидите дашборд с активными соединениями, RPS, состоянием серверов.
Envoy — service proxy от Lyft
Сильные стороны: мощный service mesh (с Istio), отличная наблюдаемость, динамическая конфигурация через xDS API.
Слабые стороны: сложнее в настройке, чем Nginx/HAProxy.
Когда выбирать: микросервисы с service mesh (Istio, Consul Connect).
Облачные Load Balancers
| Провайдер | L4 | L7 | Особенности |
|---|---|---|---|
| AWS | NLB | ALB | Интеграция с другими сервисами AWS, высокая надёжность |
| Google Cloud | TCP LB | HTTP(S) LB | Глобальный anycast IP |
| Yandex Cloud | NLB | ALB | Российская альтернатива AWS, интеграция с Yandex Cloud |
| Cloudflare | Spectrum | Web/CDN | Глобальная CDN-сеть, защита от DDoS |
| Azure | Standard LB | Application Gateway | Интеграция с Azure AD |
Преимущество облачных: не нужно самим настраивать failover, мониторинг, обновления. Платите за трафик.
Недостаток: vendor lock-in, цена при больших объёмах.
Продвинутые сценарии
Health Checks
Health check — это автоматическая проверка «живости» сервера. Балансировщик периодически отправляет запрос и считает сервер «умершим», если получает ошибку или таймаут.
Типы health checks:
Active (активный) — балансировщик сам отправляет запрос:
GET /health HTTP/1.1
Host: example.com
Ожидаем: 200 OK в течение 1 секунды
Passive (пассивный) — балансировщик наблюдает за реальным трафиком. Если 5 из 10 последних запросов получили 5xx — сервер считается нездоровым.
Глубокий health check проверяет не только процесс, но и зависимости:
@app.route("/health")
def health():
# Проверяем БД
try:
db.execute("SELECT 1")
except Exception:
return {"status": "unhealthy", "reason": "db_down"}, 503
# Проверяем кэш
if not cache.ping():
return {"status": "unhealthy", "reason": "cache_down"}, 503
# Проверяем внешние API (с таймаутом!)
if not payment_api.health_check():
return {"status": "degraded"}, 200 # Не критично, но предупреждаем
return {"status": "healthy"}, 200
Лёгкий health check (для L4 балансировщика) — только процесс жив:
@app.route("/health/live")
def liveness():
return {"status": "alive"}, 200
Рекомендация: иметь оба эндпоинта — /health/live для балансировщика (быстрый, без зависимостей), /health/ready для оркестратора (проверяет зависимости).
Sticky Sessions
Иногда нужно, чтобы все запросы одного клиента шли на один сервер. Например, когда сессия хранится в памяти приложения (не в Redis).
Реализация в Nginx (IP Hash):
upstream backend {
ip_hash;
server 10.0.1.10:8080;
server 10.0.1.11:8080;
}
Реализация в HAProxy (cookie-based):
backend backend_servers
cookie SERVERID insert indirect nocache
server web1 10.0.1.10:8080 check cookie web1
server web2 10.0.1.11:8080 check cookie web2
HAProxy вставляет cookie SERVERID=web1 в ответ, браузер сохраняет и шлёт в следующих запросах.
Проблемы sticky sessions:
- При падении сервера клиент теряет сессию
- Неравномерное распределение нагрузки
- Усложняет failover
Лучшая практика: храните сессии в Redis или БД, а не в памяти приложения. Тогда sticky sessions не нужны.
SSL Termination (терминация SSL)
Балансировщик расшифровывает HTTPS, внутренний трафик идёт по HTTP.
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://backend;
}
}
Преимущества:
- Один сертификат на балансировщике, не на каждом сервере
- Разгрузка CPU (TLS-шифрование дорогое)
- Удобнее ротация сертификатов
Недостаток: внутренний трафик в открытом виде. Решение — TLS до бэкенда (HAProxy/Nginx поддерживают proxy_ssl).
SSL Passthrough
Альтернатива терминации — балансировщик не расшифровывает трафик, а только пробрасывает TCP-соединение на бэкенд (L4 балансировка).
Когда: строгие требования к безопасности, жёсткий PCI DSS.
stream {
upstream backend_ssl {
server 10.0.1.10:443;
server 10.0.1.11:443;
}
server {
listen 443;
proxy_pass backend_ssl;
proxy_protocol on; # для передачи реального IP
}
}
Blue-Green Deployment
Два идентичных окружения (Blue — текущий, Green — новый). Переключение балансировщика мгновенное.
upstream backend_blue {
server 10.0.1.10:8080;
server 10.0.1.11:8080;
}
upstream backend_green {
server 10.0.1.20:8080;
server 10.0.1.21:8080;
}
# Переключение: меняем proxy_pass
location / {
proxy_pass http://backend_blue; # → backend_green после тестирования
}
Преимущество: мгновенный rollback (меняем обратно на Blue).
Canary Deployment
Направляем 1–5% трафика на новую версию, наблюдаем метрики.
upstream backend_stable {
server 10.0.1.10:8080;
server 10.0.1.11:8080;
}
upstream backend_canary {
server 10.0.1.20:8080;
}
split_clients $request_id ~backend_stable {
5% backend_canary;
* backend_stable;
}
server {
location / {
proxy_pass http://backend_stable; # или upstream с split_clients
}
}
Подробнее о стратегиях деплоя — в нашей статье о DevOps.
Типичные ошибки настройки
1. Нет health checks
Сервер упал, но балансировщик продолжает слать на него трафик. Пользователи получают 502/504.
Решение: настроить active health checks с интервалом 2–5 секунд.
2. Health check проверяет только HTTP-статус, не реальную работу
Сервер отвечает 200 на /health, но БД лежит — приложение не работает, а балансировщик считает его здоровым.
Решение: health check должен проверять критичные зависимости (см. «Глубокий health check» выше).
3. Sticky sessions как костыль вместо нормального хранения сессий
«У нас всё ломается, потому что sticky sessions мешают балансировке. Что делать?» — вынести сессии в Redis.
Решение: Redis или БД для сессий, sticky sessions отключить.
4. SSL termination без защиты внутренней сети
«Мы терминируем SSL на балансировщике, внутренний трафик по HTTP — ок, мы же в VPC». ОК, пока кто-то не запустит tcpdump для отладки и не засветит данные в логи.
Решение: TLS до бэкенда или изоляция внутренней сети + мониторинг.
5. Слишком агрессивные таймауты
Балансировщик отменяет запрос через 5 секунд, а бэкенду нужно 30 секунд на тяжёлый отчёт. Пользователи получают 504.
Решение: таймауты балансировщика должны быть ≥ таймаутов бэкенда. Или разделять длинные операции через очереди.
6. Один балансировщик = одна точка отказа
«У нас есть балансировщик. Что если он упадёт?» — вся система упадёт.
Решение: два балансировщика в режиме active-passive с keepalived, или DNS-failover, или облачный managed LB (который сам решает эту проблему).
7. Отсутствие логирования
«Балансировщик сломался, куда смотреть?» — а логов нет.
Решение: access-логи балансировщика в ELK / Loki / DataDog. Метрики RPS, error rate, latency.
8. Распределение на серверы разной мощности без весов
Сервер A на 16 ядер получает ту же долю трафика, что и сервер B на 4 ядра. Сервер B перегружен, A недогружен.
Решение: weighted round robin с учётом мощности.
9. Забыть про proxy_set_header X-Forwarded-For
Без этого бэкенд видит IP балансировщика, а не клиента. Логи аналитики неправильные, geo-локация сломана.
Решение: всегда пробрасывать X-Forwarded-For, X-Real-IP, X-Forwarded-Proto.
10. Балансировка БД как балансировка веб-серверов
«Поставим HAProxy перед PostgreSQL — будет работать!» Нет, БД требует репликации, синхронизации состояний, обработки конфликтов. Это другой класс задач.
Решение: для БД — специализированные решения (Patroni для PostgreSQL, ProxySQL для MySQL, Vitess).
Мониторинг балансировщика
Что обязательно отслеживать:
| Метрика | Что значит | Алерт |
|---|---|---|
| RPS (Requests Per Second) | Нагрузка | >80% от максимума |
| Active connections | Текущие соединения | >90% от лимита |
| Backend health | Сколько серверов живы | <100% |
| Response time p95/p99 | Latency | >X ms |
| Error rate (5xx) | Ошибки | >1% |
| SSL handshake errors | Проблемы с сертификатами | >0 |
Инструменты мониторинга:
- Prometheus + Grafana — для метрик с HAProxy/Nginx
- Datadog / New Relic — SaaS, коробочные интеграции
- ELK / Loki — для логов
Ресурсы для углублённого изучения
Документация:
- Nginx (nginx.org/en/docs) — официальные доки
- HAProxy (haproxy.org) — конфигурация, примеры
- Envoy (envoyproxy.io) — service mesh
- AWS ALB (AWS docs) — облачный балансировщик
- Yandex ALB — российская альтернатива
Книги:
- «Site Reliability Engineering» (Google) — глава о load balancing
- «Nginx HTTP Server» Clément Nedelcu — практическое руководство
- «Web Scalability for Startup Engineers» Артём Раскрыткин — масштабирование с балансировкой
- «Designing Data-Intensive Applications» — общая картина
Курсы:
- NGINX and NGINX Plus (официальный курс)
- HAProxy (Udemy, haproxy.com tutorials)
Заключение
Балансировщик нагрузки — это первый компонент, который нужно добавить при переходе от одного сервера к нескольким. Он решает три задачи сразу: распределение нагрузки, отказоустойчивость и масштабирование. Без него невозможно горизонтальное масштабирование, а single point of failure становится хронической болью.
Ключевые принципы:
- Начинайте с L7 (Nginx/HAProxy). HTTP — самый частый протокол, маршрутизация по URL — самый частый кейс.
- Настройте active health checks. Без них балансировщик не знает, что сервер упал.
- Используйте метрики и логи. Load balancer — это observability-узел: через него проходит весь трафик.
- Два балансировщика — не перебор. Single point of failure на уровне LB уничтожает всю работу по HA.
- Weighted Round Robin для разнородных серверов, Round Robin для одинаковых.
- SSL termination на балансировщике — стандарт. Сертификаты и ротация в одном месте.
- Sticky sessions — костыль. Храните сессии в Redis, не в памяти приложения.
С чего начать прямо сейчас:
- Поднимите 2 экземпляра вашего приложения
- Настройте Nginx как L7 балансировщик с health checks
- Включите SSL termination
- Добавьте Prometheus + Grafana для мониторинга
- Протестируйте failover (уроните один сервер — трафик должен идти на второй)
Что дальше
Связанные статьи на сайте:
- System Design: проектирование систем — где балансировщик стоит в общей архитектуре
- DevOps: принципы и практики — blue-green и canary deployments
- C4 Model: документируем архитектуру — как описать LB в контексте системы
- Кэширование — работа с кэшем за балансировщиком
- Event-Driven Architecture — взаимодействие сервисов через очереди
- Анализ корневых причин — RCA для инцидентов с балансировщиком
- Docker cheatsheet — контейнеризация для пула серверов
- Архитектурные концепции — справочник паттернов