opened image

Минимальный балансировщик нагрузки на HAProxy: практическое руководство

Балансировка нагрузки - один из ключевых инструментов для построения отказоустойчивых и масштабируемых систем. HAProxy остаётся золотым стандартом в этой области благодаря высокой производительности, гибкости и нулевой стоимости. В этой статье разберём, как настроить балансировщик с нуля, углубимся в алгоритмы распределения нагрузки и рассмотрим продвинутые сценарии конфигурации.

 

Зачем нужен балансировщик нагрузки

 

Представьте ситуацию: Ваше приложение работает на одном сервере, и он выходит из строя. Результат - полная недоступность сервиса. Или другой сценарий: нагрузка растёт, один сервер уже не справляется, но добавить второй без изменения архитектуры невозможно.

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

Существует два принципиально разных подхода к балансировке: на уровне L4 (TCP/UDP) и на уровне L7 (HTTP). L4-балансировка работает быстрее, поскольку не анализирует содержимое пакетов, а просто перенаправляет соединения. L7-балансировка даёт больше возможностей - маршрутизация по URL, заголовкам, cookies - но требует больше ресурсов. HAProxy эффективно работает на обоих уровнях.

 

Установка

 

На Ubuntu или Debian:

sudo apt update
sudo apt install haproxy

 

На CentOS или Rocky Linux:

sudo dnf install haproxy

 

Проверим версию:

haproxy -v

 

Для продакшена рекомендуется версия 2.6 или выше - в ней улучшена работа с HTTP/2 и добавлены оптимизации производительности. Если в репозитории старая версия, подключите официальный PPA:

sudo add-apt-repository ppa:vbernat/haproxy-2.8 
sudo apt update 
sudo apt install haproxy

 

Архитектура конфигурации

 

Конфигурация HAProxy состоит из четырёх основных секций:

global - параметры процесса: логирование, лимиты, пользователь для запуска, настройки многопоточности.

defaults - настройки по умолчанию, которые наследуются остальными секциями. Позволяет избежать дублирования.

frontend - точка входа для клиентских соединений. Здесь указывается, на каком адресе и порту слушать, и определяются правила маршрутизации.

backend - группа серверов, между которыми распределяется нагрузка. Содержит алгоритм балансировки и параметры health checks.

Дополнительно существует секция listen, которая объединяет frontend и backend в одном блоке - удобно для простых конфигураций.

 

Минимальная рабочая конфигурация

 

Откройте файл /etc/haproxy/haproxy.cfg и замените содержимое:

global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    user haproxy
    group haproxy
    daemon
    maxconn 50000
    nbthread 4

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    option  forwardfor
    option  http-server-close
    timeout connect 5s
    timeout client  30s
    timeout server  30s
    timeout http-request 10s
    timeout http-keep-alive 15s
    retries 3

frontend http_front
    bind *:80
    capture request header Host len 50
    capture request header User-Agent len 100
    default_backend http_back

backend http_back
    balance roundrobin
    option httpchk GET /health
    http-check expect status 200
    
    server web1 192.168.1.10:8080 check
    server web2 192.168.1.11:8080 check
    server web3 192.168.1.12:8080 check

Необходимо изменить IP адреса и порты на необходимые Вам

 

Разберём ключевые параметры подробнее.

 

В секции global параметр maxconn 50000 задаёт максимальное число одновременных соединений. Это значение нужно согласовать с системными лимитами - проверьте ulimit -n. Параметр nbthread 4 включает многопоточность, что критично для современных многоядерных серверов.

В секции defaults директива option forwardfor добавляет заголовок X-Forwarded-For с реальным IP клиента - без этого бэкенды будут видеть IP балансировщика. Параметр option http-server-close закрывает соединение с бэкендом после каждого ответа, но сохраняет keep-alive с клиентом - оптимальный режим для большинства случаев.

Таймауты заслуживают отдельного внимания. timeout connect - время на установку TCP-соединения с бэкендом, 5 секунд достаточно для локальной сети. timeout client и timeout server - максимальное время неактивности соединения. timeout http-request ограничивает время на получение полного HTTP-запроса - защита от slowloris-атак. timeout http-keep-alive определяет, сколько ждать следующего запроса в keep-alive соединении.

 

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

 

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

 

roundrobin

 

Запросы распределяются по очереди между серверами. Это динамический алгоритм - веса серверов можно менять на лету. Подходит для большинства случаев с однородными бэкендами.

backend http_back
    balance roundrobin
    server web1 192.168.1.10:8080 weight 100 check
    server web2 192.168.1.11:8080 weight 100 check

Необходимо изменить IP адреса и порты на необходимые Вам

 

Пример ответов сервера при использовании этого алгоритма (с тремя бекенд серверами):

 

Минимальный балансировщик нагрузки на HAProxy: практическое руководство 1


leastconn

 

Запрос уходит на сервер с наименьшим числом активных соединений. Оптимален для долгих запросов: загрузка файлов, WebSocket, стриминг. Также хорошо работает, когда время обработки запросов сильно варьируется.

backend websocket_back
    balance leastconn
    server ws1 192.168.1.20:8080 check
    server ws2 192.168.1.21:8080 check

Необходимо изменить IP адреса и порты на необходимые Вам

 

source

 

Клиент привязывается к серверу на основе хеша IP-адреса. Один и тот же клиент всегда попадает на один сервер (пока тот доступен). Используйте, если приложение хранит сессии локально и нет возможности вынести их в Redis или базу данных.

backend legacy_back
    balance source
    hash-type consistent
    server app1 192.168.1.30:8080 check
    server app2 192.168.1.31:8080 check

Необходимо изменить IP адреса и порты на необходимые Вам

 

Параметр hash-type consistent включает consistent hashing - при добавлении или удалении сервера перераспределяется минимум клиентов.

 

uri

 

Балансировка на основе хеша URI. Запросы к одному и тому же URL всегда попадают на один сервер. Идеально для кеширующих бэкендов - максимизирует cache hit rate.

backend cache_back
    balance uri
    hash-type consistent
    server cache1 192.168.1.40:8080 check
    server cache2 192.168.1.41:8080 check

Необходимо изменить IP адреса и порты на необходимые Вам

 

hdr

 

Балансировка по значению HTTP-заголовка. Полезно для маршрутизации по API-ключу, tenant ID в мультитенантных приложениях или любому другому заголовку.

backend api_back
    balance hdr(X-API-Key)
    hash-type consistent
    server api1 192.168.1.50:8080 check
    server api2 192.168.1.51:8080 check

Необходимо изменить IP адреса и порты на необходимые Вам

 

Работа с весами

 

Для серверов разной мощности используйте веса:

backend http_back
    balance roundrobin
    server powerful 192.168.1.10:8080 weight 150 check
    server standard 192.168.1.11:8080 weight 100 check
    server small    192.168.1.12:8080 weight 50 check

Необходимо изменить IP адреса и порты на необходимые Вам

 

Сервер с weight 150 получит в три раза больше запросов, чем сервер с weight 50. Веса можно менять без перезапуска через runtime API.

 

Проверки здоровья

 

Health checks - критически важный механизм. Без них балансировщик будет отправлять запросы на неработающие серверы.

 

Базовая HTTP-проверка

 

backend http_back
    option httpchk GET /health
    http-check expect status 200
    server web1 192.168.1.10:8080 check inter 3s fall 3 rise 2

Необходимо изменить IP адреса и порты на необходимые Вам

 

Параметры после check:

  • inter 3s - интервал между проверками
  • fall 3 - после трёх неудачных проверок сервер помечается как DOWN
  • rise 2 - для возврата в ротацию нужны две успешные проверки

 

Проверка с заголовками

 

Некоторые приложения требуют определённый Host header:

backend http_back
    option httpchk
    http-check send meth GET uri /health hdr Host app.example.com
    http-check expect status 200

Необходимо изменить домен на необходимый Вам

 

Проверка содержимого ответа

 

Можно проверять не только код ответа, но и тело:

backend http_back
    option httpchk GET /health
    http-check expect string "status":"ok"

 

Или с помощью регулярного выражения:

backend http_back
    option httpchk GET /health
    http-check expect rstring "status":\s*"(ok|healthy)"

 

TCP-проверка

 

Для L4-балансировки или когда HTTP-проверка избыточна:

backend tcp_back
    mode tcp
    option tcp-check
    tcp-check connect
    tcp-check send PING\r\n
    tcp-check expect string PONG
    server redis1 192.168.1.60:6379 check

Необходимо изменить IP адреса и порты на необходимые Вам

 

Внешние проверки

 

Для сложной логики можно использовать внешний скрипт:

backend http_back
    option external-check
    external-check path "/usr/bin:/bin"
    external-check command /usr/local/bin/check-backend.sh
    server web1 192.168.1.10:8080 check

Необходимо изменить IP адреса и порты на необходимые Вам, также и скрипт.

 

Скрипт получает параметры сервера и должен вернуть код 0 для healthy, любой другой - для unhealthy.

 

Маршрутизация запросов

 

HAProxy позволяет направлять запросы в разные бэкенды на основе множества условий.

 

ACL - Access Control Lists

 

ACL определяют условия, которые затем используются в правилах маршрутизации:


frontend http_front
    bind *:80
    
    acl is_api path_beg /api/
    acl is_static path_end .css .js .png .jpg .gif .ico
    acl is_websocket hdr(Upgrade) -i websocket
    acl is_admin path_beg /admin/
    acl is_internal src 192.168.0.0/16 10.0.0.0/8
    
    use_backend api_back if is_api
    use_backend static_back if is_static
    use_backend websocket_back if is_websocket
    use_backend admin_back if is_admin is_internal
    
    default_backend web_back

Необходимо изменить IP адреса и порты на необходимые Вам

 

В последнем правиле is_admin is_internal - это логическое AND: запрос должен начинаться с /admin/ И приходить из внутренней сети.

 

Типы сравнений в ACL

 

path_beg - путь начинается с указанной строки
path_end - путь заканчивается на указанную строку
path_reg - путь соответствует регулярному выражению
hdr(name) - значение заголовка
hdr_beg(name) - заголовок начинается с...
src - IP-адрес источника
method - HTTP-метод
url_param(name) - значение URL-параметра

 

Маршрутизация по методу

 

frontend http_front
    bind *:80
    
    acl is_read method GET HEAD OPTIONS
    acl is_write method POST PUT PATCH DELETE
    
    use_backend read_replicas if is_read
    use_backend primary_back if is_write
    
    default_backend read_replicas

 

Модификация запросов

 

HAProxy может изменять запросы на лету:

frontend http_front
    bind *:80
    
    http-request set-header X-Real-IP %[src]
    http-request set-header X-Request-ID %[uuid()]
    
    http-request del-header X-Debug
    http-request set-path /api/v2%[path] if { path_beg /api/v1 }
    
    default_backend http_back

 

Модификация ответов

 

backend http_back
    http-response set-header X-Content-Type-Options nosniff
    http-response set-header X-Frame-Options DENY
    http-response set-header X-XSS-Protection "1; mode=block"
    
    http-response del-header Server
    
    server web1 192.168.1.10:8080 check

Необходимо изменить IP адреса и порты на необходимые Вам

 

Страница статистики

 

Встроенная статистика

 

frontend stats
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 10s
    stats auth admin:your_secure_password
    stats admin if TRUE

Необходимо изменить пароль на Ваш

 

Параметр stats admin if TRUE включает административный интерфейс - возможность включать и выключать серверы через веб-интерфейс.

Веб интерфейс:

 

Минимальный балансировщик нагрузки на HAProxy: практическое руководство 2

 

 

Готовая конфигурация для старта

 

 

Собраная конфигурация со всеми рекомендациями с этой статти: 

global
    log /dev/log local0
    chroot /var/lib/haproxy
    stats socket /var/run/haproxy.sock mode 660 level admin
    user haproxy
    group haproxy
    daemon
    maxconn 50000
    nbthread 4

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    option  forwardfor
    option  http-server-close
    timeout connect 5s
    timeout client  30s
    timeout server  30s
    timeout http-request 10s
    retries 3

frontend http_front
    bind *:80
    http-request set-header X-Real-IP %[src]
    default_backend http_back

backend http_back
    balance roundrobin
    option httpchk GET /health
    http-check expect status 200
    server web1 192.168.1.10:8080 check inter 3s fall 3 rise 2
    server web2 192.168.1.11:8080 check inter 3s fall 3 rise 2

frontend stats
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 10s
    stats auth admin:change_me

Необходимо изменить IP адреса и порты на необходимые Вам, также и пароль для статистики

 

Измените адресы серверов на свои, также измените пароль для статистии та проверьте конфигурацию командой haproxy -c -f /etc/haproxy/haproxy.cfg.