Балансировщик HAProxy

HAProxy - это балансировщик нагрузки, который умеет распределять и проксировать запросы по TCP и HTTP протоколу.

Он бесплатный, легкий и шустрый, способен обрабатывать 20 тысяч запросов в секунду, а то и больше. Хорош для http-трафика. Его несложно вклинить в уже работающий проект. У него простая конфигурация и поэтому его можно быстро настроить под свои нужды.

Еще HAProxy использует Airbnb, Alibaba, Github, Instagram, Vimeo и еще не менее сорока известных высоконагруженных проектов.

Базовые понятия

Настройка HAProxy обычно сосредоточена вокруг пяти разделов: global, defaults, frontend, backend, реже listen.

  • Раздел global определяет общую конфигурацию для всего HAProxy
  • Defaults определяет настройки по-умолчанию для остальных разделов проксирования.
  • Раздел listen объединяет в себе описание для фронтенда и бэкенда и содержит полный список прокси. Он полезен для TCP трафика.

  • Раздел Frontend определяет, каким образом перенаправлять запросы к бэкенду в зависимости от того, что за запрос поступил от клиента.

  • Секция Backend содержит список серверов и отвечает за балансировку нагрузки между ними в зависимости от выбранного алгоритма

Разделы global, defaults и listen я затрагивать в статье почти не буду, поэтому просто дам стандартную конфигурацию из панели D2C, которую достаточно взять и применить:

global
 log 127.0.0.1 local0
 log 127.0.0.1 local1 notice
 maxconn 4096
 tune.ssl.default-dh-param 2048

defaults
 log global
 mode http
 option httplog
 option dontlognull
 option http-server-close
 option http-pretend-keepalive
 option forwardfor
 option originalto
 retries 3
 option redispatch
 maxconn 2000
 timeout connect 5000
 timeout client 50000
 timeout server 50000
 default-server init-addr last,libc,none

А вот на разделах Frontend и Backend я остановлюсь подробнее. Они помогут нам настроить балансировщик исходя из наших задач.

Frontend и типы распределения нагрузки

Фронтенд обычно отвечает за тип распределения нагрузки и выбор бэкенда исходя из правил списков контроля доступа (ACL).

Транспортный уровень или Layer 4

Самый простой способ распределения нагрузки — транспортный или Layer 4. В этом случае HAProxy будет выбирать Бэкенд в зависимости от порта, на который поступил запрос. Например, если запрос поступил на http://ваш-сайт.ру, то трафик обработает Бэкенд, отвечающий за 80 порт.

Пример конфигурации фронтенда для работы на транспортном уровне:

frontend haproxy
  mode tcp
  bind *:80
  default_backend http-backend

mode. Тип распределения нагрузки. В данном случае мы используем транспортный, поэтому указан протокол TCP. Если указать http, HAProxy станет работать на прикладном уровне (Layer 7). bind. IP Адрес и порт, которые HAProxy должен слушать «на входе». default_backend. Название конфигурации группы серверов, к которым надо направить запрос для обработки. В данном случае запросы пойдут к backend с названием http-backend.

На Бэкенде так же придется прописать mode tcp. В случае с транспортным уровнем конфигурация бэкенда может выглядеть так:

backend web_server_tcp
  mode tcp
  balance roundrobin
    server srv1 luna-1:80 check
    server srv2 luna-2:80 check

В HAProxy в одном конфигурационном файле допустимо использовать несколько типов распределения нагрузки. Например, можно для 80 порта задать режим http и распределять нагрузку на 7 уровне, а для 443 задать TCP и перенаправлять запросы на 4 уровне.

Такая функциональность будет полезна при работе с SSL: можно переадресовать запросы с 80 порта на 443:

frontend http_frontend *:80
  mode http
  redirect scheme https code 301 if !{ ssl_fc }

потом на транспортном уровне передать обработку запросов дальше:

frontend https_frontend_ssl_pass
  mode tcp
  bind *:443
  default_backend https-backend

и тогда уже сервера на бэкенде будут разбираться с SSL, а не HAProxy

Так будет выглядеть схема. В этом случае HAProxy передаст запрос на транспортном уровне, а его терминация произойдет на одном из серверов бэкенда.

Прикладной уровень или Layer 7

Прикладной уровень посложнее. Однако он позволит запускать несколько групп серверов приложений на одном домене и распределять между ними запросы в зависимости от их содержания.

Распределение нагрузки на прикладном уровне пригодится, если, например, один сайт состоит из разных веб-приложений.

Покажу на примере. Допустим, для блога используется WordPress, на сайте самописный движок на php, а личный кабинет требует Node-js для фронтенда. И все это обслуживают разные команды разработчиков, которые работают в нескольких git репозиториях.

При таком раскладе вы бы точно захотели разъединить все это физически. В этом помогут списки контроля доступа (ACL). Чтобы решить задачу, распределим трафик в завимости от пути на сайте:

если запрос идет к основному сайту http://ваш-сайт.ру, будем отправлять на Бэкенд №1 к NGINX, который отдает страницы промосайта;
при обращении к блогу по адресу /blog на Бэкенд №2 к Варнишам, которые кэшируют наш Блог;
при обращении к кабинету /cabinet на Бэкенд №3 с Nodejs.

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

Так схема выглядит в конфигурации фронтенда:

frontend http
 bind *:80
 mode http

acl url_blog path_beg /blog
 use_backend backend-2 if url_blog

acl url_cabinet path_beg /cabinet
 use_backend backend-3 if url_cabinet 

default_backend backend-1

mode http сообщает о том, чтобы HAProxy работал на уровне 7 по http-протоколу; инструкция acl url_blog path_beg /blog создает правило url_blog, которое будет срабатвать при обращении по этому адресу; инструкция use_backend backend-2 if url_blog сообщает, что если срабатывает правило url_blog, надо отправлять запросы к списку серверов на backend-2; default_backend backend-1 говорит о том, что по умолчанию отправлять всех к списку серверов на backend-1, если ни одно правило не сработало.

На бэкенде мы так же пропишем mode http:

backend web_server_tcp
  mode http
  balance roundrobin
    server srv1 luna-1:80 check
    server srv2 luna-2:80 check

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

За алгоритм выбора сервера отвечает секция Backend конфигурационного файла. Всего у Haproxy 9 алгоритмов.

Roundrobin

Первый в списке и самый простой алгоритм — Round Robin. Включается записью balance roundrobin в секции backend. С этой опцией HAProxy будет перебирать сервера по очереди и равномерно покрывать нагрузкой вашу «ферму». Пример конфигурации:

backend
balance roundrobin
server srv1 luna-1:80
server srv2 luna-2:80

* в моем случае luna-1 — это внутренний alias сервера, у вас скорее всего будет IP-адрес.

Если к серверам добавить параметр weight, то получится более продвинутая версия алгоритма — weighted roundrobin:

backend
balance roundrobin
server srv1 luna-1:80 weight 2
server srv2 luna-2:80 weight 1

Благодаря разным значением весов, балансировщик будет распределять нагрузку исходя из физических возможностей серверов. И сервер с параметром weight 2 из примера выше будет получать в 2 раза больше запросов, чем с параметром weight 1.

+

Алгоритм поможет добиться понятной, предсказуемой и стабильной балансировки. Все сервера получат справедливое, обозначенное в соответствии со значением веса, количество запросов на обработку.

Однако, Round Robin имеет один существенный недостаток, который не позволяет его применять там, где есть длинные сессии. При балансировке алгоритм не учитывает количество активных соединений. И даже если один из узлов будет полностью загружен, он все равно получит новые запросы.

Static-rr

Об этом алгоритме скажу кратко. По сути, Static-rr — это тот же Roundrobin. Одно исключение: изменение веса серверов на лету с его применением не даст никакого эффекта. Зато Statc-rr не ограничен по количеству бэкендов в отличие от Roundrobin, который может работать максимум с 4095 активными серверами.

Включается с помощью записи «balance static-rr» в секции бэкенда.

backend
balance static-rr
server srv1 luna-1:80
server srv2 luna-2:80

Least Connections

Алгоритм с названием «Least connections» учитывает количество подключений на серверах. Таким образом, каждый следующий запрос передаётся серверу с наименьшим количеством активных подключений. Включить алгоритм можно с помощью опции «balance leastconn» в секции backend.

backend
balance leastconn
server srv1 luna-1:80
server srv2 luna-2:80

Алгоритм «Least connections» в Haproxy динамический и способен назначать веса серверам «на лету».

+

Least connections подходит для задач, сопряженных с длительными соединениями. Например, для распределения нагрузки между серверами БД. Если на каких-то узлах будет слишком много активных подключений, новых запросов они уже не получат, и наоборот.

Нет никакого смысла применять алгоритм для задач с короткими сессиями, характерными, например, для http протокола. Там подойдет Round Robin.

First

Алгоритм First появился в HAProxy в версии 1.5. Если применить его , то балансировщик начнет заполнять свободные для соединений слоты начиная с первого сервера в списке бэкенда и по порядку.

Чтобы использовать алгоритм First, нужно прописать в секции Backend «balance first» и определить желаемое значение «maxconn» для серверов.

backend
balance first
server srv1 luna-1:80 maxconn 1000
server srv2 luna-2:80 maxconn 2000

Исходя из конфигурации в примере, HAProxy сначала заполнит тысячью соединений srv1, а потом пойдет наполнять srv2.

+

Главная цель алгоритма First — использовать наименьшее количество серверов. Это позволяет выключать дополнительные сервера в неинтенсивные часы работы.

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

Не сказать, что это минус ввиду специфики алгоритма, однако алгоритм First совершенно не учитывает веса серверов и распределяет нагрузку в зависимости от заданного вручную значения maxconn.

Source

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

+

Этот алгоритм обычно используют для работы по TCP протоколу, где невозможно присвоить cookie.

Если у источника динамический IP, алгоритм не сможет привязать его сессии к одному и тому же серверу.

URI

Алгоритм выбирает сервер исходя из адреса страницы. Благодаря ему один и тот же сервер будет обрабатывать определенные конечные адреса страниц. Включить алгоритм можно с помощью опции «balance uri«.

+

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

Минус алгоритма заключается в его узкой специфике применения. На мой взгляд, история с кэширующими серверами — единственная, где решение находит применение.

URL parameter

Алгоритм URL parameter выбирает сервер на основе GET-параметров запроса. Если используется модификатор «check_post», то алгоритм будет принимать решение, в том числе в зависимости от аргумента параметра. Для наглядности приведу 2 примера использования алгоритма:

  1. balance url_param userid
  2. balance url_param session_id check_post 64

В первом случае алгоритм будет запросы пользователей направлять на одинаковые сервера, руководствуясь значением их userid. Во втором случае алгоритм назначит определенный сервер для обработки запросов с id сессии 64.

Если никаких параметров передано не будет, алгоритм будет распределять нагрузку по принципу Round robin.

+

URL parameter может быть полезен, если вы хотите закрепить сервера за авторизованными пользователями, а для анонимных распределять нагрузку с помощью простого Round Robin.

Алгоритм будет работать только в http mode. Для TCP трафика он бесполезен.

HDR

Выбирается сервер на основе заголовка HTTP запроса. Если искомое значение отсутствует в заголовке, то используется принцип Round Robin.

  1. balance hdr(User-Agent: Mozilla/5.0)

В данном случае HAProxy будет искать в заголовке запроса запись User-Agent: Mozilla/5.0.

+

Алгоритм будет полезен, если необходимо, например, привязывать пользователей к серверам по типу браузера, адресу запроса и т.п.

Опять же, алгоритм для очень специфических узких задач. Пригодится не многим.

Алгоритм эквивалентен ACL ‘req_rdp_cookie()’ секции Frontend. Его задача — передавать одного и того же пользователя на один и тот же сервер с идентификацией по cookie.

+

Алгоритм предназначен привязки к серверам сессии с определенными cookie.

Если cookie не используются клиентом, то алгоритм будет работать как простой Round robin.

Резюме

В широкой практике обычно используются 2 алгоритма из списка: Round Robin и Leastconn. Первый для http трафика, второй — для обслуживания кластера баз данных. Остальные по ситуации.

Если у вас несколько кэширующих серверов, то пригодится URI. Если требуется привязать источники запросов к определенным серверам, помогут source и rdp-cookie. Если захотите распределить запросы по каким-то их свойствам, то пригодятся HDR и URL parameter.

Об алгоритмах, пока все. В следующей статье цикла о HAProxy разберу полноценную конфигурацию.