Балансировка нагрузки с помощью HAProxy

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

Рассмотрим решение по балансировке нескольких веб-серверов при помощи сервиса HAProxy.

Общая схема

  • Сервер под балансировщик с двумя сетевыми интерфейсами (Рекомендация. Внешний адрес для связи с внешним миром и локальный для общения между узлами кластера);
  • Два и более серверов под распределение запросов/трафика;

В примере будет использоваться дистрибутив Linux CentOS 7.

В качестве backend-серверов я буду использовать несколько веб-серверов с NGINX и настроенным SSL.

Прежде всего установим NGINX

yum install nginx
Выполним настройку веб-сервера
server {
    listen 80;
    server_name devservers.network;
    rewrite ^(.*)$ https://devservers.network$1 permanent;
}
 
server {
    server_name devservers.network;
    access_log /srv/www/devservers.network/logs/access.log;
    error_log /srv/www/devservers.network/logs/error.log;
    root /srv/www/devservers.network/public_html;
 
    ssl on;
    ssl_session_timeout 24h;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    listen 443 ssl http2 proxy_protocol;
 
    set_real_ip_from 10.0.1.10/32;
    real_ip_header proxy_protocol;
 
    ssl_certificate /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;
 
    location / {
   try_files $uri $uri/ /index.html;
    }
 
    location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {
        try_files $uri =404;
    }
 
}

Конфигурацию SSL можно выполнить и на самом балансировщике, а бэкенд сервера оставить работать на 80 порту, но в моем случае я буду использовать режим SSL Pass-Through. Не забываем поместить SSL-сертификаты в директорию /etc/nginx/ssl.

Для того, чтобы принимать реальные IP-адреса клиентов на веб-сервере от HAProxy необходимо добавить proxy_protocol в параметр listen, а также указать реальный адрес откуда разрешить принимать запросы:

set_real_ip_from 10.0.1.10/32;
real_ip_header proxy_protocol;

Создадим приветственную страницу на обоих веб-серверах touch /srv/www/devservers.network/public_html со следующим содержимым, изменив только номер сервера для его дальнейшего определения:

<!DOCTYPE html>
<html>
<head>
<title>Server #1</title>
</head>
<body>
<h1>This is server #1</h1>
</body>
</html>

Добавляем NGINX в автозагрузку и запускаем сервис.

systemctl enable nginx
systemctl start nginx

И не забываем выполнить настройку Firewalld

firewall-cmd --permanent --add-service=https
firewall-cmd --reload

В качестве сервера балансировки я буду использовать VDS сервер с ресурсами 1 vCPU и 1Gb RAM. При правильной настройке, данных мощностей хватит для обслуживания 10-15к одновременных сессий, чего вполне достаточно для среднестатистического интернет-ресурса.

Установим HAProxy

Пакет HAProxy доступен в базовой репозитории CentOS.

yum install haproxy

Настройка HAProxy

HAProxy обладает очень гибкими настройками и конфигурация может содержать большое количество директив и условий. Конфигурационный файл состоит из нескольких секций. Директивы frontend, backend, listen должны иметь своё имя, к примеру defaults - может, но не обязательно, а такие как global - не должны.

Выполняем конфигурацию HAProxy в файле /etc/haproxy/haproxy.cfg. Рекомендую использовать свой конфигурационный файл, а представленный по умолчанию оставить как резерв.

Секция global

global
    log   /dev/log  local0
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    stats    timeout 30s
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon

Рассмотрим настройки в директиве global:

  • log - вести лог в /dev/log сохраняя в local0;
  • chroot - настройки безопасности, позволяющие работать HAProxy только в указанной директории;
  • maxconn - максимальное количество соединений на один процесс;
  • daemon - запуск процесса как демона.

Секция defaults

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

В нашем примере указаны следующие параметры:

defaults
    log global
    mode http
    retries 3
    option httplog
    option redispatch
    maxconn 2000
    contimeout 2000
    timeout http-request    10s
    timeout queue   1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
  • log - указывает в какой лог вести запись (global в данном случае означает, что используются параметры, заданные в секции global);
  • mode - устанавливает протокол взаимодействия, принимает одно из значений: tcp, http, health;
  • retries - количество попыток соединения с сервером в случае отказа;
  • option httplog - формат лога, в случае использования HAProxy для проксирования HTTP-запросов (рекомендуется включить данную настройку);
  • option redispatch - разрешает программе разорвать и переназначить сессию в случае отказа сервера;
  • contimeout - максимальное время ожидания успешного соединения с сервером.

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

Секция frontend

Укажем HAProxy какие запросы он должен обрабатывать, для этого задаем секцию frontend с именем https-in:

frontend https-in
    log /dev/log local0
    mode tcp
    option tcplog
    bind *:443
    default_backend backend-https-servers

Параметр bind со значением *:443 говорит о том, что HAProxy должен принимать все запросы на 443-й порт. Ещё раз напомню, что в данном примере я использую режим SSL Pass-Through, поэтому настройка SSL на самом HAProxy не выполняется.

Параметр default_backend указывает, какие сервера будут обрабатывать эти запросы. В данном случае - backend-https-servers, именно так необходимо назвать секцию backend.

Секция backend

В этой секции мы задаем алгоритм балансировки (параметр balance) и список серверов-обработчиков (server). В качестве алгоритма балансировки указываем roundrobin.

HAProxy имеет несколько алгоритмов балансировки:

  • roundrobin - каждый сервер получает запросы пропорционально своему весу, при этом веса серверов могут меняться на лету;
  • static-rr - то же, что и roundrobin, только изменение весов на лету не даст никакого эффекта;
  • leastconn - выбирает сервер с наименьшим количеством активных соединений;
  • first - выбирает первый сервер с доступными слотами для соединения source - на основе хэша IP-адреса отправителя запроса и весов серверов назначается сервер для соединения;
  • uri - сервер выбирается на основе адреса (без параметров) страницы;
  • url_param - сервер выбирается на основе GET-параметров запроса;
  • hdr - сервер выбирается на основе заголовков запроса;
  • rdp-cookie - сервер выбирается на основе cookie (если они не установлены, то применяется обычный roundrobin);

При перечислении серверов используется следующий формат: ключевое слово server, имя сервера, IP-адрес, дополнительные параметры (в данном случае - проверка статуса хоста и Proxy Protocol, которые позволяет перенаправлять реальные IP-адреса клиента с HAProxy на веб-сервер).

backend backend-https-servers
    mode tcp
    balance roundrobin
    option ssl-hello-chk
    server nginx01 10.0.1.3:443 check send-proxy
    server nginx02 10.0.1.4:443 check send-proxy

Для включения расширенной статистики HAProxy необходимо в конфигурационный файл добавить секцию:

listen stats
    bind :10001
    stats enable
    stats uri /haproxy_stats
    stats auth admin:password
  • bind :10001 - HAProxy будет ожидать запросы к порту 10001;
  • stats enable - включить отчёты со статистикой;
  • stats uri - установка адреса страницы с отчётом;
  • stats auth - логин и пароль для авторизации на странице со статистикой;

В секции listen указываем HAProxy, что он должен показывать статистику на странице /haproxy_stats на порту 10001 после ввода логина admin и пароля password.

Проверяем.

Переходим по адресу HAProxy (IP-адрес или хостнйем) и просто обновляем страницу. В зависимости от выбранного балансировщиком сервера будем получать ответ This is server #1 либо This is server #2.

Проверяем статистику. Переходим по адресу SITE_NAME:10001/haproxy_stats

На этом всё. Выше рассмотрен самый простой метод балансировки. Более сложная настройка требует грамотно составленного ТЗ и технических данных проекта. HAProxy очень удобный и гибкий в настройке инструмент с помощью которого можно выполнить балансировку и других сервисов, к примеру почтовый сервер, Memcached, Redis и пр.