Привет, Хабр! Я Юрий Дергач, я возглавляю ЦК DevOps и релизного управления в РСХБ. Мы с командой развиваем инфраструктуру и автоматизируем разработку продуктов компании. При внедрении наших проектов группы «Экосистема Свое», частично основанных на стеке Java Spring, в Kubernetes, возникли вопросы, связанные с различными методами балансировки нагрузки между микросервисами.

В этой статье мы обсудим два основных подхода к балансировке нагрузки между Backend-компонентами приложений на стеке Java Spring Cloud в Kubernetes. Мы также рассмотрим преимущества и недостатки каждого метода.
Server-side Load Balancing

Server-side Load Balancing — это, как правило, стандартная (нативная) модель балансировки трафика по средствам штатных средств среды kubernetes. В роли балансировщика выступает сущность Kubernetes Service.
Kubernetes Service имеет 3 режима работы:
iptables - Стандартный режим (рекомендован до Kubernetes v1.26). Использует цепочки iptables. Алгоритм балансировки - Случайный выбор (random)
-
ipvs - Режим на основе IP Virtual Server (рекомендован с v1.26+).
Поддерживает 6 алгоритмов:
rr (round-robin)
lc (least connection)
dh (destination hashing)
sh (source hashing)
sed (shortest expected delay)
nq (never queue)
userspace - Устаревший режим (не рекомендуется).
Можно построить свою (кастомную) балансировку внутри кластера Kubernetes по средствам Istio, или популярного HAProxy. Но, как правило, такие подходы используются для решения специфичных задач, например построение балансировки с шифрованием трафика или использования безопасных контуров информационных систем с продвинутым фаерволом.
Client-side Load Balancing

Client-side Load Balancing — это подход, при котором клиент сам выбирает сервис из списка доступных экземпляров для отправки запроса, минуя центральный балансировщик. В Kubernetes это часто применяется в микросервисных архитектурах для повышения эффективности и снижения задержек.
Как работает Client-side Load Balancing в Kubernetes?
Клиент получает список всех доступных подов сервиса через:
DNS-запросы (например, через headless-сервис: nslookup my-svc.my-namespace.svc.cluster.local).
Kubernetes API (используя клиентские библиотеки, например, Java-приложение с kubernetes-client).
Специализированные инструменты (Consul, Eureka, Zookeeper).
Выбор алгоритма балансировки.
Клиентская библиотека применяет алгоритм для выбора пода:
Round Robin
Random
Weighted
Least Connections
Zone-aware (учёт зоны доступности)
Запрос идёт напрямую в контейнер, минуя kube-proxy.
Рассмотрим подробней подход Client-side Load Balancing на примере Spring Cloud Kubernetes.
Spring Cloud Kubernetes интегрирует экосистему Spring Cloud с Kubernetes, используя нативные возможности платформы для реализации паттернов микросервисной архитектуры. Основные принципы работы:
1. Service Discovery (обнаружение сервисов)
Используется Kubernetes API.
Принцип работы:
Каждый сервис регистрируется в Kubernetes как Service (с типом ClusterIP).
Spring Cloud Kubernetes обращается к Kubernetes API для получения списка доступных сервисов.
Клиенты используют DNS-имена вида {service-name}.{namespace}.svc.cluster.local.
2. Load Balancing (балансировка нагрузки)
Spring Cloud Kubernetes использует Spring Cloud LoadBalancer для L7-балансировки на стороне клиента.
Принцип работы:
Клиент получает список IP-адресов подов через DiscoveryClient.
Логика балансировки (Round Robin, Random и т.д.) применяется на стороне клиента.
3. Distributed Tracing (распределенная трассировка)
Интеграция с Jaeger/Zipkin через стандартные инструменты Spring Cloud Sleuth.
4. Service Resilience (устойчивость сервисов)
Используются стандартные Spring Cloud компоненты:
Hystrix или Resilience4j для Circuit Breaker.
Ribbon (устаревший) или Spring Cloud LoadBalancer.
Ключевые компоненты:
1. spring-cloud-starter-kubernetes-client:
Основная зависимость для доступа к Kubernetes API.
Автоматически настраивает DiscoveryClient
2. spring-cloud-starter-kubernetes-ribbon (опционально):
- Интеграция с Ribbon для клиентской балансировки (в новых версиях заменена на Spring Cloud LoadBalancer).

Если говорить простым языком, Spring Cloud Kubernetes позволяет сервисам внутри Kubernetes общаться «напрямую» между собой, и использовать встроенные механизмы для обеспечивания балансировки, трассировки и отказоустойчивости вне зависимости от namespace.
Подведем итоги и рассмотрим плюсы и минусы подходов по балансировке нагрузки.
Server-side Load Balancing (балансировка нагрузки на стороне сервера) в Kubernetes
Плюсы:
Централизованное управление. В Kubernetes можно централизованно управлять настройками балансировки через API и инструменты оркестрации. Это упрощает мониторинг и управление распределением нагрузки.
Высокая производительность. Kubernetes может оптимизировать распределение нагрузки между подинстансами (pods) для обеспечения максимальной эффективности и балансировки ресурсов.
Надёжность. В случае сбоя одного из подинстансов Kubernetes может автоматически перераспределить трафик на другие доступные подинстансы, обеспечивая высокую доступность приложений.
Возможность использования сложных алгоритмов балансировки. Kubernetes поддерживает использование различных алгоритмов балансировки, включая те, которые оптимизируют распределение нагрузки на основе различных параметров.
Минусы:
Зависимость от инфраструктуры. Для реализации server-side load balancing в Kubernetes требуется мощная инфраструктура и ресурсы кластера.
Задержки. Централизованное управление и обработка запросов могут привести к небольшим задержкам из-за необходимости координации между компонентами системы.
Сложность настройки. Настройка и оптимизация алгоритмов балансировки в Kubernetes может быть сложной задачей, требующей глубоких знаний и опыта.
Client-side Load Balancing (балансировка нагрузки на стороне клиента) в Kubernetes
Плюсы:
Снижение нагрузки на сервер. В Kubernetes можно использовать клиентские балансировщики (например, Spring Cloud Kubernetes), что снижает нагрузку на компоненты кластера Kubernetes.
Уменьшение задержек. Поскольку балансировка может происходить на стороне клиента, это может снизить общую задержку при обращении к приложениям в Kubernetes.
Гибкость. Клиенты могут самостоятельно выбирать серверы или подинстансы для подключения, что обеспечивает гибкость в распределении нагрузки.
Возможность использования простых алгоритмов балансировки. На стороне клиента можно использовать более простые алгоритмы, что упрощает реализацию и настройку.
Минусы:
Необходимость в дополнительных настройках на стороне клиента. Пользователи должны настроить параметры балансировки на своих клиентах, что может потребовать дополнительных усилий и времени.
Сложность управления. Управление распределением нагрузки может быть менее централизованным и более сложным для мониторинга в крупных кластерах.
Возможные проблемы с равномерным распределением нагрузки. Клиенты могут выбирать серверы неравномерно, что может привести к неравномерной нагрузке на подинстансы в Kubernetes.
Ограниченная возможность использования сложных алгоритмов. На стороне клиента сложнее реализовать сложные алгоритмы балансировки, которые могут потребоваться для оптимизации распределения нагрузки в сложных сценариях.
Сначала мы выбрали Server-side Load Balancing из-за его простоты для команд разработки. Однако уже рассматриваем более продвинутую балансировку на основе Service Discovery, чтобы решать более сложные задачи.
m_sinelnikov
Отличная статья, Юрий! Вы очень наглядно разложили два подхода и их компромиссы. Особенно ценно, что вы поделились практическим опытом перехода от server-side к client-side.
В статье вы упомянули, что при Client-side Load Balancing клиент получает список подов через DNS (headless service) или Kubernetes API. Но что происходит в реальных боевых условиях с высокой нагрузкой и частыми перескалированиями (autoscaling/horizontal pod autoscaler)?
Мои вопросы:
Когнитивная нагрузка на DNS / API — если у вас 1000 микросервисов, каждый из которых общается с 100 другими, и каждый делает запросы к API или DNS для обновления списка эндпоинтов, как вы боретесь с «эффектом стадности» (thundering herd) при частых редеплоях или масштабированиях?
Задержка обновления — при использовании headless service + DNS, клиент может кешировать DNS-запись на 30 секунд (TTL). Как вы решаете проблему, когда под умер, а клиент всё ещё шлёт трафик на мёртвый IP? Используете ли вы retry с другой нодой, или полагаетесь на health checks на уровне LoadBalancer?