Модуль реализует механизм LoadBalancer для сервисов в кластерах bare-metal.

Поддерживает следующие режимы работы:

  • Режим Layer 2 (L2 LoadBalancer) — реализует улучшенный (относительно стандартного режима L2 в MetalLB) механизм балансировки в кластерах bare-metal, который позволяет использовать несколько адресов для сервисов кластера.
  • Режим BGP (BGP LoadBalancer) — полностью основан на решении MetalLB.

Режим Layer 2

В режиме Layer 2 один или несколько узлов берут на себя ответственность за предоставление сервиса в локальной сети. С точки зрения сети это выглядит так, словно у каждого из узлов несколько IP-адресов, назначенных его сетевому интерфейсу. Технически это реализовано следующим образом: модуль отвечает на ARP-запросы для IPv4-сервисов и NDP-запросы — для IPv6. Основным преимуществом режима layer 2 является его универсальность: он работает в любой сети Ethernet, не требуя специального оборудования.

Принцип работы в сравнении с режимом L2 модуля MetalLB

MetalLB в режиме L2 позволяет заказать Service с типом LoadBalancer, работа которого основана на том, что в пиринговой сети узлы-балансировщики имитируют ARP-ответы от “публичного” IP. Данный режим имеет существенное ограничение — единовременно лишь один балансировочный узел обрабатывает весь входящий трафик данного сервиса. Соответственно:

  • Узел, выбранный в качестве лидера для “публичного” IP становится “узким местом” без возможности горизонтального масштабирования.
  • В случае выхода узла-балансировщика из строя, все текущие соединения будут разорваны в процессе переключения на новый узел, который будет назначен лидером.

Данный модуль помогает обойти эти ограничения. Он предоставляет новый ресурс MetalLoadBalancerClass, который позволяет с помощью nodeSelector связать группу узлов и пул IP-адресов. После чего мы можем создать стандартный ресурс Service с типом LoadBalancer и в нем указать имя обслуживающего MetalLoadBalancerClass‘a, а с помощью аннотации указать необходимое количество IP-адресов для L2-анонсирования.

Таким образом:

  • Приложение получит не один, а несколько “публичных” IP. Данные IP потребуется прописать в качестве A-записей для публичного домена приложения. Для последующего горизонтального масштабирования потребуется добавить дополнительные узлы-балансировщики, соответствующие Service будут созданы автоматически, потребуется лишь добавить их в список A-записей прикладного домена.
  • При выходе из строя одного из узлов-балансировщиков, лишь часть трафика будет подвержена обрыву для переключения на здоровый узел.

Режим BGP

Доступно только в редакции Enterprise Edition.

В режиме BGP каждый узел кластера устанавливает пиринговую сессию по протоколу BGP с сетевыми маршрутизаторами и использует ее для публикации IP-адресов внешних сервисов кластера. Поддержка multipath маршрутизаторами позволяет добиться настоящей балансировки нагрузки: маршруты, публикуемые MetalLB, эквивалентны друг другу (за исключением их nexthop). То есть маршрутизаторы будут использовать все nexthop вместе и балансировать нагрузку между ними. kube-proxy отвечает за финальный hop: как только пакеты поступают на узел, он переправляет их на определенный под сервиса.

Поведение при балансировке нагрузки

Конкретное поведение при балансировке нагрузки зависит от модели и конфигурации маршрутизатора. Обычно балансировка осуществляется по каждому соединению с использованием хэшей пакетов.

Балансировка по каждому соединению означает, что все пакеты определенной TCP- или UDP-сессии направляются на одну и ту же машину в кластере. Перераспределяется трафик, относящийся к разным соединениям, а не пакеты в рамках одного соединения. Перераспределение пакетов между несколькими узлами кластера привело бы к ряду проблем:

  • Разбивка одного соединения на несколько путей влечет переупорядочивание пакетов на конечном узле, оказывая значительное негативное влияние на его производительность.
  • Маршрутизация трафика на одном узле в Kubernetes не гарантирует согласованности между узлами. Разные узлы могут направлять пакеты, относящиеся к одному и тому же соединению, в разные поды, что приведет к проблемам.

Хэширование пакетов позволяет высокопроизводительным маршрутизаторам распределять соединения между несколькими бэкендами без хранения состояния (statelessly). Для каждого пакета извлекаются некоторые поля, которые используются в качестве исходных данных для детерминированного выбора одного из возможных бэкендов. Для идентичных полей всегда будет выбираться один и тот же бэкенд. Конкретные методы подсчета хэшей зависят от аппаратного и программного обеспечения маршрутизатора. Обычно используется хэширование по трем и по пяти элементам (3-tuple и 5-tuple hashing). При хэшировании по трем элементам в качестве ключей используются протокол, IP-адрес источника и IP-адрес получателя. Таким образом все пакеты с двумя уникальными IP-адресами будут направляться на один и тот же бэкенд. При хэшировании по пяти элементам также используются порты источника и назначения, что позволяет распределять по кластеру различные соединения от одних и тех же клиентов.

Рекомендуется закладывать как можно больше энтропии в хэш пакета, то есть предпочтительно большее количество полей. Увеличение энтропии приближает балансировку нагрузки к «идеальному» состоянию, когда каждый узел получает одинаковое количество пакетов. Увы, из-за перечисленных выше проблем достичь такого идеального состояния невозможно. Все, что можно сделать, — попытаться распределить соединения максимально равномерно, предотвратив образование узких мест.

Ограничения

Важное преимущество использования протокола BGP для балансировки нагрузки заключается в возможности использовать обычные маршрутизаторы вместо их экзотичных реализаций. Однако у этого подхода есть и недостатки.

Главный — балансировка нагрузки на основе BGP плохо реагирует на изменения в бэкендах. При выходе из строя узла кластера последует разрыв всех активных соединений с сервисом (пользователи увидят сообщение «Connection reset by peer»). Маршрутизаторы на базе BGP реализуют stateless-распределение нагрузки. Они вычисляют хэш пакета на основе полей его заголовка и используют вычисленный хэш в качестве индекса в массиве для определения адреса бэкенда.

Проблема в том, что используемые в маршрутизаторах хэши обычно нестабильны. Когда набор бэкендов меняется (например, обрывается BGP-сессия узла), существующие соединения перехэшируются фактически случайным образом. В этом случае большинство существующих соединений могут быть внезапно перенаправлены на другие бэкенды, не имеющие о них никакого представления. Таким образом, любое изменение связки «IP-адрес → узел сервиса» влечет разовый отказ, при котором большинство активных соединений с сервисом прервется. Выражается это в полном однократном разрыве без потери пакетов или blackhol’инга.

Существуют различные стратегии, позволяющие снизить риск (в зависимости от предназначения сервиса):

  • Настроить BGP-маршрутизатор на использование более стабильного алгоритма хэширования ECMP (часто называется resilient ECMP или resilient LAG). Использование такого алгоритма значительно снижает количество затронутых соединений при изменении набора бэкендов.
  • Привязывать сервисы к определенным узлам, тем самым сократив количество узлов, за которыми нужно следить.
  • Планировать изменения на время минимального трафика, когда большинство пользователей неактивны и трафик невелик.
  • Разбить каждый логический сервис на два Service‘а Kubernetes с разными IP-адресами и использовать DNS для миграции пользовательского трафика с одного Service‘а на другой до того, как произойдет прерывание работы сервиса.
  • Добавить прозрачную логику повторных запросов на стороне клиента для восстановления после внезапных обрывов. Это особенно актуально для мобильных приложений или одностраничных веб-приложений.
  • Разместить сервисы за Ingress-контроллером. Ingress-контроллер может использовать MetalLB для получения трафика, при этом наличие промежуточного слоя между BGP и сервисами позволяет вносить изменения в сервисы, не опасаясь обрывов трафика. Осторожность потребуется только при изменении самого Ingress-контроллера (например, при его масштабировании).
  • Принять факт, что время от времени будут возникать обрывы соединений. Это может быть приемлемо для внутренних сервисов с низкой доступностью.