Алгоритмы балансировки нагрузок

Рано или поздно веб-приложения перерастают среду одного сервера. Компаниям требуется увеличить или их доступность, или масштабируемость, или и то, и другое. Чтобы сделать это, они развёртывают своё приложение на нескольких серверах и ставят перед ним балансировщик нагрузок для распределения входящих запросов. Чтобы справляться с нагрузками, большим компаниям могут потребоваться тысячи серверов, на которых запущено веб-приложение.
В этом посте мы рассмотрим способы, которыми один балансировщик нагрузок может распределять HTTP-запросы на множество серверов. Мы начнём снизу и проделаем весь путь вверх до современных алгоритмов балансировки нагрузок.
▍ Визуализация проблемы
Давайте начнём сначала: с одного балансировщика нагрузок , отправляющего запросы одному серверу . Запросы отправляются с частотой 1 запрос в секунду (request per second, RPS), и каждый запрос постепенно уменьшается в размере, пока сервер обрабатывает его.
Для многих веб-сайтов такая система вполне работает. Современные серверы мощны и способны обрабатывать множество запросов . Но что будет, если они перестанут справляться?
Мы видим, что при частоте 3 RPS — часть запросов отбрасывается . Если запрос поступает на сервер , пока обрабатывается другой запрос , то сервер его отбросит . Это приводит к тому, что у пользователя отображается ошибка, и такую ситуацию нужно избегать. Чтобы устранить проблему, можно добавить к нашему балансировщику нагрузок ещё один сервер .
Отбрасываемых запросов больше нет! Наш балансировщик нагрузок в этом случае отправляет по очереди запрос каждому серверу ; это называется балансировкой нагрузок циклическим перебором (round robin). Это одна из самых простых разновидностей балансировки нагрузок, которая хорошо работает, когда серверы имеют одинаковую мощность, а запросы одинаково затратны.
▍ Когда round robin не подходит
В реальном мире серверы редко имеют одинаковую мощность, а запросы редко одинаково затратны. Даже если оборудование серверов полностью одинаково, производительность может различаться. Приложениям может требоваться обрабатывать множество разных типов запросов , и они, скорее всего, будут иметь разные характеристики производительности.
Давайте посмотрим, что произойдёт, когда мы начнём варьировать затраты на запрос . В показанной ниже симуляции запросы имеют разные затраты. Это можно заметить по тому, что на уменьшение некоторым запросам требуется больше времени, чем другим.
Хотя большинство запросов обрабатывается успешно, некоторые приходится отбрасывать . Один из способов решения этой проблемы заключается в создании «очереди запросов».
Очереди запросов позволяют нам справляться с неопределённостью, но при этом приходится идти на компромисс. Мы будем отбрасывать меньшее количество запросов , однако ценой того, что некоторые запросы будут иметь повышенную задержку. Если вы понаблюдаете за показанной выше симуляцией, то заметите, что запросы немного меняют цвет. Чем дольше они не обрабатываются, тем сильнее меняется их цвет. Также вы заметите, что благодаря различиям в стоимости запросов серверы начинают проявлять признаки дисбаланса. Очереди накапливаются на серверах , которым не повезло и которым приходится обрабатывать множество затратных запросов подряд. Если очередь заполнена, мы отбросим запрос .
Всё вышесказанное применимо и к серверам , мощность которых варьируется. В следующей симуляции мы также будем варьировать мощность каждого сервера , что визуально представлен более тёмными оттенками серого.
Серверам присваивается случайное значение мощности, однако есть шансы, что их часть будет менее мощной, чем другие, и быстро начнёт отбрасывать запросы . В то же время более мощные серверы бОльшую часть времени находятся в состоянии простоя. Этот сценарий показывает основное слабое место round robin: колебания.
Однако несмотря на свои недостатки, round robin по-прежнему остаётся стандартным методом балансировки HTTP-нагрузок для nginx.
▍ Совершенствование round robin
Можно улучшить round robin так, чтобы он лучше справлялся с колебаниями. Существует алгоритм, называющийся «weighted round robin» («взвешенный цикличный перебор»); он заключается в том, что люди присваивают каждому серверу вес, определяющий, сколько запросов ему отправлять.
В этой симуляции мы используем в качестве веса каждого сервера его известное значение мощности, и отдаём более мощным серверам больше запросов при их циклическом обходе.
Хотя это позволяет лучше справляться с колебаниями мощности серверов , чем стандартный round robin, нам всё равно нужно решить вопрос колебаний запросов . На практике ручное указание весов быстро оказывается неэффективным. Сложно свести производительность сервера к одному числу, и для этого потребуется тщательное тестирование нагрузок с реальными рабочими нагрузками. Это делают редко, поэтому другой вариант weighted round robin вычисляет веса динамически при помощи вспомогательной метрики: задержки.
Логично, что если один сервер обрабатывает запросы в три раза быстрее, чем другой сервер , то, вероятно, он в три раза быстрее и должен получать в три раза больше запросов .
На этот раз я добавил к каждому серверу текст, отображающий среднюю задержку трёх последних обработанных запросов . Затем мы решаем, отправить ли 1, 2 или 3 запроса каждому серверу , на основании относительного различия в задержках. Результат очень схож с исходной симуляцией weighted round robin,
но отсутствует необходимость заранее указывать вес каждого сервера . Этот алгоритм также сможет адаптироваться к изменениям производительности сервера со временем. Это называется «dynamic weighted round robin».
Давайте посмотрим, как этот алгоритм справляется со сложной ситуацией, когда есть сильные колебания и в мощности сервера , и в стоимости запросов . В симуляции из оригинала статьи используются рандомизированные значения, поэтому можно несколько раз обновить страницу, чтобы посмотреть, как она адаптируется к новым вариантам.
▍ Уходим от round robin
Кажется, dynamic weighted round robin хорошо учитывает колебания и мощности сервера , и затрат на запросы . Но что, если я скажу вам, что можно решать задачу ещё лучше и при помощи более простого алгоритма?
Это называется балансировкой нагрузок по принципу «least connections» (наименьшего количества соединений).
Так как балансировщик нагрузок находится между сервером и пользователем, он может точно отслеживать, сколько ожидающих запросов есть у каждого сервера . То есть при поступлении нового запроса и необходимости выбора, куда его отправить, он знает, у каких из серверов меньше всего работы, и отдаёт приоритет им.
Этот алгоритм работает чрезвычайно хорошо вне зависимости от степени колебаний. Он избавляет от неопределённости, обеспечивая точное понимание того, чем занят каждый из серверов . Он обладает и ещё одним преимуществом: простотой реализации. Поэтому такой алгоритм является стандартным методом балансировки HTTP-нагрузки в балансировщиках нагрузок AWS. Он также используется как опция в nginx, и с ним стоит поэкспериментировать, если вы никогда не меняли стандартный метод.
Давайте посмотрим это в действии в симуляции схожего уровня сложности и с теми же параметрами, что и в алгоритме dynamic weighted round robin. Эти параметры в оригинале статьи тоже рандомизированы, так что можно обновить страницу, чтобы увидеть новые варианты.
Хотя этот алгоритм имеет хороший баланс между простотой и производительностью, у него нет иммунитета к отбрасыванию запросов . Однако можно заметить, что это единственный случай, когда алгоритм отбрасывает запросы только в случае полного отсутствия места в очередях. Он обеспечивает использование всех доступных ресурсов, поэтому для большинства нагрузок может стать отличным выбором по умолчанию.
▍ Оптимизация задержек
Пока мы избегали самой важной части обсуждения: того, что мы оптимизируем. Подразумевалось, что я считаю отброшенные запросы очень плохой ситуацией и стремлюсь избегать их. Это отличная цель, но не та метрика, которую мы хотим оптимизировать в балансировщике HTTP-нагрузок .
Часто нас больше волнуют задержки. Они измеряются в миллисекундах с момента создания запроса до его обработки. При обсуждении задержки в этом контексте обычно говорят о разных «перцентилях». Например, 50-й перцентиль (также называемый «медианой») определяется как значение в миллисекундах, ниже которого 50% запросов и выше которого 50% запросов.
Я запустил на 60 секунд три симуляции с одинаковыми параметрами и каждую секунду фиксировал различные измерения. Симуляции отличались лишь алгоритмом балансировки нагрузок. Давайте сравним медианы для каждой из трёх симуляций:

Возможно, вы этого не ожидали, но round robin имеет самую низкую медианную задержку. Если бы мы не рассматривали все остальные точки данных, то упустили бы общую картину. Давайте взглянем на 95-й и 99-й перцентили.

Примечание: здесь нет цветовых различий между разными перцентилями для каждого алгоритма балансировки нагрузок. Более высокие перцентили всегда будут находиться выше на графике.
Мы видим, что round robin плохо проявляет себя на высоких перцентилях. Почему round robin имеет отличную медиану, но плохие 95-й и 99-й перцентили?
При round robin состояние каждого сервера не учитывается, поэтому довольно много запросов будут отправляться к серверам , находящимся в состоянии простоя. Вот как мы получаем низкий 50-й перцентиль. С другой стороны, мы запросто будем отправлять запросы на перегруженные серверы , таким образом — получая плохие 95-й и 99-й перцентили.
Полные данные мы можем рассмотреть в виде гистрограммы:

Я выбрал параметры этих симуляций таким образом, чтобы избежать отбрасывания всех запросов . Это гарантирует, что мы будем сравнивать одинаковое количество точек данных для всех трёх алгоритмов. Давайте снова запустим симуляции, но с увеличенным значением RPS, чтобы все алгоритмы перестали справляться. Ниже показан график кумулятивных запросов , отбрасываемых с течением времени.

Least connections справляется с перегрузками гораздо лучше, но ценой чуть больших задержек 95-го и 99-го перцентиля. В некоторых случаях это может быть приемлемым компромиссом.
▍ Ещё один алгоритм
Если нам действительно нужно оптимизировать задержки, то требуется алгоритм, учитывающий их. Разве не будет здорово, если мы сможем скомбинировать алгоритмы dynamic weighted round robin и least connections? Так мы получим задержку weighted round robin и надёжность least connections.
Оказывается, не у нас первых возникла такая мысль. Ниже показана симуляция, использующая алгоритм под названием «peak exponentially weighted moving average» (или PEWMA). Название длинное и сложное, но не торопитесь, скоро мы его разберём.
Я подобрал для этой симуляции конкретные параметры, гарантирующие демонстрацию ожидаемого поведения. Если присмотреться, то можно заметить, что алгоритм, спустя какое-то время, перестаёт отправлять запросы самому левому серверу . Он делает так, потому что понимает, что все остальные серверы быстрее, и нет необходимости отправлять запросы самому медленному. Это приведёт к повышению задержки запросов .
Как он это делает? Он сочетает методики из dynamic weighted round robinс методиками из least connections, а поверх добавляет немного своей магии.
Для каждого сервера алгоритм отслеживает задержку последних N запросов . Вместо того, чтобы использовать её как среднее, он суммирует значения, но экспоненциально снижает коэффициент масштаба. Это приводит к тому, что чем старее задержка, тем меньше она влияет на сумму. Недавние запросы влияют на расчёт сильнее, чем старые.
Затем это значение умножается на количество открытых соединений с сервером , и результатом этого становится значение, которое мы используем для выбора сервера , которому будем отправлять следующий запрос . Чем меньше значение, тем лучше.
Как же он проявляет себя в сравнении с другими алгоритмами? Сначала давайте взглянем на 50-й, 95-й и 99-й перцентиль и сравним их с показанными выше данными least connections.

Мы видим улучшения во всех перцентилях! Гораздо больше они проявляются на высоких перцентилях, но стабильно присутствуют и в медиане. Вот те же данные в виде гистограммы.

А как насчёт отброшенных запросов ?

Изначально производительность лучше, но со временем алгоритм начинает вести себя хуже, чем least connections. Это логично. PEWMA оппортунистичен, он пытается добиться наилучшей задержки, а это значит, что он иногда может обеспечивать неполную загрузку сервера .
Я хочу здесь добавить, что PEWMA имеет множество параметров, которые можно настраивать. Реализация, которую я написал для этого поста, использует конфигурацию, хорошо работающую в тестированных мной ситуациях, однако дальнейшая настройка может обеспечить более качественные результаты в сравнении с least connections. Это один из недостатков PEWMA по сравнению с least connections: дополнительная сложность.
▍ Заключение
Я потратил на этот пост много времени. Было сложно сбалансировать реализм с простотой понимания, но результат мне понравился. Надеюсь, что понимание того, как сложные системы ведут себя на практике в идеальных и неидеальных ситуациях, поможет вам выработать интуитивное понимание того, что лучше применять при конкретных рабочих нагрузках.
Обязательное примечание : всегда следует выполнять бенчмарки собственных нагрузок, а не воспринимать советы из Интернета как панацею. В моих симуляциях игнорируются реальные ограничения (медленный запуск сервера, сетевые задержки), они настроены для демонстрации свойств каждого алгоритма. Это не реалистичные бенчмарки, которые стоит принимать за чистую монету.
В конце оригинала статьи есть песочница, в которой можно поэкспериментировать с большинством параметров в реальном времени.

- ruvds_перевод
- балансировка нагрузки
- балансировщик нагрузки
- оптимизация запросов
- веб-серверы
Что такое балансировка нагрузки в сети?
![]()
Современные веб-сайты и приложения генерируют большой трафик и одновременно обслуживают многочисленные запросы клиентов. Балансировка нагрузки помогает удовлетворить эти запросы и обеспечивает быстрый и надежный отклик веб-сайта и приложений.
В этой статье вы узнаете, что такое балансировка нагрузки, как она работает и какие существуют различные типы балансировки нагрузки.
Что такое балансировка нагрузки?
Балансировка нагрузки (Load Balancing) распределяет высокий сетевой трафик между несколькими серверами, позволяя организациям масштабироваться для удовлетворения рабочих нагрузок с высоким трафиком. Балансировка направляет запросы клиентов на доступные серверы, чтобы равномерно распределять рабочую нагрузку и улучшать скорость отклика приложений, тем самым повышая доступность веб-сайта или сервера.
Балансировка нагрузки применяется к уровням 4-7 в семиуровневой модели OSI.
- L4. Направление трафика на основе сетевых данных и протоколов транспортного уровня, например IP-адреса и TCP-порта.
- L7. Добавляет переключение содержимого в балансировку нагрузки, позволяя принимать решения о маршрутизации в зависимости от таких характеристик, как HTTP-заголовок, унифицированный идентификатор ресурса, идентификатор сеанса SSL и данные HTML-формы.
- GSLB. Global Server Load Balancing расширяет возможности L4 и L7 на серверы на разных сайтах.
Почему важна балансировка нагрузки?
Балансировка нагрузки необходима для поддержания информационного потока между сервером и пользовательскими устройствами, используемыми для доступа к веб-сайту (например, компьютерами, планшетами, смартфонами).
Есть несколько преимуществ балансировки нагрузки:
Надежность. Веб-сайт или приложение должны обеспечивать хороший UX даже при высоком трафике. Балансировщики нагрузки обрабатывают пики трафика, эффективно перемещая данные, оптимизируя использование ресурсов доставки приложений и предотвращая перегрузки сервера. Таким образом, производительность сайта остается высокой, а пользователи остаются довольными.
Доступность. Балансировка нагрузки важна, поскольку она включает периодические проверки работоспособности между балансировщиком нагрузки и хост-машинами, чтобы гарантировать, что они получают запросы. Если одна из хост-машин не работает, балансировщик нагрузки перенаправляет запрос на другие доступные устройства.
Балансировщики нагрузки также удаляют неисправные серверы из пула, пока проблема не будет решена. Некоторые подсистемы балансировки нагрузки даже создают новые виртуализированные серверы приложений для удовлетворения возросшего количества запросов.
Безопасность. Балансировка нагрузки становится требованием для большинства современных приложений, особенно с добавлением функций безопасности по мере развития облачных вычислений. Функция разгрузки балансировщика нагрузки защищает от DDoS-атак, перекладывая трафик атак на общедоступного облачного провайдера, а не на корпоративный сервер.
Прогнозирование. Балансировка нагрузки включает аналитику, которая может предсказать узкие места трафика и позволить организациям их предотвратить. Прогнозные аналитические данные способствуют автоматизации и помогают организациям принимать решения на будущее.
Как работает балансировка нагрузки?
Балансировщики нагрузки находятся между серверами приложений и пользователями в Интернете. Как только балансировщик нагрузки получает запрос, он определяет, какой сервер в пуле доступен, а затем направляет запрос на этот сервер.
Направляя запросы на доступные серверы или серверы с более низкой рабочей нагрузкой, балансировка нагрузки снимает нагрузку с загруженных серверов и обеспечивает высокую доступность и надежность.
Балансировщики нагрузки динамически добавляют или отключают серверы в случае высокого или низкого спроса. Таким образом, обеспечивается гибкость.
Балансировка нагрузки также обеспечивает аварийное переключение в дополнение к повышению производительности. Балансировщик нагрузки перенаправляет рабочую нагрузку с отказавшего сервера на резервный, уменьшая воздействие на конечных пользователей.
Типы балансировки нагрузки
Балансировщики нагрузки различаются по типу хранилища, сложности и функциональности балансировщика. Ниже описаны различные типы балансировщиков нагрузки.
Аппаратное обеспечение (Hardware-Based)
Аппаратный балансировщик нагрузки — это специализированное оборудование с установленным проприетарным программным обеспечением. Он может обрабатывать большие объемы трафика от различных типов приложений.
Аппаратные балансировщики нагрузки содержат встроенные возможности виртуализации, которые позволяют использовать несколько экземпляров виртуального балансировщика нагрузки на одном устройстве.
Программное обеспечение (Software-Based)
Программный балансировщик нагрузки работает на виртуальных машинах или серверах белого ящика, как правило, в составе ADC (application delivery controllers — контроллеры доставки приложений). Виртуальная балансировка нагрузки обеспечивает превосходную гибкость по сравнению с физической.
Программные балансировщики нагрузки работают на обычных гипервизорах, контейнерах или как процессы Linux с незначительными накладными расходами на bare metal сервере.
Виртуальный (Virtual)
Виртуальный балансировщик нагрузки развертывает проприетарное программное обеспечение для балансировки нагрузки с выделенного устройства на виртуальной машине для объединения двух вышеупомянутых типов. Однако виртуальные балансировщики нагрузки не могут решить архитектурные проблемы ограниченной масштабируемости и автоматизации.
Облачный (Cloud-Based)
Облачная балансировка нагрузки использует облачную инфраструктуру. Вот некоторые примеры облачной балансировки нагрузки:
- Балансировка сетевой нагрузки. Балансировка сетевой нагрузки основана на уровне 4 и использует информацию сетевого уровня, чтобы определить, куда отправлять сетевой трафик. Это самое быстрое решение для балансировки нагрузки, но ему не хватает балансировки распределения трафика между серверами.
- Балансировка нагрузки HTTP(S). Балансировка нагрузки HTTP(S) основана на уровне 7. Это один из наиболее гибких типов балансировки нагрузки, позволяющий администраторам принимать решения о распределении трафика на основе любой информации, поступающей с адресом HTTP.
- Внутренняя балансировка нагрузки. Внутренняя балансировка нагрузки почти идентична балансировке сетевой нагрузки, за исключением того, что она может балансировать распределение во внутренней инфраструктуре.
Алгоритмы балансировки нагрузки
Различные алгоритмы балансировки нагрузки предлагают разные преимущества и сложность в зависимости от варианта использования. Наиболее распространенные алгоритмы балансировки нагрузки:
Round Robin (По-круговой)
Последовательно распределяет запросы на первый доступный сервер и по завершении перемещает этот сервер в конец очереди. Алгоритм Round Robin используется для пулов равных серверов, но он не учитывает нагрузку, уже имеющуюся на сервере.
Least Connections (Наименьшее количество подключений)
Алгоритм наименьшего количества подключений предполагает отправку нового запроса наименее загруженному серверу. Метод наименьшего соединения используется, когда в пуле серверов много неравномерно распределенных постоянных соединений.
Least Response Time (Наименьшее время отклика)
Балансировка нагрузки с наименьшим временем отклика распределяет запросы на сервер с наименьшим количеством активных подключений и с самым быстрым средним временем отклика на запрос мониторинга работоспособности. Скорость отклика показывает, насколько загружен сервер.
Hash (Хеш)
Алгоритм хеширования определяет, куда распределять запросы, на основе назначенного ключа, такого как IP-адрес клиента, номер порта или URL-адрес запроса. Метод Hash используется для приложений, которые полагаются на сохраненную информацию о пользователях, например, тележки на веб-сайтах интернет магазинов.
Custom Load (Пользовательская нагрузка)
Алгоритм Custom Load направляет запросы к отдельным серверам через SNMP (Simple Network Management Protocol). Администратор определяет нагрузку на сервер, которую балансировщик нагрузки должен учитывать при маршрутизации запроса (например, использование ЦП и памяти, а также время ответа).
Заключение
Теперь вы знаете, что такое балансировка нагрузки, как она повышает производительность и безопасность сервера и улучшает взаимодействие с пользователем.
Различные алгоритмы и типы балансировки нагрузки подходят для разных ситуаций и вариантов использования, и вы должны иметь возможность выбрать правильный тип балансировщика нагрузки для своего варианта использования.
Как устроен балансировщик нагрузки: алгоритмы, методы и задачи
Рассказываем, как устроены алгоритмы и методы балансировки, какие существуют точки отказа в инфраструктуре, в чем преимущества облачных балансировщиков.

Для построения надежной отказоустойчивой инфраструктуры необходимо равномерное распределение нагрузки на серверы. За эту функцию отвечает балансировщик нагрузки — сервис дистрибуции заданий внутри кластера, гарантирующий работу системы даже в случае отказа одного из серверов.
Из статьи вы узнаете, как устроены алгоритмы и методы балансировки, какие существуют точки отказа, в чем разница между Load balancer и прокси.
Что такое балансировка сетевой нагрузки
Балансировка нагрузки — метод распределения сетевого трафика и задач между сетевыми устройствами.
На старте развития сервиса все компоненты (Frontend, Backend, база данных) могут находиться на одном сервере. Если нагрузка растет, его можно масштабировать вертикально: поменять конфигурацию сервера на более мощную или быстро добавить ресурсов в облачный сервер — добавить число vCPU или объем памяти.
На какое-то время этого будет достаточно, но в конечном итоге мощности сервера может не хватить, и задачи разделятся на несколько серверов. Так, фронтенд уйдет на отдельный сервер, бэкенд — на второй, а база данных будет храниться еще на одной машине. Причем каждый из серверов тоже можно «проапгрейдить» вертикально.

В облачных балансировщиках доступны различные комбинации протоколов, которые имеют дело с нагрузкой L4 и нагрузкой L7-уровней.
- TCP–TCP — классическая L4-балансировка,
- TCP–PROXY — информация о клиенте не теряется и передается в отдельном заголовке соединения,
- UDP–UDP — UDP-протокол быстрее, чем TCP, но менее надежен,
- HTTP–HTTP — L7-балансировка,
- HTTPS–HTTP — L7-балансировка с шифрованием и терминацией SSL-сертификата на балансировщике.

Заключение
На этом мы закончим обзор балансировщиков нагрузки. Мы рассмотрели современные подходы балансировки сетевой нагрузки, изучили функционал Load balancer и алгоритмы балансировки.
Балансировщики нагрузки — обязательный элемент сложной инфраструктуры, которая состоит из нескольких серверов и требует «умных» подходов к управлению трафиком.
Сравнительный анализ методов балансировки трафика

Сегодня я бы хотел дать некий обзорный доклад о балансировке трафика в высоконагруженных системах. Так как доклад обзорный, рассмотрим различные методы балансировки, что такое балансировка, в принципе, различные методы и алгоритмы балансировки, и озвучим плюсы и минусы того или иного метода.
Представим такую сюрреалистичную ситуацию, что мы фермеры, которые занимаются селекционными работами, и выводим, допустим, новые виды картофеля. Ходим мы, выращиваем свои картофелины, картошечка у нас классная, все ее любят, всем она нравится. Продаем ее на рынке и тут подумали: а как бы нам найти еще один способ ее продавать? Вспомнили, что мы ребята с IT’шным прошлым, когда-то этим занимались, в селекционеры мы ушли так, ради хобби, и решили: а давайте мы будем продавать ее в Интернете.
Сходили, недолго думая, купили сервер, запилили на него некий веб-сервис нашего Интернет магазина, и пошли к нам клиенты. Клиенты пошли, продажи растут, нагрузка на сервер повышается, повышается, повышается. Мы понимаем, что нагрузка растет, сервер надо тоже как-то апгрейдить. Увеличиваем его мощности, увеличиваем, увеличиваем, клиентов приходит все больше и больше, и, в конце концов, упираемся в такую ситуацию, что апгрейдить сервер больше некуда.
Случается такая неприятная ситуация, что год неурожайный, и у всех конкурентов картошка умерла. А у нас же селекционная картошка, она у нас классная и засухоустойчивая, и все резко ломанулись к нам. Причем, ломанулись так, что нагрузка на сервер возросла до таких размеров, что сервер просто взял и упал.

Мы взялись за голову, подумали: что же делать? Начали резко гуглить и наткнулись на такое понятие как «кластеризация». Решили: а чего бы нам не купить еще один сервак и не размазать наш веб-сервер по нему? Сходили, купили, поставили, и думали, что все у нас будет хорошо, что клиенты начнут ходить на оба наши сервера, нагрузочка немного выровняется, и все будет отлично.

Но после запуска, мы понимаем, что нагрузка на один из серверов у нас такая:

а нагрузка на второй сервер по-прежнему такая:

Вопрос: почему так происходит?
А происходит все потому, что мы не учитывали тот факт, что трафик между этими серверами надо как-то балансировать. Собственно, здесь мы переходим к основному вопросу — что же такое балансировка, и какие основные цели она преследует?

В первую очередь, балансировка применяется для распределения нагрузки между нашими серверами. Во-вторых, за счет балансировки мы можем повысить отказоустойчивость нашей системы, т.е., например, если один из наших серверов в кластере выходит из строя, то нагрузку на себя принимает второй, если сможет ее потянуть. Благодаря балансировке также достигается некая защита от некоторых видов атак, например, атаки на все соединения.
К балансировке, как и к любой системе, предъявляются некие требования. Требования такие:
- балансировка должна удовлетворять требованиям справедливости, т.е. любой запрос, пришедший в нашу систему, должен быть обслужен, а не просто брошен;
- балансировка должна быть эффективной, т.е. мы должны обеспечить такую ее работу, чтобы все наши сервера в кластере работали примерно равномерно, и брали на себя равномерную нагрузку;
- благодаря балансировке должно сокращаться время выполнения запроса, т.е. должно обеспечиваться сокращение времени отклика — как только запрос приходит к нам в систему, мы должны как можно быстрее его обслужить, на него ответить;
- балансировка должна удовлетворять требованиям предсказуемости, т.е. мы должны четко понимать какой алгоритм балансировки и в каком случае мы должны использовать;
- равномерность загрузки системы, в принципе, это требование схоже с эффективностью;
- балансировка должна быть масштабируемой, т.е. при резком увеличении нагрузки система балансировки должна обеспечивать стабильную работу нашего сервиса.
Подробнее о каждом.

Локально систему балансировки можно применять:
- на канальном уровне, как с использованием отдельного балансировщика, так и без него;
- на сетевом уровне;
- на транспортном уровне.

Балансировка на канальном уровне выполняется за счет следующего. Мы берем и навешиваем на некий специализированный интерфейс всех наших серверов один и тот же IP-адрес нашего ресурса, на который будут приходить запросы, и с которого будут уходить ответы. Но на ARP-запрос с этого IP-адреса сервера не должны отвечать. И мы навешиваем такой же IP-адрес на наш балансировщик, соответственно, на него будут приходить запросы, и отправляться ответы с него, и он же будет отвечать на ARP запросы. Т.о., получая запрос от клиента, наш балансировщик выбирает по определенному алгоритму тот или иной сервер, который будет обрабатывать этот запрос, подменяет destination MAC и отправляет его на обработку на данный сервер. Сервер его у себя обрабатывает, и т.к. мы не делали подмену заголовков на сетевом уровне, то непосредственно, минуя балансировщик, сразу отвечает клиенту через наш шлюз.
Тут к нам приходит руководство, и говорит: «Ребят, мы тут вконтакте посидели, полистали, всякие мемы почитали и поняли, что мужики от своих жен очень часто борща требуют. Подумали мы и решили, что давайте-ка мы сейчас рекламную компанию свеклы сделаем, и будем свеклу продавать. И все деньги вбухаем туда. А вас попросим как-нибудь сократить расходы на вашу классную систему балансировки».
Мы подумали и решили: а почему бы нам не избавиться от балансировщика? Но при этом прикинули, как мы можем без него реализовать балансировку.

А реализовать мы можем ее очень просто: нам необходимо превратить входящий unicast запрос в broadcast, либо в multicast, кто как хочет.
Делается это следующим образом. Все сервера должны на ARP запрос отвечать одним и тем же MAC-адресом, т.е. это может быть либо несуществующий MAC-адрес, либо какой-то мультикастовый. Либо мы можем навесить этот мультикастовый MAC-адрес на наш шлюз. Соответственно, запрос приходит на наш ресурс, и шлюз его просто размножает ко всем серверам, т.о. запросы поступают на все сервера одновременно, и каждый сервер должен сам понимать, должен ли он отвечать на запрос или нет. И понимать может очень просто, можно поставить, например, поставить деление на нуль srcIP, и дальше дело техники.
Плюсы и минусы алгоритма балансировки на канальном уровне.

В первую очередь, он не зависит от протоколов вышележащих уровней, т.е. можно балансировать нагрузку как по HTTP, так и по FTP, так и по SMTP. Затем, мы сокращаем расходы за счет отказа от выделенного балансировщика, в случае, если мы его не используем. Обратный трафик в сторону клиента не нагружает балансировщик, что тоже хорошо, например, для HTTP, когда у нас входящий запрос, как правило, легкий, а ответ на него весит порой в десятки и сотни раз больше. Затем, в современных реалиях очень полезный для нас плюс — это то, что мы используем только один публичный адрес, т.к. публичные IP нынче дорогие, это несомненно огромный плюс для нашей системы. И такая система способствует быстрому добавлению отключения серверов в кластере.
Из минусов стоит назвать: необходимость размещения сервера в одном сегменте, ограничение по входящей полосе в случае с разделяемыми адресами, т.к. трафик попадает на все сервера одновременно, нагружая нашу систему.
Из решений, использующих данный алгоритм балансировки, можно назвать Linux Virtual Server.

Балансировка на сетевом уровне. В принципе, механизм довольно схожий с балансировкой на канальном уровне, за одним единственным отличием — в данном случае, получая входящий запрос, наш балансировщик подменяет destination IP, соответственно, применяет его на тот сервер, который будет обрабатывать запрос. Сервер получает его, обрабатывает и должен передать его обратно балансировщику, чтобы тот выполнил обратную подмену.

Плюсы такого метода: он также не зависит от протоколов высокого уровня; полная прозрачность работы для сервера, т.е. сервер думает, что т.к. он получает запрос от клиента и работает с ним непосредственно напрямую, минуя всякие балансировщики, он не знает о них ничего; и здесь так же, как и в предыдущем методе, мы используем один публичный адрес, что тоже является большим плюсом.
Из недостатков — это повышенная нагрузка на балансировщик за счет обратного трафика. Каждый сервер должен отправить ответ, в первую очередь, на балансировщик, чтобы тот выполнил обратную подмену. Соответственно, в случае с HTTP нагрузка на балансировщик тоже будет большой.

Балансировка на транспортном уровне. Здесь очень тонкая грань, которая отличает балансировку на сетевом от балансировки на транспортном уровне. Ну, для простоты скажем, что в данном виде балансировки используются при балансировании нагрузки входящие порты источника и адресата.
Очень интересный пример реализации данного вида балансировки — это балансировка при помощи т.н. алгоритма ECMP, т.е. equal-cost multi-path. Выяснилось, что все современные роутеры могут распределять, балансировать нагрузку сами. Для этого нам достаточно на роутер анонсировать одну и ту же подсеть по разным маршрутам. Балансировщик в данном случае имея два одинаковых маршрута по своим общим метрикам будет распределять нагрузку по ним равномерно.
Но существует ряд нюансов, которые тоже надо учитывать. В первую очередь, наш роутер должен распределять нагрузку таким образом, чтобы пакеты в рамках одной TCP сессии попадали на один и тот же сервер. Это раз. Такой режим на роутерах, по-моему, на современных Cisco’ах называется perdestination и perflow и поддерживается по умолчанию.
Следующий нюанс — если мы пропишем на роутере статические маршруты, то мы должны как-то автоматизировать процесс добавления и удаления серверов из нашего кластера. Для того чтобы избежать этой проблемы, мы можем использовать различные протоколы маршрутизации, такие как BGP. Т.е. на каждый наш сервер мы устанавливаем некий софтварный BGP роутер, который будет анонсировать серверную сеть на роутер, который принимает запросы. Из софтварных таких роутеров можно назвать Квага или Bird.
И также необходимо учитывать, что существуют плюсы и минусы этого метода:

Преимущества такого алгоритма балансировки на транспортном уровне: он не зависит от протоколов высокого уровня, как и три предыдущих метода; также используется один публичный адрес; отсутствует необходимость приобретать дополнительное оборудование.
Но существуют свои недостатки, такие как, например, необходимость ставить на сервер дополнительный софт. Как я уже говорил, это программные роутеры BGP, хотя они не сильно нагружают наши сервера. Отсутствие server-affinity, т.е. все соединения будут разрываться в том случае, если мы добавляем или удаляем сервера из кластера. Также существует проблема ограниченности одинаковых маршрутов на разных роутерах. На слайде внизу приведены несколько роутеров марки Cisco — для первых двух одновременно поддерживается восемь одинаковых маршрутов, а для последнего поддерживается до 32-х одинаковых маршрутов. Также мы должны обеспечивать равномерность нагрузки, что предъявляет некие требования к производительности наших серверов, например, если мы добавим более производительный сервер в наш кластер, то нагрузка на него будет распределяться ровно такая же, как и на все остальные. И последний минус — это таймауты BGP протокола, т.е. если сервер перестает слать на роутер, анонсировать свою сеть, т.е. он вышел из строя, роутер в течение некоторого таймаута он может все еще распределять нагрузку по данному маршруту.

Из методов глобальной балансировки можно выделить следующие наиболее распространенные методы:

- балансировка на уровне DNS,
- балансировка на прикладном уровне — это проксирование и redirect запросов,
- балансировка на сетевом уровне — рассмотрим алгоритм Anycast.
В DNS балансировке чаще всего применяют т.н. алгоритмы Round Robin, т.е. это самый простой механизм балансировки, и балансировать таким образом можно любые системы, в которых доступ к сервису происходит по имени. Суть его вот в чем: на DNS сервер просто добавляется несколько А-записей с разными IP-адресами всех наших серверов, и сервер сам будет в цикличном порядке выдавать эти адреса. Т.е. первый запрос получит первый сервер, второй запрос — второй сервер, третий запрос — третий сервер, четвертый запрос — первый сервер и т.д.

Плюсы этого алгоритма: он абсолютно не зависит от протоколов высокого уровня; не зависит от нагрузки сервера, благодаря тому, что на всех клиентах, в основном, есть кэширующие DNS сервера, которые позволяют в случае резкого увеличения нагрузки на наш сервис компенсировать эту проблему; универсальность, т.е. DNS балансировку может выполнять как на локальном уровне в рамках одного дата-центра, так и на глобальном уровне. Последний самый главный плюс такого алгоритма балансировки — это очень низкая стоимость и быстрый старт, т.к. любой сайт фактически имеет доменное имя, имеет DNS сервер, соответственно, добавив несколько А-записей можно добиться балансировки уже на старте.
Из минусов следует назвать следующие. Сложность отключения серверов, в случае, например, их выхода из строя. В данном случае необходимо предусматривать некоторые способы резервирования этих серверов, например, по протоколу CARP или VRRP. Также сложно распределять нагрузку в нужной пропорции, т.к. DNS сервер ничего не знает о том, насколько загружен каждый сервер, а только лишь выдает их IP. Ограниченность IP-адресов — это наиболее весомый минус данного алгоритма балансировки, т.к. каждый сервер должен иметь свой глобальный IP-адрес, а как я уже говорил, IP-адреса нынче дорогие и ограничены. Кроме того, необходимо держать двойной запас серверной мощности, в том случае, когда у нас один из серверов выйдет из строя, а клиент будет ломиться на его IP и не получит нашего сервиса.
Из реализаций можно назвать любой DNS сервер, в качестве примера можно привести сервер Named из пакета BIND, PowerDNS сервер и т.д.

Следующий алгоритм — алгоритм проксирования. Суть его заключается в том, что в качестве балансировщика применяется т.н. умный прокси. Т.е. если балансировщик получает запрос к нашему ресурсу, он анализирует заголовки прикладного уровня, соответственно, он может понимать, запрос к какому ресурсу пришел на наш балансировщик, и направить запрос на тот или иной сервер, на котором этот ресурс содержится. Плюс ко всему, при получении этого запроса балансировщик может добавлять в заголовки HTTP, например, информацию о том, с какого IP пришел клиент, для того, чтобы сервер знал, куда его потом впоследствии отправлять, и с кем он работает. Выполнив запрос, сервер передает его обратно на балансировщик, тот выполняет необходимые манипуляции с новыми заголовками либо третьего уровня, либо седьмого уровня и отдает его клиенту.

Преимущества такого алгоритма балансировки в том, что мы можем обеспечить server-affinity, т.е. мы можем привязать конкретного клиента к определенному серверу, например, используя различные настройки cookie. Затем, мы можем распределять разные типы запросов разным серверам, т.е. мы, например, можем на одном сервере держать статику, которая у нас мало весит, а на другом сервере держать какой-то тяжелый контент, и, соответственно, понимая, анализируя HTTP заголовок, мы можем направлять запрос пользователя на тот или иной сервер. Благодаря этому алгоритму балансировки мы также можем фильтровать запросы по URL, защищаясь тем самым от различных родов атак, и самостоятельно определять работоспособность каждого узла, т.е. не просто доступность каждого сервера на сетевом уровне, но и понимать насколько работоспособен, в данном случае, наш софт на сервере, и не применять при этом никаких дополнительных средств.
- необходимость балансировать нагрузку на самих балансировщиках;
- это дополнительная точка отказа нашей системы;
- потребление очень большого количества ресурсов, т.к. анализируются запросы прикладного уровня;
- необходимость иметь свой прокси для каждого вида протоколов.

Redirect запросов. Redirect запросов имеет довольно ограниченное применение — применяется, в основном, для глобальной балансировки, и, в частности, для HTTP он хорошо применим. Суть его заключается в том, что мы получаем запрос от клиента на наш балансировщик, балансировщик отвечает ему редиректом на наш сервер, на котором содержатся ресурсы. Например, получая запрос по HTTP, балансировщик отвечает ему в ответ — выдает ошибку 302 move temporary с указанием адреса того сервера, на который дальше будет ходить наш клиент.

Плюсы такого алгоритма балансировки в том, что он распределяет запрос по разным серверам за счет анализа заголовков прикладного уровня.
Минусы — это достаточно малая применимость к протоколам высокого уровня; увеличение времени отклика за счет обращения к редиректору; фактически на каждый запрос клиента мы имеем по два запроса, т.е. первый запрос идет к балансировщику, второй запрос клиент делает непосредственно к серверу. Таким образом, если, например, клиент запрашивает какой-то контент, который порезан кусками, то на каждый кусок клиент будет выполнять по два запроса, что резко увеличивает время обслуживания клиента.
Из решений можно назвать программный веб-сервер nginx.

И последний вид балансировки, о котором я хотел рассказать, — это балансировка на базе Anycast. Этот алгоритм балансировки не требует никакой настройки со стороны клиента, и суть его заключается в следующем: мы из разных географических участков анонсируем один и тот же префикс сети. Таким образом, каждый запрос клиента будет маршрутизироваться на ближайший к нему сервер, который будет его обрабатывать.

Плюсы такого метода в том, что мы обеспечиваем минимальную задержку при обработке запроса, т.к. клиент будет обслуживаться на ближайшем к нему сервере не только с географической точки зрения, но и с топологической. Мы обеспечиваем доставку трафика, минуя магистральные каналы связи, соответственно, получаем некоторое удешевление трафика. Еще плюс в том, что нагрузку распределяет сама сеть, — мы здесь уже фактически никак не участвуем и об этом не заботимся, т.е. этот процесс ложится на плечи Интернет-провайдера. Высокая отказоустойчивость данного алгоритма балансировки, например, если один из серверов выходит из строя, то все запросы к нему будут просто переброшены на ближайший от него сервер. Легко добавлять и выводить из работы любые наши сервера — просто перестаем анонсировать по BGP, например, подсеть, и все запросы пользователей будут маршрутизироваться на другие сервера.
Из минусов следует назвать то, что существует возможность перестроения маршрутов, что для TCP сессии достаточно критично. Например, если в рамках одной сессии пакеты пойдут по другому маршруту на другой сервер, мы получим просто ресет по TCP, и сессия прервется. Отсутствует возможность проконтролировать, с какого узла обслуживается пользователь, т.к. этим рулит сама сеть, мы не можем знать, где сейчас в данный момент обслуживается пользователь, не прибегая к различным дополнительным решениям. Дорогое оборудование, т.е. если, например, мы будем использовать аппаратные роутеры, а это достаточно дорогая штука. Еще такой важный момент, что при использовании балансировки на базе Anycast необходимо учитывать интересы Интернет провайдеров. Т.к. все мы знаем, что Интернет-провайдеры любят пиринговаться друг с другом и, соответственно, в идеале пакеты должны ходить по наиболее короткому маршруту, но по факту получается так, что Интернет-провайдер передает пакеты там, где им дешевле, и это необходимо иметь в виду.

Какие алгоритмы балансировки применяются? О методах уже рассказали, теперь расскажу о том, каким образом выбираем, на какой сервер нам отправлять запросы. Существует несколько наиболее распространенных алгоритмов, о которых я хотел бы рассказать.

- Про Round Robin я выше рассказал. Существенным его недостатком является то, что нагрузка в данном случае распределяется без учета особенностей конкретного сервера. Алгоритм Weighted Round Robin позволяет навесить на каждый сервер определенный весовой коэффициент, который будет учитывать мощность и производительность того или иного сервера, т.о. более производительный сервер будет получать запросы чаще.
- Следующий алгоритм — Least Connections, т.е. в данном случае будут учитываться не просто нагрузка на сервер, но и количество одновременных соединений с данным сервером в данный момент. Если мы хотим, например, улучшить данный алгоритм, то мы можем использовать алгоритм Least Connections, например, с весами, т.е. навесив дополнительно на каждый сервер некий весовой коэффициент в соответствии с его производительностью и мощностью.
- Destination Hash Scheduling и Source Hash Scheduling — это т.н. алгоритмы, которые анализируют IP-адрес либо источника, либо адресата и выбирают из некой статической таблицы тот или иной сервер, на который будет проксироваться дальше запрос.
- И алгоритм Sticky Sessions — в данном случае обеспечивается привязка клиента к определенному серверу и, соответственно, все пакеты в рамках одной сессии будут ходить только на этот сервер.
Дальше хочу озвучить несколько решений интеграторов, потому что они часто применяются. Мы в своем сервисе эти решения не используем, поэтому я их просто назову. Здесь перечислены решения от Cisco — это решения Cisco ACE, некий хардварный балансировщик, который имеет теоретически пропускную способность до 16 Гбит в секунду и поддерживает практически все названные мною методы и алгоритмы балансировки. Также от Cisco есть решение под названием Cisco CSS, имеет гигабитные порты, позволяет балансировать трафик на сетевом уровне и выполнять глобальную балансировку на уровне DNS. От компании F5 решение — BigIP, довольно-таки гибкое, имеет свой скриптовый язык и благодаря ему, мы можем настраивать балансировку таким образом, как нам удобнее, и как мы этого хотим. И решение от компании radware, называется оно Alteon NG — выполняет и поддерживает практически все алгоритмы балансировки, позволяет анализировать настройку куки, выполнять привязку клиента к конкретному серверу и ряд других особенностей. Кому интересно, тот может погуглить и найти информацию, она доступна.

И в завершении я бы хотел рассказать о том, как мы применяем различные алгоритмы и методы балансировки, и как их можно всячески соединять.
Т.к. мы являемся провайдером SDN, у нас сеть серверов, которые распределены географически, соответственно, надо не просто балансировать нагрузку между серверами, нам надо еще и перенаправлять все запросы клиентов на ближайший к ним сервер. Как это работает? У нас имеется несколько балансировщиков, балансировщики у нас представляют из себя некий пропатченный DNS сервер, который мы выдаем, просто используя обычный DNS Round Robin. Получив его в свое распоряжение, клиент отправляет на него запрос на получение А-записи и, соответственно, адрес того сервера, который будет обрабатывать его зарос. Каждый балансировщик знает о доступности каждого сервера во всех локациях, которые распределены географически, соответственно, формирует у себя комплексную метрику, которая учитывает загруженность каждого сервера в данный момент, использование ресурсов центрального процессора, утилизацию памяти, доступность сервера с точки зрения сети, потом анализирует расстояние от каждого сервера до клиента и т.д. Т.е. вычисляется некая комплексная метрика, на основе которой принимается решение о том, куда мы должны перенаправить запрос этого клиента, и выбирается, как правило, ближайший не только с точки зрения географии сервер, но и с точки зрения топологии, т.о. сокращается время обслуживания каждого клиента и ускоряется сервис.

Балансировка — это очень просто! Даже коты балансировать умеют, как мы видим.
Контакты
Этот доклад — расшифровка одного из лучших выступлений на обучающей конференции разработчиков высоконагруженных систем HighLoad++ Junior.
Также некоторые из этих материалов используются нами в обучающем онлайн-курсе по разработке высоконагруженных систем HighLoad.Guide — это цепочка специально подобранных писем, статей, материалов, видео. Уже сейчас в нашем учебнике более 30 уникальных материалов. Подключайтесь!
Ну и главная новость — мы начали подготовку весеннего фестиваля «Российские интернет-технологии», в который входит восемь конференций, включая HighLoad++ Junior. Такие доклады, как этот — наша основная гордость!
- сергей зубов
- highload junior
- балансировка