L4 vs L7 load balancers, algorithms (round-robin, weighted, least-connections, P2C/Power of Two Choices, consistent hash, random), active+passive health checks with outlier detection, connection draining, GSLB via Anycast IP+BGP and GeoDNS. 5 scenarios: round-robin-naive, p2c-choice, health-check-evict, l4-vs-l7-routing, global-server-lb-anycast.
Load balancing нужен не только для того, чтобы «размазать RPS по серверам». В системном дизайне это точка, где принимаются решения о доступности, задержке, стоимости и управляемости релизов. Один backend быстро становится и bottleneck, и single point of failure: он держит все соединения, переживает все всплески, падает вместе со всей пользовательской функцией. Балансировщик вводит слой управления входящим трафиком: он выбирает здоровый endpoint, учитывает емкость разных машин, выключает плохие инстансы из rotation, дает время на graceful shutdown и может менять маршрут без изменения клиента.
Важно понимать, что LB не делает систему бесконечно масштабируемой. Он только распределяет работу между тем, что уже существует. Если каждый backend ходит в одну перегруженную базу, балансировщик лишь быстрее доведет проблему до базы. Поэтому в архитектурном решении LB всегда рассматривается вместе с health checks, back-pressure, rate limiting, connection pools и моделью state. Связанные темы: rate-limiting, load-shedding, api-gateway, service-discovery, cdn-edge-network.
Представьте стойку регистрации в клинике. Посетитель не выбирает кабинет сам: администратор знает, какие врачи свободны, кто ушел на перерыв, у кого очередь, где принимают конкретный тип обращения. L4 load balancer похож на администратора, который видит только номер кабинета и поток TCP-соединений. L7 load balancer уже читает содержание заявки: URL, headers, cookie, host, иногда JWT claims. Поэтому L4 быстрее и проще, а L7 умнее и дороже.
Хорошая ментальная модель: LB является policy point на пути к backend pool. Он не владеет бизнес-логикой, но владеет admission и routing. Его задача не «справедливо раздать всем поровну», а направить запрос так, чтобы система выполнила SLO: минимальная задержка, отсутствие трафика в мертвые инстансы, предсказуемый rollback, сохранение sticky state там, где его еще не вынесли из процесса.
Диаграмма показывает три клиента, edge-зону с балансировщиком и backend pool из трех серверов. Все внешние клиенты подключаются к одному LB, а LB имеет соединения к каждому backend. Это важно: клиенты не знают о внутренней топологии и не должны зависеть от количества реплик. Сценарии демонстрируют, что одна и та же физическая схема может вести себя по-разному в зависимости от алгоритма и политики health.
В round-robin запросы идут по кругу: backend-1, backend-2, backend-3, снова backend-1. Это дает простую равномерность, но не видит, что один сервер может быть в GC pause, обслуживать долгие WebSocket-сессии или иметь слабее CPU. P2C показывает другой подход: LB случайно выбирает два backend и отправляет запрос менее загруженному. Health-check сценарий показывает, как backend удаляется из rotation после нескольких ошибок и возвращается только после восстановления. L4/L7 сценарий разделяет forwarding TCP-потока и HTTP-aware routing. GSLB показывает, что балансировка бывает не только внутри одного датацентра: anycast и DNS направляют пользователей в ближайший или живой регион.
round-robin-naive учит не путать равномерное распределение запросов с равномерной нагрузкой. Если запросы различаются по стоимости, а backend различаются по текущему состоянию, простой круг может ухудшить tail latency. ADR-вывод: round-robin допустим как базовый выбор для однородного пула и коротких запросов, но требует health checks и наблюдения за p95/p99.
p2c-choice показывает, почему Power of Two Choices часто выигрывает у сложных глобальных алгоритмов. Он не требует точного знания всего кластера, но резко снижает вероятность попадания на перегруженный endpoint. ADR-вывод: если есть дешевые метрики текущих соединений или pending requests, P2C является хорошим default для большого backend pool.
health-check-evict учит, что health check должен быть и активным, и пассивным. Активный /health проверяет готовность заранее, passive outlier detection видит реальные 5xx и timeout. ADR-вывод: только TCP-port check недостаточен для приложений, где процесс жив, но база недоступна или event loop забит.
l4-vs-l7-routing показывает решение уровня OSI. L4 выбирают для низкой задержки, pass-through TLS, non-HTTP и большого числа соединений. L7 выбирают для path routing, WAF, auth, cookie affinity, request transforms и observability. ADR-вывод: не ставьте L7 только потому, что он «богаче»; платите за него там, где нужны HTTP-семантика и политики.
global-server-lb-anycast показывает выбор между GeoDNS и anycast/BGP. ADR-вывод: DNS проще, но ограничен TTL и поведением resolver'ов; anycast быстрее в failover, но требует сетевой экспертизы и аккуратного управления route withdrawal.
Главный trade-off: умный routing против простоты и latency. L4 NLB быстрее, дешевле и лучше держит миллионы TCP-соединений, но почти ничего не знает о запросе. L7 ALB/Envoy/HAProxy дает routing по path, header и cookie, но добавляет parsing, больше state и больше мест для misconfiguration.
Второй trade-off: sticky sessions против горизонтальной свободы. Sticky решает проблему in-memory session, но закрепляет пользователя за backend и усложняет drain, autoscaling и failover. ADR-решение обычно такое: short-term можно включить cookie affinity или consistent hash, long-term состояние выносится в Redis/DB, чтобы backend оставались stateless.
Третий trade-off: aggressive health eviction против false positive. Если eject делать после одной ошибки, можно устроить флаппинг и снизить емкость кластера. Если ждать слишком долго, пользователи будут получать ошибки от больного backend. Практичный вариант: несколько последовательных failures, cooldown, passive 5xx-rate, slow start при возврате.
Четвертый trade-off: connection draining против скорости релиза. Долгий drain защищает WebSocket и long polling, но замедляет deploy. Короткий drain ускоряет rollout, но повышает риск 503. Решение зависит от протокола и SLO.
AWS ALB подходит для HTTP/HTTPS, path-based routing, host routing, TLS termination и интеграций с WAF. AWS NLB используют для TCP/UDP, низкой задержки и pass-through сценариев. HAProxy и Nginx популярны как software LB, особенно когда нужен контроль конфигурации и on-prem окружение. Envoy часто используется в cloud-native стеке и service mesh, потому что соединяет L7 routing, retries, circuit breakers и telemetry. Cloudflare и AWS Global Accelerator закрывают global traffic steering через edge и anycast.
Внутри Kubernetes похожие роли делят Service, Ingress Controller, Gateway API и service mesh. Важно не смешивать уровни: Kubernetes Service делает L4 service discovery и kube-proxy/IPVS/eBPF forwarding, Ingress/Gateway добавляет L7, mesh дает per-service policies.
Round-robin без health checks отправляет трафик в мертвый backend. Sticky session по IP ломается за NAT и мобильными сетями. Один Nginx как «балансировщик» становится новым SPOF, если нет HA pair или managed LB. Connection draining часто забывают, и каждый deploy превращается в короткую волну 502/503. Еще одна ошибка: считать /health успешным, если процесс отвечает, хотя приложение не может читать critical dependency.
Опасен и shortcut routing: когда часть клиентов ходит мимо LB напрямую к backend. Тогда политики, TLS, rate limits и drain работают не для всех. Для gateway/proxy паттернов трафик должен идти через выбранную точку управления.
Отдельный LB не нужен для локального single-process инструмента, для batch job без входящего трафика или для прототипа, где отказ одного процесса не имеет цены. Не стоит ставить сложный L7 mesh/LB ради трех внутренних сервисов без SLO, если простого Kubernetes Service достаточно. Не стоит использовать sticky sessions как долгосрочную замену нормальному хранению session state. Не стоит использовать global load balancing до появления реальной multi-region стратегии: активный-активный режим требует данных, failover-процедур и consistency-решений, а не только DNS-записи.
В CloudArch дальше полезно открыть service-discovery, api-gateway, cdn-edge-network, rate-limiting, load-shedding, circuit-breaker, timeout-deadline и microservices-mesh. Из внешних материалов: System Design Primer про load balancer, статьи Marc Brooker о connection pooling, документация Envoy по load balancing и outlier detection, документация HAProxy/Nginx, материалы по Power of Two Choices и Gateway API для Kubernetes.