Балансировщик 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 разберу полноценную конфигурацию.