Привет, Habr.
Сегодня снова поговорим о прокси — это, пожалуй, моя любимая тема, и я рад вернуться к ней. На этот раз речь пойдёт об универсальном солдате в мире балансировки — HAProxy. Этот инструмент уже много лет остаётся стандартом в высоконагруженных системах, но за последние релизы он стал ещё мощнее и гибче.

Напомню, HAProxy (High Availability Proxy) — это высокопроизводительный, отказоустойчивый прокси-сервер и балансировщик нагрузки, способный работать как с HTTP(S), так и с TCP-трафиком. Это делает его идеальным решением не только для веб-приложений, но и для баз данных, почтовых систем, брокеров сообщений и других сервисов.

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

Итак, чем же хорош HAProxy как балансировщик и что интересного появилось в новых версиях?

ACL

ACL (Access Control List) — это универсальный инструмент, позволяющий описывать условия обработки запросов на основе различных признаков: IP-адреса клиента, пути и параметров URL, HTTP-заголовков, методов, доменов SNI, а также регулярных выражений и масок. По сути, ACL — это “условие if” в конфигурации HAProxy: оно вычисляется для каждого запроса, и в зависимости от результата (true/false) выбирается нужное действие — перенаправить, заблокировать, модифицировать или отправить в определённый backend. Благодаря ACL конфигурация перестаёт быть статичной и превращается в набор динамических правил, реагирующих на контекст запроса. Это особенно важно для маршрутизации (routing), когда от характеристик запроса зависит выбор целевого сервиса. Пару примеров смотри ниже:

Маршрутизация по префиксу пути

acl is_api_path path_beg /api
use_backend api_backend if is_api_path

Проверяет, начинается ли путь с /api; если да — трафик идёт в api_backend.

Блокировка диапазона IP-адресов

acl blocked_ips src 192.168.0.0/24
http-request deny if blocked_ips

Отклоняет запросы от клиентов из подсети 192.168.0.0/24.

Фильтрация по HTTP-методу

acl is_post_method method POST
use_backend write_backend if is_post_method

Если метод запроса POST — отправляем в write_backend.

Сопоставление пути с RegExp

acl has_version_in_path path_reg ^/v[0-9]+/
use_backend versioned_backend if has_version_in_path

Срабатывает, если путь начинается с /v и номера версии, например /v2/users.

Маршрутизация по домену (SNI) в TLS

acl sni_is_api req_ssl_sni -i api.example.com
use_backend api_backend if sni_is_api

Выбирает api_backend, если в TLS SNI указан домен api.example.com.

Условие с несколькими признаками

acl is_mobile hdr_sub(User-Agent) Mobile
acl is_logged_in cook(session_id) -m found
use_backend mobile_backend if is_mobile is_logged_in

Отправляет запрос в mobile_backend, если User-Agent содержит “Mobile” и есть cookie session_id.

ACL можно комбинировать, использовать логические операции AND/OR, отрицания (!), объединять их в сложные условия и выносить в отдельные секции для повторного применения. Именно на этой базе строятся гибкие сценарии маршрутизации, фильтрации и балансировки, которые мы рассмотрим в следующем разделе про routing, где ACL будут применяться в более комплексных и многоуровневых примерах.

Routing

Балансировка на уровне L4 в HAProxy — это про простоту и скорость. Представьте, у нас есть база данных или любая другая TCP-служба, и мы хотим, чтобы запросы распределялись между несколькими серверами без лишней логики. L4 работает на транспортном уровне, не заглядывая внутрь пакетов, поэтому он быстрый, экономичный по ресурсам и легко переваривает десятки тысяч соединений. Настроить можно буквально в несколько строк: определяем порт, на котором слушаем трафик, и список серверов, куда его “пинаем”. Например, MySQL-клиент подключается к одному адресу, а HAProxy незаметно для него распределяет запросы между несколькими базами. Это удобно, когда важно просто держать нагрузку под контролем и не заморачиваться с анализом содержимого запросов.

frontend mysql
  mode tcp
  bind :3306
  default_backend mysql_servers

backend mysql_servers
  mode tcp
  balance leastconn
  server s1 192.168.0.10:3306
  server s2 192.168.0.11:3306

В этом примере входящие TCP-подключения к порту 3306 равномерно распределяются между двумя MySQL-серверами. Такой режим практически не нагружает процессор, позволяет обрабатывать десятки тысяч соединений и подходит для сервисов, где не требуется анализ содержимого запросов. Однако он не даёт гибкости маршрутизации, так как решения принимаются только по IP и порту. Есть одно уточнение: L4 можно использовать даже с HTTP(S)-приложением, если не требуется анализ содержимого пакетов (payload). В этом случае всё будет работать корректно — по сути, мы просто передаём сырые байты без разбора

Балансировка на уровне L7 — это уже “умная” маршрутизация. Здесь HAProxy понимает, что именно внутри запроса, и может принимать решения по пути, заголовкам, cookies, методу или даже версии API. Например, у нас есть веб-приложение: статику хотим отдавать с одних серверов, API-запросы — с других, а админку — только авторизованным пользователям. При этом мобильным клиентам можно выделить отдельный пул, чтобы не мешали десктопным. Всё это делается с помощью ACL, которые проверяют условия и направляют трафик в нужное место. В итоге один фронтенд принимает весь HTTP-поток, а дальше уже мы решаем, куда пойдёт каждый запрос — хоть в соседний сервер, хоть на другой конец света. Такой подход потребляет больше ресурсов, потому что HAProxy разбирает протокол, но взамен мы получаем гибкость и полный контроль над маршрутизацией.

frontend http_front
    bind *:80
    mode http

    acl is_api path_beg /api
    acl is_static path_end .css .js .png .jpg
    acl is_admin hdr(host) -i admin.example.com
    acl is_mobile hdr_sub(User-Agent) Mobile
    acl has_auth cook(session_id) -m found

    use_backend api_backend if is_api
    use_backend static_backend if is_static
    use_backend admin_backend if is_admin has_auth
    use_backend mobile_backend if is_mobile !is_admin
    default_backend default_web

backend api_backend
    balance leastconn
    server api1 10.0.0.3:8080 check
    server api2 10.0.0.4:8080 check

backend static_backend
    balance roundrobin
    server cdn1 10.0.0.5:80 check
    server cdn2 10.0.0.6:80 check

backend admin_backend
    balance source
    server admin1 10.0.0.7:8080 check

backend mobile_backend
    balance roundrobin
    server mob1 10.0.0.8:8080 check
    server mob2 10.0.0.9:8080 check

backend default_web
    balance roundrobin
    server web1 10.0.0.10:8080 check

В этом примере один фронтенд принимает весь HTTP-трафик и с помощью ACL распределяет его: API-запросы идут на отдельный пул, статика — на CDN-сервера, админка доступна только авторизованным пользователям с определённого домена, мобильные клиенты получают свой backend, а всё остальное уходит в общий веб-пул. Такой подход требует больше CPU и памяти из-за разбора протокола, но даёт максимальную гибкость в управлении трафиком и позволяет реализовывать сложные схемы маршрутизации.

Также доступны варианты настройки под gRPC или HTTP/2 для взаимодействия между clients > haproxy (downstream) и haproxy > backends (upstream):

global
    log stdout format raw local0
    maxconn 4096

defaults
    log     global
    mode    http
    option  httplog

frontend fe_https
    bind *:80
    bind *:443 ssl crt /etc/haproxy/certs/mysite.pem alpn h2,http/1.1
    http-request redirect scheme https unless { ssl_fc }
    default_backend be_h2_tls

frontend fe_h2c
    bind *:8080 proto h2
    default_backend be_h2c

backend be_h2_tls
    balance roundrobin
    server srv1 10.0.0.11:9443 alpn h2 check ssl verify none
    server srv2 10.0.0.12:9443 alpn h2 check ssl verify none

backend be_h2c
    balance roundrobin
    server srv3 10.0.0.21:9000 proto h2 check
    server srv4 10.0.0.22:9000 proto h2 check

В этой конфигурации HAProxy принимает входящие соединения от клиентов с поддержкой HTTP/2 (TLS) на фронтенде fe_https и без TLS (H2C) на фронтенде fe_h2c. В первом случае, при подключении по HTTPS с использованием ALPN (h2,http/1.1), клиент и HAProxy согласовывают протокол HTTP/2, который затем используется для обмена данными с backend-серверами по защищённому каналу TLS. Во втором случае H2C (HTTP/2 без TLS) применяется для внутреннего взаимодействия, где шифрование не требуется, но преимущества HTTP/2 (мультиплексирование, уменьшение задержек) сохраняются. Такой подход позволяет HAProxy как принимать, так и устанавливать соединения по HTTP/2 и H2C, оптимизируя трафик и обеспечивая совместимость с различными сценариями использования. Если вы настраиваете HTTP/2 (или H2C — HTTP/2 без TLS) на upstream, убедитесь, что ваш backend-сервер поддерживает этот протокол.

Stick Table

Stick Table — это встроенный механизм хранения состояний в HAProxy, представляющий собой внутреннюю базу данных, где балансировщик ведёт учёт трафика в разрезе определённых ключей — чаще всего IP-адресов, cookie или session ID. С его помощью можно реализовать ограничение скорости запросов (rate limiting), контроль числа одновременных соединений (connection tracking), автоматическую защиту от DoS/DDoS-атак, а также привязку сессий (stickiness) к определённым backend-серверам. Stick Table работает как на TCP, так и на HTTP-уровне, что делает его универсальным инструментом для управления трафиком.

Например, следующий конфиг создаёт таблицу с ключом по IP-адресу клиента, ограниченную одним миллионом записей, с временем жизни записи 10 секунд, в которой хранится статистика по скорости HTTP-запросов и установленных TCP-соединений за последние 10 секунд. В блоке frontend включено отслеживание IP-адреса клиента и добавлено правило: если он отправляет более 100 HTTP-запросов за 10 секунд, соединение немедленно отклоняется. Это простой, но эффективный способ защититься от сканеров, ботнетов и чрезмерно активных парсеров:

frontend ft_http
    bind *:80
    stick-table type ip size 1m expire 10s store http_req_rate(10s), conn_rate(10s)
    http-request track-sc0 src
    http-request deny if { sc_http_req_rate(0) gt 100 }

Stick-таблицы можно комбинировать с параметром maxconn, который задаёт ограничение на количество одновременных соединений: на глобальном уровне (global maxconn) — для всего процесса HAProxy, на уровне frontend — для входящих клиентских подключений, и на уровне server — для конкретного сервера в backend. При превышении лимита соединения ставятся в очередь, время ожидания которой определяется параметром timeout queue. Если за это время запрос не может быть обработан, клиент получает ошибку 503 Service Unavailable. Следует учитывать, что значение maxconn напрямую зависит от лимита файловых дескрипторов в системе (ulimit -n), поскольку каждое соединение использует один дескриптор.

global
    # Ограничение для всего процесса HAProxy
    maxconn 20000

    # Важно: нужно поднять лимит файловых дескрипторов в ОС
    # ulimit -n 40000

frontend http-in
    bind *:80

    # Лимит соединений на frontend
    maxconn 5000

    # Stick table для отслеживания скорости соединений с IP
    stick-table type ip size 100k expire 30s store conn_rate(10s)

    # Запоминаем IP клиента
    tcp-request connection track-sc0 src

    # Если клиент превысил 50 соединений за 10 секунд — блокируем на TCP уровне
    tcp-request connection reject if { sc_conn_rate(0) gt 50 }

    # Если хотим блокировать на HTTP-уровне (с кодом 403)
    # http-request deny if { sc_conn_rate(0) gt 50 }

    default_backend web-backend

backend web-backend
    # Пример backend с maxconn для каждого сервера
    balance roundrobin

    server web1 192.168.1.101:80 maxconn 2000 check
    server web2 192.168.1.102:80 maxconn 2000 check

Помимо описанного механизма, stick tables позволяют строить сложные ACL и автоматические правила блокировки, например мгновенно разрывать соединение на TCP-уровне с помощью tcp-request connection reject, отклонять HTTP-запросы до их обработки через http-request reject или блокировать клиентов по скорости соединений (sc_conn_rate). Благодаря гибкости и универсальности stick tables можно рассматривать как встроенный мини-файрвол в HAProxy, способный в реальном времени реагировать на аномалии в трафике.

Подробнее о механизме перегрузки можно прочитать здесь.

Что касается stick-таблиц, всё не так просто и однозначно. Эта тема довольно обширная и не ограничивается парой примеров — существует множество вариантов хранения и ведения key-value таблиц, тонкостей их настройки и способов применения.

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

Timeouts

В конфигурации HAProxy директивы timeout используются для определения различных предельных временных интервалов, влияющих на установление соединений, передачу запросов и откликов, а также поведение keep-alive-соединений. Все таймауты задаются в секундах, миллисекундах или комбинированном формате (10s, 500ms, 1m), и их можно указывать как в секции defaults, так и в секциях frontend или backend — приоритет отдается более локальной настройке. Таймауты — очень важные настройки. Они помогают избежать долгих висящих TCP-соединений, которые могут занимать дескрипторы, и позволяют использовать эти ресурсы для других соединений.

  1. timeout connect — максимальное время, в течение которого HAProxy будет ждать установления TCP-соединения с backend-сервером. Если сервер не отвечает или не принимает соединение в течение заданного времени, запрос считается неуспешным. Этот таймаут особенно важен при наличии "медленных" или перегруженных серверов. В современных сетях данный таймаут можно устанавливать очень маленьким в пределах 500msдля одного ЦОДа.

  2. timeout client — таймаут бездействия от клиента. Если клиент не отправляет никаких данных в течение указанного времени, соединение закрывается. Это защищает от "зависших" клиентов и атак типа slowloris.

  3. timeout server — аналогично timeout client, но применяется к соединению между HAProxy и backend-сервером. Если backend не отвечает или завис, соединение завершится по истечении заданного времени.

  4. timeout http-request — максимальное время ожидания полного HTTP-запроса от клиента. Если клиент не отправил заголовки целиком за этот период, соединение закрывается. Это важная защита от атак с медленной отправкой заголовков.

  5. timeout http-keep-alive — максимальное время, в течение которого HAProxy будет держать открытое keep-alive-соединение от клиента, ожидая новый HTTP-запрос. Если за это время запрос не поступает — соединение закрывается. Используется в HTTP/1.1 persistent connections.

  6. timeout queue — максимальное время, которое запрос может провести в очереди backend-пула, ожидая свободного сервера. Если все сервера заняты, и клиент ждет ответа дольше указанного времени — запрос прерывается. Это полезно для защиты от перегрузки и вывода соответствующего статуса клиенту (например, 503).

  7. timeout tarpit — время ожидания при использовании механизма tarpitting (например, при блокировке клиентов). Это может применяться для создания искусственной задержки на подозрительных запросах.

  8. timeout tunnel — таймаут неактивности в туннелируемых соединениях, например при TCP proxy или WebSocket. Используется для управления временем простоя в туннеле без активности.

  9. timeout client-fin и timeout server-fin — таймаут ожидания завершения TCP-соединения после получения FIN от клиента или сервера соответственно. Обычно не требуется настраивать явно, но в случае TCP-режима могут пригодиться.

  10. timeout check — таймаут для health-check соединений с backend-серверами. Определяет, сколько времени HAProxy будет ждать отклика на активный health-check. Если время истекло — сервер считается недоступным.

defaults
  timeout connect 500ms
  timeout client 30s
  timeout server 30s
  timeout http-request 3s
  timeout http-keep-alive 10s
  timeout queue 10s
  timeout tarpit 10s
  timeout tunnel 30m
  timeout client-fin 10s
  timeout server-fin 10s
  timeout check 3s

Данные таймауты можно взять, как минимальная отправная точка, а дальше уже крутить по своему усмотрению. Важно понимать, что слишком короткие таймауты приведут к обрывам соединений и ошибкам у клиентов, а слишком длинные — увеличат нагрузку на HAProxy из-за большого количества "висящих" соединений. В продакшн-среде таймауты необходимо тщательно подбирать, ориентируясь на реальное поведение клиентов, latency backend-сервисов и допустимый уровень отказов.

Также стоит учитывать, что в режиме TCP (mode tcp) будут учитываться только timeout connect, timeout client, timeout server, timeout tunnel и timeout check, в то время как HTTP-специфичные параметры (например, timeout http-request) применяются только в mode http.

Health check

Health-check’и исключают нерабочие сервера из пула до того, как на них начнёт идти трафик. В HAProxy 3.2.3 улучшены проверки по TCP, HTTP, TLS и кастомным сценариям. Таймауты в чекерах обязательны: если узел долго отвечает — он считается "плохим", и на него не шлются новые запросы. Это предотвращает задержки и снижает количество ошибок на стороне клиента.

Приведу несколько примеров:

backend tcp_check_send_expect
    mode tcp
    option tcp-check
    tcp-check connect
    tcp-check send "PING\r\n"
    tcp-check expect string "PONG"
    server srv1 10.0.0.21:6379 check inter 3s fall 3 rise 2

backend tcp_check_code
    mode tcp
    option tcp-check
    tcp-check connect
    tcp-check send "HELLO\r\n"
    tcp-check expect binary 0x4F4B
    server srv2 10.0.0.22:9000 check inter 2s fall 2 rise 1

backend tcp_check_regex
    mode tcp
    option tcp-check
    tcp-check connect
    tcp-check send "STATUS\r\n"
    tcp-check expect rstring "READY.*OK"
    server srv3 10.0.0.23:7000 check inter 4s fall 3 rise 2

backend tcp_check_with_timeout
    mode tcp
    option tcp-check
    timeout check 1s
    tcp-check connect
    tcp-check send "HEALTH\r\n"
    tcp-check expect string "ALIVE"
    server srv4 10.0.0.24:1883 check inter 3s fall 2 rise 1

backend tcp_check_negate
    mode tcp
    option tcp-check
    tcp-check connect
    tcp-check send "CHECK\r\n"
    tcp-check expect ! string "FAIL"
    server srv5 10.0.0.25:5000 check inter 2s fall 2 rise 1

backend tcp_check_no_send
    mode tcp
    option tcp-check
    tcp-check connect
    tcp-check expect rstring "^OK$"
    server srv6 10.0.0.26:6000 check inter 5s fall 3 rise 1

Что здесь происходит: в tcp-check HAProxy сам устанавливает TCP-соединение и может посылать произвольные байты (send), ожидая определённый ответ (expect). В expect string "PONG" ищется ASCII-строка PONG в ответе. В expect binary 0x4F4B проверяются первые два байта ответа на совпадение с кодом OK. В expect rstring "READY.*OK" используется регулярное выражение для анализа тела ответа. В timeout check 1s ограничено время проверки. В ! string "FAIL" сервер считается здоровым, если строка FAIL не найдена. В примере без send HAProxy просто ждёт приветственный баннер от сервера и проверяет его. Все проверки активные: HAProxy открывает TCP-соединение, опрашивает сервис и помечает его UP/DOWN по заданным условиям (fall — сколько подряд ошибок для признания DOWN, rise — сколько успешных ответов для восстановления, inter — интервал проверок).

backend app_http_check_string
    mode http
    option httpchk GET /health HTTP/1.1\r\nHost:\ example.com
    http-check expect string "OK"
    server srv1 10.0.0.11:80 check inter 3s fall 3 rise 2

backend app_http_check_code
    mode http
    option httpchk GET /status
    http-check expect status 200
    server srv2 10.0.0.12:80 check inter 2s fall 2 rise 1

backend app_http_check_multi_code
    mode http
    option httpchk GET /alive
    http-check expect rstatus ^2(00|04)$
    server srv3 10.0.0.13:8080 check inter 4s fall 3 rise 2

backend app_http_check_body_regex
    mode http
    option httpchk GET /healthz HTTP/1.1\r\nHost:\ internal
    http-check expect rstring "HEALTHY.*READY"
    server srv4 10.0.0.14:80 check inter 5s fall 3 rise 1

backend app_http_check_timeout
    mode http
    option httpchk GET /health
    timeout check 1s
    http-check expect status 200
    server srv5 10.0.0.15:8080 check inter 3s fall 2 rise 1

backend app_http_check_optional
    mode http
    option httpchk GET /ping
    http-check disable-on-404
    http-check expect status 200
    server srv6 10.0.0.16:80 check inter 4s fall 2 rise 1

backend app_http_check_negate
    mode http
    option httpchk GET /check
    http-check expect ! string "FAIL"
    server srv7 10.0.0.17:80 check inter 2s fall 2 rise 1

В этом примере: http-check expect string "OK" проверяется наличие строки "OK" в теле ответа, сервер считается здоровым только если она найдена. В expect status 200 проверяется ровно код ответа 200. В rstatus ^2(00|04)$ принимаются коды 200 или 204 через регулярное выражение. В rstring "HEALTHY.*READY" проверяется тело ответа по regex. В timeout check 1s задаётся ограничение на время health-чека. В disable-on-404 сервер не считается «плохим», если отвечает 404. В ! string "FAIL" используется отрицательная проверка — сервер признаётся здоровым, если строка FAIL не найдена. Все проверки выполняются активными health-чеками: HAProxy сам шлёт HTTP-запросы к каждому серверу и помечает его UP/DOWN в зависимости от совпадения условий

Но, это мы рассмотрели активные health checks — они работают по принципу периодической проверки состояния backend. Есть также и пассивные механизмы, которые реагируют на реальные ошибки во время обработки трафика. В версии 3.2.3 появилась улучшенная поддержка circuit breaker — инструментов, позволяющих автоматически изолировать отказавшие или перегруженные узлы, предотвращая лавинообразную деградацию всей системы.

В документации также приведен более сложный пример реализации «размыкателя цепи» (Circuit Breaker), где используются таблицы, счетчики и условия для временного исключения бекенда из пула. Сейчас рассмотрим более простые для понимания примеры:

backend tcp_backend
    mode tcp
    option tcp-check
    server db1 10.0.0.10:5432 check on-error mark-down observe layer4
    server db2 10.0.0.11:5432 check on-error mark-down observe layer4

Здесь backend работает в TCP-режиме (например, проксирование PostgreSQL или Redis). Активный TCP health check (tcp-check) проверяет порт, но дополнительный механизм on-error mark-down observe layer4 позволяет HAProxy пометить сервер как недоступный при ошибках установления соединения в реальном трафике — например, при таймаутах или TCP reset. Это снижает время реакции по сравнению с периодическими проверками и реализует пассивный circuit breaker на уровне транспорта.

backend api_backend
    mode http
    option httpchk GET /health
    stick-table type ip size 100k expire 30s store gpc0,conn_rate(10s),http_req_rate(10s),http_err_rate(10s)
    http-request track-sc0 src
    http-request deny if { sc_http_err_rate(api_backend) gt 10 }
    server api1 10.0.0.1:8080 check on-error mark-down observe layer7
    server api2 10.0.0.2:8080 check on-error mark-down observe layer7

Здесь backend обслуживает HTTP-запросы. В stick-table собирается статистика по IP-адресам: частота соединений, запросов и количество HTTP-ошибок. Если с одного IP за 10 секунд приходит более 10 ошибок (например, 5xx), срабатывает http-request deny — это circuit breaker на прикладном уровне, отключающий проблемного клиента. Также используется observe layer7, чтобы исключать backends при высоком уровне HTTP-ошибок в ответах. Это даёт более гибкий и точный контроль отказов в сложных API-сценариях.

И напоследок еще пару интересных встроенных health-check'ов для MySQL, PostgreSQL, Redis, SMTP и LDAP:

backend mysql_check
    mode tcp
    option mysql-check user root post-41
    server db1 10.0.0.31:3306 check inter 3s fall 3 rise 2

backend pgsql_check
    mode tcp
    option pgsql-check user haproxy
    server db2 10.0.0.32:5432 check inter 3s fall 3 rise 2

backend redis_check
    mode tcp
    option redis-check
    server cache1 10.0.0.33:6379 check inter 2s fall 2 rise 1

backend smtp_check
    mode tcp
    option smtpchk EHLO haproxy.local
    server mail1 10.0.0.34:25 check inter 5s fall 3 rise 1

backend ldap_check
    mode tcp
    option ldap-check
    server ldap1 10.0.0.35:389 check inter 4s fall 3 rise 2

В новых релизах имеются встроенные готовые проверки для популярных протоколов, которые не требуют ручного tcp-check send/expect. Для MySQL можно использовать option mysql-check user root — HAProxy сам выполняет handshake и проверяет, что сервер отвечает корректно (если добавить пароль — option mysql-check user root post-41 с параметрами). Для PostgreSQL добавили option pgsql-check user haproxy — выполняется начальное подключение с проверкой статуса базы. Для Redis есть option redis-check — HAProxy сам отправляет PING и ждёт PONG. Для SMTP применяется option smtpchk (при необходимости с EHLO: option smtpchk EHLO haproxy.local), сервер проверяется по баннеру и ответу на команду. Для LDAP доступна option ldap-check — HAProxy инициирует bind-запрос и валидирует ответ каталога. Эти встроенные функции значительно упрощают конфигурацию: не нужно вручную описывать tcp-check send/expect, так как логика проверки зашита внутри HAProxy и корректно обрабатывает нюансы протоколов, включая таймауты, коды и форматы ответов.

Retries

Также в версии 3.2.3 директивы retries, option redispatch и retry-on обеспечивают гибкое управление повторными попытками при сбоях, повышая отказоустойчивость. В разделе defaults задаются глобальные параметры: retries устанавливает число попыток при неудаче соединения, а option redispatch разрешает перенаправление запроса на другой backend, если первоначально выбранный сервер недоступен. Для TCP-бэкенда (L4) повторы возможны лишь до начала передачи данных — например, при conn-failure или empty-response — и применимы только для ошибок на этапе установления соединения. В HTTP-бэкенде (L7) механизм более тонкий: retry-on позволяет указать условия повторов при HTTP-ошибках, включая 5xx-ответы, сбои шлюза (502, 503, 504) и др., а также учитывать, какие HTTP-методы допустимы к повтору. Чтобы избежать дублирования при исполнении запросов, меняющих состояние (POST, PUT, DELETE), используется http-request disable-l7-retry if METH_POST METH_PUT METH_DELETE.

defaults
    retries 4
    option redispatch

backend dynamic_backend
    mode http
    retries 3
    retry-on 500 502 503 504 conn-failure 0rtt-rejected
    http-request disable-l7-retry if METH_POST METH_PUT METH_DELETE
    server app1 10.1.0.1:8080 check
    server app2 10.1.0.2:8080 check

backend analytics_tcp
    mode tcp
    tcp-request content set-retries 10 if { nbsrv(analytics_tcp) 1 }
    retry-on conn-failure empty-response
    server db1 10.2.0.1:5432 check
    server db2 10.2.0.2:5432 check

backend mixed_l7_l4
    mode http
    retry-on 408 425 conn-failure
    http-request set-retries 10 if { nbsrv(mixed_l7_l4) 1 }
    http-request disable-l7-retry if METH_POST
    server mix1 10.3.0.1:8000 check
    server mix2 10.3.0.2:8000 check

В секции defaults задаются общие параметры, которые наследуются всеми frontends и backends, если они не переопределены локально. Здесь retries 4 означает, что при недоступности сервера HAProxy будет пробовать перенаправить запрос до четырёх раз. Директива option redispatch разрешает повторно отправлять запрос на другой сервер в пуле, если выбранный ранее сервер стал недоступен.

Backend dynamic_backend работает в режиме HTTP (mode http). Здесь retries 3 локально переопределяет общее значение из defaults. Параметр retry-on определяет список ошибок (500, 502, 503, 504, сбой соединения и отказ 0-RTT), при которых возможна повторная отправка запроса. Однако, для методов POST, PUT и DELETE (http-request disable-l7-retry if METH_POST METH_PUT METH_DELETE) повторные попытки уровня L7 запрещены, чтобы избежать дублирования операций, изменяющих данные.

Backend analytics_tcp работает в TCP-режиме (mode tcp). Здесь retries 2 означает две попытки повторного соединения. Инструкция tcp-request content set-retries 10 if { nbsrv(analytics_tcp) 1 } увеличивает число попыток до 10, если в группе analytics_tcp остался только один активный сервер. Параметр retry-on conn-failure empty-response задаёт условия повторного соединения при обрыве или пустом ответе.

Backend mixed_l7_l4 — пример смешанной логики, когда используется HTTP-режим, но ошибки обрабатываются частично как на L7, так и на L4. Здесь retry-on 408 425 conn-failure определяет список HTTP-кодов и ошибок соединения для повторной попытки. Инструкция http-request set-retries 10 if { nbsrv(webservers) 1 } аналогична TCP-варианту, но применяется к HTTP. Директива http-request disable-l7-retry if METH_POST запрещает L7-повторы для POST-запросов.

Также полный статус кодов для разных уровней:

  • TCP: conn-failure, empty-response, junk-response, response-timeout, 0rtt-rejected или все вместе all-retryable-errors

  • HTTP: 404, 408, 425, 500, 501, 502, 503, 504

С полным списком и более подробно можно ознакомится здесь

Traffic Shaping

Traffic shaping — это механизм управления пропускной способностью, позволяющий замедлять передачу HTTP‑запросов и ответов при превышении заданных лимитов, а не отбрасывать трафик, как это происходит при traffic policing. Он действует на уровне HTTP, эффективно разглаживая нагрузку и предотвращая сетевой перегруз, при этом сохраняя качество обслуживания сервисов, где важно не допустить лавинообразного роста соединений.

В HAProxy traffic shaping реализован посредством фильтров bandwidth limitation filter. Применимы разные фильтры для загрузки (upload, входящий трафик) и выгрузки (download, исходящий трафик):

Upload (входящий трафик):

frontend http-in
    filter bwlim-in default-limit 62500 default-period 1s
    http-request set-bandwidth-limit

Download (исходящий трафик):

backend http-out
    filter bwlim-out default-limit 62500 default-period 1s
    http-response set-bandwidth-limit

Здесь default-limit задаёт объём данных (в байтах) за период default-period — в примере 62 500 B/s, что примерно равно 5 Mbps

HTTP/3 (QUIC)

Стоит также вкратце упомянуть HTTP/3 и протокол QUIC, лежащий в его основе. Не углубляясь в технические детали, скажу лишь, что эта технология уже существует, и её можно использовать в экспериментальном режиме.

Интересный факт: если проанализировать сетевые запросы в браузере, можно заметить, что большинство сервисов Google уже работают по протоколу QUIC. Тем не менее, его общее распространение пока невелико. На данный момент готовой поддержки «из коробки» или в популярных Docker-образах нет. Поэтому, если вы захотите опробовать HTTP/3 на своих проектах, придётся собирать бинарный файл вручную.

В версии 3.2.3 также представлена экспериментальная поддержка HTTP/3. Это позволяет обрабатывать клиентские запросы и значительно снижать задержки за счёт усовершенствованной мультиплексии и устранения необходимости в отдельных рукопожатиях (handshake) для TCP и TLS, поскольку QUIC объединяет в себе транспортный и криптографический (TLS) уровни.

frontend fe
  mode http
  bind :80
  bind :443  ssl crt /etc/haproxy/certs/foo.com/cert.crt alpn h2
  bind quic4@:443 ssl crt /mycert.pem alpn h3

  http-request redirect scheme https unless { ssl_fc }

  http-after-response add-header alt-svc 'h3=":443"; ma=60'

В такой конфигурации доступны три слушателя. При первом подключении веб-браузер использует HTTP/1.1 на удалённом порту 80. HAProxy перенаправит браузер на порт 443 и переключится на протокол HTTP/2. Конечная точка QUIC будет обнаружена через объявление Alt-Svc заголовок. Затем новое QUIC-подключение на порту 443 проведёт обмен данными. В этом примере Alt-Svc помечен как действительный в течение 60 секунд. Как только это время истечёт, браузер вернётся к подключению по HTTP/2. Установка такого небольшого значения удобна при первоначальном тестировании и может быть увеличена, когда QUIC начнёт работать должным образом.


Версия HAProxy 3.2.3 подтверждает зрелость и технологическую гибкость продукта, способного удовлетворить строгие требования к высокой доступности, отказоустойчивости и производительности в современных распределённых инфраструктурах. В ней реализован широкий набор механизмов, формирующих надёжную маршрутизацию и балансировку трафика на уровнях L4 и L7: ACL, Routing, Stick Table, Timeouts, Health Checks, Retries, Traffic Shaping, а также поддержка HTTP/3. ACL и маршрутизация позволяют строить сложную логику принятия решений на основе параметров запросов и сетевых характеристик, Stick Table обеспечивает stateful-логику, защиту от атак и ограничение частоты запросов, таймауты, активные и пассивные проверки состояния и повторные попытки повышают устойчивость соединений, а Traffic Shaping даёт возможность управлять пропускной способностью и приоритезацией трафика. Поддержка HTTP/3 открывает путь к обслуживанию современных веб-приложений с низкой задержкой и высокой скоростью доставки контента. Грамотно сконфигурированная комбинация этих возможностей позволяет сформировать отказоустойчивый, безопасный и предсказуемо работающий сервисный ландшафт, что особенно критично для масштабируемых и геораспределённых систем. HAProxy остаётся надёжным выбором для DevOps-инженеров и SRE-команд, стремящихся к максимальной управляемости сетевого трафика, прозрачности работы приложений и готовности к будущим протоколам передачи данных.

Комментарии (0)