Балансировка нагрузки
| Назначение | масштабирование и отказоустойчивость |
|---|---|
| Уровни | L4 (транспортный), L7 (прикладной) |
| Алгоритмы | round robin, least connections, хеширование, EWMA |
| Программные | HAProxy, nginx, Envoy, Traefik, IPVS |
| Облачные | AWS ELB (ALB/NLB), Google Cloud LB, Yandex NLB/ALB |
| Связанные | health checks, sticky sessions, Anycast |
Балансировка нагрузки (англ. load balancing) — распределение входящих запросов или соединений между несколькими экземплярами сервиса. Она решает две задачи: масштабирование (нагрузку, которую не выдержит один сервер, делят несколько) и отказоустойчивость (упавший экземпляр исключается, клиенты продолжают работать). Балансировщик обычно стоит перед группой серверов как единая точка входа и часто совмещает роль обратного прокси: завершает TLS, кэширует, ограничивает частоту запросов.
Уровни балансировки
| L4 (транспортный) | L7 (прикладной) | |
|---|---|---|
| Что видит | IP-адреса и порты TCP/UDP | HTTP-заголовки, пути, cookie, gRPC-методы |
| Решение принимается | на соединение | на каждый запрос |
| Скорость | очень высокая, возможна в ядре | ниже, нужен разбор протокола |
| TLS | проходит насквозь | обычно завершается на балансировщике |
| Примеры | IPVS, AWS NLB, HAProxy mode tcp, Maglev |
nginx, HAProxy mode http, Envoy, AWS ALB |
L7-балансировщик умеет маршрутизировать по содержимому: /api — на один пул, /static — на другой; переключать трафик между версиями (canary, blue-green); повторять неудачные запросы. L4 дешевле и универсальнее — подходит для баз данных, очередей и любых TCP-протоколов.[3]
Алгоритмы
- Round robin — по кругу; с весами — пропорционально мощности серверов. Простой и предсказуемый вариант по умолчанию.[1]
- Least connections — на сервер с наименьшим числом активных соединений; хорош для долгих запросов и WebSocket.
- Power of two choices — выбрать два случайных сервера и отдать запрос менее загруженному; почти так же хорош, как полный перебор, но масштабируется лучше (используется в Envoy).
- Хеширование по IP клиента, URL или заголовку — один и тот же ключ всегда попадает на один сервер. Консистентное хеширование (кольцо, Maglev) минимизирует перераспределение ключей при добавлении или удалении серверов — важно для кэшей.[5]
- EWMA / least response time — учитывает задержку ответов, отправляя меньше трафика медленным узлам.
Проверки здоровья
Балансировщик периодически опрашивает серверы (health checks): активно — открывает TCP-соединение или запрашивает GET /healthz — и пассивно, отслеживая ошибки в реальном трафике. Сервер, не прошедший несколько проверок подряд, выводится из ротации и возвращается после восстановления. При плановом выводе узла используют draining: новые соединения на него не идут, а текущие завершаются спокойно.
Состояние и липкие сессии
Если приложение хранит сессию в памяти конкретного сервера, пользователь должен каждый раз попадать на него — это sticky sessions (по cookie балансировщика или хешу IP). Такая привязка мешает равномерному распределению и теряет сессии при падении узла, поэтому предпочтительнее хранить состояние вне серверов — в Redis или базе данных (см. Cookie и сессии). Сервисы без состояния масштабируются горизонтально простым добавлением экземпляров.
Где балансируют
- DNS — несколько A/AAAA-записей или ответы в зависимости от географии (GeoDNS). Грубо: кэширование DNS мешает быстро убрать упавший узел.
- Anycast — один IP-адрес анонсируется из многих точек по BGP, трафик идёт в ближайшую; так работают CDN и публичные DNS-резолверы.
- Выделенный балансировщик — аппаратный (F5, Citrix) или программный (HAProxy, nginx, Envoy) перед группой серверов. Чтобы он сам не стал единой точкой отказа, ставят пару с общим виртуальным IP (keepalived, VRRP).
- Внутри кластера — в Kubernetes объект Service распределяет трафик между подами через kube-proxy (iptables/IPVS) или eBPF, а Ingress и Gateway API выполняют L7-маршрутизацию (см. Service и Ingress).[4]
- На стороне клиента — клиент сам получает список экземпляров (из service discovery) и выбирает; так устроены gRPC и service mesh (Envoy-сайдкары в Istio).
Пример: nginx
upstream app {
least_conn;
server 10.0.0.11:8000 weight=2;
server 10.0.0.12:8000;
server 10.0.0.13:8000 backup;
}
server {
listen 80;
location / {
proxy_pass http://app;
proxy_next_upstream error timeout http_502;
}
}
Открытая версия nginx делает только пассивные проверки (max_fails, fail_timeout); активные проверки есть в nginx Plus, HAProxy и Envoy.[2]