Кладовочка

О разном, чтобы было под рукой онлайн

Балансировщик нагрузки: как работает и зачем нужен

Балансировщик нагрузки (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) и перенаправит запросы на оставшиеся. Пользователь ничего не заметит (кроме возможного кратковременного замедления).

graph TB Client[Клиент] --> LB[Load Balancer] LB --> S1[Сервер 1 ✅] LB --> S2[Сервер 2 ✅] LB -.->|health check failed| S3[Сервер 3 ❌] LB -.->|не отправляем| S3

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.

graph LR R1[Запрос 1] --> A[Сервер A] R2[Запрос 2] --> B[Сервер B] R3[Запрос 3] --> C[Сервер C] R4[Запрос 4] --> A R5[Запрос 5] --> B

Плюсы: прост, справедлив для одинаковых серверов. Минусы: не учитывает нагрузку. Мощный сервер получит ту же долю, что и слабый.

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 становится хронической болью.

Ключевые принципы:

  1. Начинайте с L7 (Nginx/HAProxy). HTTP — самый частый протокол, маршрутизация по URL — самый частый кейс.
  2. Настройте active health checks. Без них балансировщик не знает, что сервер упал.
  3. Используйте метрики и логи. Load balancer — это observability-узел: через него проходит весь трафик.
  4. Два балансировщика — не перебор. Single point of failure на уровне LB уничтожает всю работу по HA.
  5. Weighted Round Robin для разнородных серверов, Round Robin для одинаковых.
  6. SSL termination на балансировщике — стандарт. Сертификаты и ротация в одном месте.
  7. Sticky sessions — костыль. Храните сессии в Redis, не в памяти приложения.

С чего начать прямо сейчас:

  • Поднимите 2 экземпляра вашего приложения
  • Настройте Nginx как L7 балансировщик с health checks
  • Включите SSL termination
  • Добавьте Prometheus + Grafana для мониторинга
  • Протестируйте failover (уроните один сервер — трафик должен идти на второй)

Что дальше

Связанные статьи на сайте: