HAProxy: балансировка нагрузки и когда он лучше nginx
Разбираем конфигурацию HAProxy для балансировки нагрузки: frontend, backend и ACL, алгоритмы распределения и health checks, а также честное сравнение с nginx в задачах балансировки.
HAProxy: специализированный инструмент, а не универсальный веб-сервер
HAProxy (High Availability Proxy) — свободное программное обеспечение, спроектированное с единственной основной целью: приём входящих соединений (TCP на уровне L4 или HTTP на уровне L7) и их распределение между несколькими бэкенд-серверами по заданному алгоритму, с постоянным контролем их доступности. В отличие от nginx, изначально созданного как веб-сервер для отдачи статического содержимого и лишь позже дополненного функциями обратного прокси и балансировки, HAProxy никогда не был веб-сервером в привычном смысле — он не умеет и не пытается отдавать файлы с диска напрямую.
Эта узкая специализация — не недостаток, а сознательный архитектурный выбор: вся кодовая база и внутренняя модель обработки соединений HAProxy оптимизированы исключительно под задачу балансировки, что даёт заметно более широкий и глубоко проработанный набор возможностей именно в этой области — сложные схемы health checks, гибкие ACL для маршрутизации на основе практически любого параметра запроса, stick tables для хранения состояния клиентов прямо в памяти балансировщика, детальная встроенная статистика по каждому бэкенду.
Типичное практическое применение HAProxy — точка входа перед несколькими серверами приложения (каждый из которых может сам по себе работать под управлением nginx или Apache), распределяющая трафик между ними, отслеживающая их состояние и исключающая из ротации те серверы, что перестали отвечать корректно. Эта статья разбирает базовую структуру конфигурации HAProxy, основные алгоритмы балансировки и health checks, а также разбирает честно, в каких сценариях набор возможностей HAProxy реально оправдывает выбор в его пользу вместо более привычной балансировки через nginx.
Структура конфигурации: frontend, backend и listen
Конфигурация HAProxy строится вокруг трёх основных типов блоков: frontend описывает, как HAProxy принимает входящие соединения (на каком порту, с каким протоколом и по каким правилам маршрутизирует их дальше), backend описывает пул реальных серверов, обслуживающих трафик, и правила его балансировки, а listen — упрощённый гибрид, объединяющий frontend и backend в одном блоке для простых случаев, где сложная маршрутизация между несколькими бэкендами не требуется.
global
log /dev/log local0
maxconn 4096
defaults
mode http
timeout connect 5s
timeout client 30s
timeout server 30s
frontend web_front
bind *:80
default_backend web_servers
backend web_servers
balance roundrobin
server web1 10.0.0.11:80 check
server web2 10.0.0.12:80 check
server web3 10.0.0.13:80 check
Секция global задаёт общесистемные параметры процесса HAProxy (логирование, лимиты на число одновременных соединений), а defaults — значения по умолчанию для всех последующих frontend и backend блоков, которые можно переопределить в конкретном блоке при необходимости. Директива mode http включает обработку на уровне L7 с пониманием HTTP-протокола (заголовки, методы, коды состояния), тогда как mode tcp работает на уровне L4, просто передавая байты TCP-соединения без разбора содержимого — выбор режима определяется тем, нужна ли балансировщику логика, зависящая от содержимого HTTP-запроса, или достаточно распределения на уровне TCP-соединений.
Ключевое слово check в конце строки server включает активную проверку доступности этого сервера — без него HAProxy будет считать сервер всегда доступным и продолжит направлять на него трафик, даже если сервер фактически не отвечает, что делает check фактически обязательной практикой для любой конфигурации, претендующей на реальную отказоустойчивость.
ACL: маршрутизация на основе содержимого запроса
ACL (Access Control List) в терминологии HAProxy — не список доступа в привычном смысле файрвола, а именованное условие, проверяющее произвольный параметр запроса (путь URL, заголовок, домен, метод, cookie), которое затем используется в правилах маршрутизации для направления запроса на тот или иной backend.
frontend web_front
bind *:80
acl is_api path_beg /api/
acl is_static path_end .css .js .png .jpg .svg
use_backend api_servers if is_api
use_backend static_servers if is_static
default_backend app_servers
В этом примере запросы, начинающиеся с /api/, направляются в отдельный пул серверов, обслуживающих API, запросы к файлам с расширениями статики — в отдельный пул, оптимизированный под отдачу статических файлов, а всё остальное попадает в backend по умолчанию, обслуживающий основное приложение. Такая маршрутизация на основе содержимого запроса — типичный пример возможности, для которой в nginx потребовались бы отдельные блоки location с похожей логикой, но у HAProxy условия ACL можно свободно комбинировать логическими операторами и переиспользовать в разных правилах без дублирования условия целиком.
acl is_mobile hdr_sub(User-Agent) -i mobile
acl is_internal src 10.0.0.0/8
use_backend mobile_servers if is_mobile !is_internal
Health checks: активная и пассивная проверка бэкендов
Простейшая форма проверки доступности сервера в HAProxy — TCP health check, устанавливающий тестовое соединение с указанным портом сервера и считающий его доступным, если соединение успешно устанавливается, — этого достаточно для базового контроля того, что процесс на сервере вообще жив и слушает нужный порт, но не гарантирует, что приложение внутри действительно способно корректно обработать реальный запрос.
backend web_servers
option httpchk GET /health
http-check expect status 200
server web1 10.0.0.11:80 check inter 3s fall 3 rise 2
server web2 10.0.0.12:80 check inter 3s fall 3 rise 2
Директива option httpchk включает HTTP-level проверку — HAProxy отправляет реальный HTTP-запрос к указанному пути (в примере — /health, специальный эндпоинт приложения, отвечающий быстро и без обращения к тяжёлым внешним зависимостям) и, при указании http-check expect, проверяет не только сам факт ответа, но и конкретный код состояния или содержимое ответа. Такая проверка ловит ситуации, недоступные обычному TCP-чеку: например, процесс приложения жив и порт открыт, но сама логика приложения вернула ошибку 500 из-за потерянного соединения с базой данных.
Параметры inter, fall и rise настраивают чувствительность проверки: inter задаёт интервал между последовательными проверками, fall — сколько подряд неудачных проверок нужно, прежде чем сервер будет помечен как недоступный и исключён из ротации, а rise — сколько подряд успешных проверок требуется, чтобы вернуть ранее недоступный сервер обратно в ротацию. Подбор этих значений — практический компромисс между быстрой реакцией на реальный отказ и устойчивостью к кратковременным единичным сбоям сети, не отражающим реальное состояние сервера.
Алгоритмы балансировки: roundrobin, leastconn и другие
roundrobin — простейший и наиболее предсказуемый алгоритм, распределяющий последовательные запросы по кругу между всеми доступными серверами в равной пропорции; он хорошо подходит, когда серверы в пуле обладают сопоставимой производительностью и типичное время обработки отдельного запроса примерно одинаково между разными запросами.
backend web_servers
balance leastconn
server web1 10.0.0.11:80 check
server web2 10.0.0.12:80 check weight 2
server web3 10.0.0.13:80 check
leastconn направляет каждое новое соединение на сервер с наименьшим текущим числом активных подключений — этот алгоритм заметно лучше roundrobin справляется с ситуацией, когда время обработки отдельных запросов сильно различается (например, часть запросов — быстрые чтения из кеша, а часть — тяжёлые отчёты с обращением к базе данных), поскольку не позволяет медленным запросам накапливаться на одном сервере просто потому, что подошла его очередь по кругу.
Параметр weight позволяет задать относительный вес конкретного сервера в пуле — полезно, когда серверы в пуле физически неоднородны (например, один сервер мощнее остальных после частичного апгрейда инфраструктуры) и должны получать пропорционально больше или меньше трафика, чем остальные, без изменения самого алгоритма балансировки. Помимо этих двух наиболее распространённых алгоритмов, HAProxy поддерживает и другие: source (привязка клиента к серверу на основе хеша его IP-адреса, полезно для примитивной привязки сессии без внешнего хранилища) и uri (балансировка на основе хеша пути запроса, полезная для повышения эффективности локального кеша на каждом сервере за счёт того, что одинаковые URL стабильно попадают на один и тот же сервер).
Stick tables: состояние клиентов прямо в балансировщике
Stick table — внутренняя структура данных, которую HAProxy хранит в памяти и обновляет на основе проходящего через него трафика, привязывая произвольный ключ (чаще всего IP-адрес клиента) к набору счётчиков и метаданных — количество запросов за период, к какому серверу был привязан клиент в прошлый раз, и другие показатели.
frontend web_front
bind *:80
stick-table type ip size 100k expire 10m rate(10s)
tcp-request connection track-sc0 src
tcp-request connection reject if { src_conn_rate gt 50 }
В этом примере HAProxy отслеживает частоту новых TCP-соединений с каждого уникального IP-адреса за скользящее окно в 10 секунд и отклоняет новые подключения от адреса, превысившего порог в 50 соединений за этот период, — это простой, но эффективный встроенный механизм ограничения частоты запросов (rate limiting) прямо на уровне балансировщика, без необходимости разворачивать для этого отдельный сервис или модуль.
Другое частое применение stick tables — привязка сессии к конкретному бэкенд-серверу (session persistence, или sticky sessions) на основе значения cookie или IP-адреса клиента, что важно для приложений, хранящих состояние сессии локально на конкретном сервере, а не в общем внешнем хранилище вроде Redis — без такой привязки последовательные запросы одного и того же пользователя, случайно направленные на разные серверы алгоритмом балансировки, увидели бы разные, несинхронизированные копии его сессии.
SSL termination и встроенная статистика
HAProxy умеет принимать HTTPS-соединения и расшифровывать их самостоятельно (SSL/TLS termination) прямо на уровне балансировщика, передавая на бэкенд-серверы уже расшифрованный обычный HTTP-трафик, — это снимает нагрузку на шифрование с самих бэкенд-серверов и централизует управление сертификатами в одном месте.
frontend web_front_ssl
bind *:443 ssl crt /etc/haproxy/certs/example.com.pem
http-request set-header X-Forwarded-Proto https
default_backend web_servers
Заголовок X-Forwarded-Proto нужно передавать явно, поскольку после расшифровки TLS на уровне балансировщика бэкенд-сервер получает уже обычное HTTP-соединение и без этого заголовка не может знать, что исходный запрос клиента был по HTTPS — многие приложения используют этот заголовок для корректного формирования абсолютных ссылок на собственные страницы и для решения о необходимости редиректа на HTTPS.
Отдельная практичная возможность — встроенная страница статистики, включаемая директивой stats enable вместе с указанием пути и учётных данных для доступа: она показывает в реальном времени состояние каждого сервера в каждом backend, текущее число активных соединений, результат последней проверки здоровья и накопленную статистику по трафику, без необходимости разворачивать для этой единственной цели отдельную систему мониторинга вроде Prometheus и Grafana, хотя для полноценного долгосрочного мониторинга такая интеграция всё равно рекомендуется.
Когда HAProxy действительно лучше nginx для балансировки
Честный ответ на вопрос "что лучше" в этой паре — оба инструмента способны балансировать HTTP-трафик между несколькими серверами, и для простой схемы с равномерным распределением round-robin между несколькими равноценными бэкендами разница в практическом результате минимальна, а конфигурация в обоих случаях занимает буквально несколько строк.
HAProxy предпочтительнее, когда реально нужны продвинутые health checks с проверкой конкретного HTTP-эндпоинта и гибкой настройкой чувствительности через inter/fall/rise, сложная маршрутизация на основе множества ACL-условий, комбинируемых логическими операторами, stick tables для встроенного ограничения частоты запросов или привязки сессии без внешнего хранилища, а также когда важна детальная встроенная статистика по состоянию каждого бэкенда без развёртывания отдельной системы мониторинга.
nginx, в свою очередь, оправданно проще выбрать, когда балансировка нагрузки — лишь одна из многих задач одного и того же сервера, который уже и так обслуживает статику, выполняет функции обратного прокси для PHP-FPM или другого обработчика приложения и, возможно, кеширует ответы — держать для этого отдельный процесс HAProxy имеет смысл только тогда, когда его дополнительные возможности балансировки действительно используются, а не добавляют лишний компонент в архитектуру без реальной практической выгоды.
На практике многие production-архитектуры используют оба инструмента вместе, а не выбирают один вместо другого: HAProxy на переднем крае принимает весь входящий трафик, распределяет его между несколькими серверами приложения и следит за их состоянием через продвинутые health checks, а на каждом из этих серверов nginx выполняет свою привычную работу — отдаёт статику и проксирует динамические запросы дальше в PHP-FPM или другой бэкенд конкретного приложения.
Тайм-ауты и защита от медленных или зависших клиентов
Отдельная область, где конфигурация HAProxy требует осознанного внимания, — тайм-ауты на разных этапах обработки соединения: timeout connect ограничивает время ожидания установления соединения с бэкенд-сервером, timeout client — время ожидания данных от самого клиента, а timeout server — время ожидания ответа от бэкенда после отправки ему запроса. Слишком большие значения этих тайм-аутов позволяют медленным или намеренно затягивающим соединение клиентам надолго занимать ресурсы балансировщика, а слишком маленькие — обрывают легитимные, но чуть более медленные запросы раньше времени.
defaults
timeout connect 5s
timeout client 30s
timeout server 30s
timeout http-request 10s
timeout queue 30s
Директива timeout http-request отдельно ограничивает время, за которое клиент должен полностью передать заголовки HTTP-запроса, — это защищает от атак вроде Slowloris, при которых клиент намеренно передаёт заголовки запроса по одному байту с большими паузами, стремясь удерживать открытыми как можно больше соединений и исчерпать лимит одновременных подключений сервера. Без разумного ограничения этого тайм-аута такая атака способна создать реальную проблему доступности даже при относительно небольшом числе одновременных вредоносных соединений.
Директива timeout queue ограничивает время, которое запрос может провести в очереди ожидания, если все серверы в backend уже заняты обработкой предыдущих запросов (актуально при использовании ограничения maxconn на конкретный сервер) — по истечении этого времени HAProxy вернёт клиенту ошибку вместо неограниченного ожидания, что предпочтительнее полного зависания запроса на неопределённый срок при кратковременном всплеске нагрузки, превышающем текущую пропускную способность пула бэкендов.
Итог: инструмент под конкретную задачу балансировки
HAProxy — не замена nginx и не универсально более совершенный инструмент, а специализированное решение именно для задачи балансировки нагрузки и постоянного контроля состояния бэкендов, с заметно более глубоким и гибким набором возможностей именно в этой узкой области, чем у nginx, для которого балансировка — лишь одна из многих встроенных функций универсального веб-сервера.
Выбор между ними стоит делать не по абстрактной репутации инструментов, а по реальному набору требований конкретного проекта: если достаточно простого равномерного распределения между несколькими бэкендами, nginx, скорее всего, уже закрывает эту задачу без необходимости добавлять в архитектуру ещё один компонент. Если же нужны сложные health checks, детальная ACL-маршрутизация или встроенное ограничение частоты запросов через stick tables — опишите архитектуру проекта в заявке внизу страницы, поможем спроектировать схему балансировки под реальную нагрузку.
Почему стоит выбрать нас
Быстрый запуск
Подберём конфигурацию и развернём проект в день обращения.
Надёжность
NVMe-диски, защита от DDoS и резервные копии по расписанию.
Поддержка 24/7
Дежурные инженеры на связи в любое время суток.
Настройка под ключ
Установим CMS, настроим веб-сервер, SSL и почту за вас.
Перенос без простоя
Аккуратно перенесём сайт и базу данных на новый сервер.
Прозрачные цены
Актуальные тарифы и честный подбор без навязывания.
Вопросы и ответы
HAProxy — это веб-сервер, как nginx?
Нет, HAProxy не умеет отдавать статические файлы и не предназначен для этого — это специализированный балансировщик нагрузки и прокси уровней L4 (TCP) и L7 (HTTP), спроектированный исключительно для приёма соединений и их распределения между серверами, тогда как nginx изначально веб-сервер с постепенно добавленными функциями обратного прокси и балансировки.
Можно ли использовать HAProxy и nginx вместе на одном проекте?
Да, и это распространённая практическая схема — HAProxy принимает входящий трафик и распределяет его между несколькими бэкенд-серверами, на каждом из которых работает nginx, обслуживающий статику и передающий динамические запросы дальше в PHP-FPM или другой обработчик приложения.
Что такое stick table и зачем оно нужно?
Stick table — внутренняя структура данных HAProxy, хранящая информацию о клиентах (например, число запросов с конкретного IP-адреса за период) прямо в памяти самого балансировщика, что позволяет реализовать ограничение частоты запросов, отслеживание аномального поведения клиента и привязку сессии к конкретному серверу без обращения к внешнему хранилищу.
Какой алгоритм балансировки выбрать по умолчанию?
roundrobin — разумный выбор по умолчанию для серверов с примерно одинаковой мощностью и однородной нагрузкой на запрос; leastconn предпочтительнее, когда время обработки отдельных запросов заметно различается, поскольку он направляет новое соединение на сервер с наименьшим текущим числом активных подключений, а не просто по очереди.
Нужны ли health checks, если серверы и так редко падают?
Да, health checks нужны не только на случай полного отказа сервера — они также ловят ситуации частичной деградации: например, приложение зависло, но сам процесс всё ещё жив и слушает порт, и без активной проверки конкретного HTTP-эндпоинта такой сервер продолжил бы получать трафик, отвечая ошибками, пока кто-то не заметит проблему вручную.
Насколько сложнее настроить HAProxy по сравнению с nginx для простой балансировки?
Для базовой схемы с двумя-тремя бэкендами и простым алгоритмом round-robin сложность сопоставима — обе конфигурации занимают буквально несколько строк; разница в сложности проявляется, когда нужны продвинутые health checks, ACL-based маршрутизация или stick tables, где у HAProxy готовый и хорошо документированный синтаксис, а у nginx для того же результата обычно нужны сторонние модули или более сложные обходные конструкции.
Поддерживает ли HAProxy SSL/TLS termination?
Да, HAProxy умеет принимать HTTPS-соединения и расшифровывать их самостоятельно (SSL termination), передавая на бэкенд уже расшифрованный HTTP-трафик, либо работать в режиме сквозной передачи TCP-соединения без расшифровки (SSL passthrough), если расшифровка должна выполняться уже на самом бэкенд-сервере.
Есть ли у HAProxy готовая панель мониторинга состояния бэкендов?
Да, встроенная страница статистики (stats page), включаемая директивой stats enable в конфигурации, показывает в реальном времени состояние каждого бэкенда, число активных соединений, статус health checks и накопленную статистику по каждому серверу без необходимости разворачивать отдельную систему мониторинга только для этой цели.
Как оплачивать услуги?
Оплата помесячная, для облачных серверов доступна почасовая тарификация. Принимаются карты, банковские переводы и электронные кошельки.
Есть ли круглосуточная поддержка?
Да, дежурные инженеры на связи 24/7.
Выдаёте ли закрывающие документы?
Да, предоставим необходимые документы для оплаты.
Как оставить заявку?
Заполните форму на странице или нажмите «Заказать» — мы свяжемся и поможем с подбором.