· 16 мин чтения

HAProxy + Keepalived: отказоустойчивый балансировщик нагрузки для корпоративных сервисов

Семёнов Евгений Сергеевич, директор АйТи Фреш. HAProxy — мой любимый инструмент для балансировки. Лёгкий, быстрый, с отличной документацией, справляется с десятками тысяч соединений на скромном железе. Но сам HAProxy — точка отказа. Падает балансировщик — падает всё, что за ним. Поэтому в production я никогда не ставлю один HAProxy, только пару с Keepalived поверх. В этой статье покажу полный путь: от установки до переключения за 3 секунды при отказе активного узла.

Задача и архитектура

Представим задачу: три веб-сервера с Nginx и 1С веб-клиентом, трафик 500 req/sec в пиках, нужен один входной HTTPS-адрес 10.10.10.100. При падении одного веб-сервера трафик переходит на оставшиеся, при падении балансировщика — запасной подхватывает виртуальный IP за несколько секунд. Никаких разрывов пользовательских сессий.

Архитектура:

Я всегда беру лёгкие виртуалки под балансировщики: 2 vCPU / 2 ГБ RAM / 20 ГБ диска, Debian 12. HAProxy на таком железе тянет 20-30k req/sec без проблем.

Установка HAProxy и Keepalived

На обеих виртуалках ставим пакеты из официального репозитория Debian. Я использую HAProxy 2.8 LTS из backports — у неё лучшая поддержка HTTP/2 и TLS 1.3.

# На lb01 и lb02
sudo apt update
sudo apt install -y haproxy keepalived

# Включаем forwarding и non-local bind (нужно для VIP)
cat << EOF | sudo tee /etc/sysctl.d/99-haproxy.conf
net.ipv4.ip_nonlocal_bind = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system

Параметр ip_nonlocal_bind позволяет HAProxy слушать VIP даже на пассивной ноде — иначе бэкап-нода при поднятии адреса не сможет принять соединения.

Конфиг HAProxy

Один конфиг на обеих нодах, идентичный. Я всегда синхронизирую через rsync из git-репозитория при деплое.

# /etc/haproxy/haproxy.cfg
global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon
    maxconn 20000
    ssl-default-bind-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
    ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11

defaults
    log global
    mode http
    option httplog
    option dontlognull
    timeout connect 5s
    timeout client 30s
    timeout server 30s
    retries 3

frontend web_https
    bind *:80
    bind *:443 ssl crt /etc/haproxy/certs/portal.corp.ru.pem alpn h2,http/1.1
    http-request redirect scheme https unless { ssl_fc }
    http-request set-header X-Forwarded-Proto https
    default_backend web_pool

backend web_pool
    balance leastconn
    cookie SRVID insert indirect nocache
    option httpchk GET /health HTTP/1.1\r\nHost:\ portal.corp.ru
    http-check expect status 200
    server web01 10.10.10.201:443 check ssl verify none cookie w1
    server web02 10.10.10.202:443 check ssl verify none cookie w2
    server web03 10.10.10.203:443 check ssl verify none cookie w3

listen stats
    bind 127.0.0.1:9000
    stats enable
    stats uri /
    stats refresh 5s
    stats admin if TRUE

Важные моменты: balance leastconn — выбирает бэкенд с наименьшим числом соединений, лучше чем round-robin при длинных запросах. Cookie SRVID — sticky sessions, привязка клиента к бэкенду через cookie. Option httpchk с HTTP 1.1 — проверка живости бэкендов каждые 2 секунды.

Конфиг Keepalived на MASTER

Keepalived — это демон VRRP, который присваивает VIP одному из узлов. Конфиг на lb01 и lb02 отличается только ролью и приоритетом.

# /etc/keepalived/keepalived.conf на lb01 (MASTER)
global_defs {
    router_id LB01
    script_user root
    enable_script_security
}

vrrp_script chk_haproxy {
    script "/usr/bin/killall -0 haproxy"
    interval 2
    weight 2
    fall 2
    rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 150
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass ДлинныйПароль17Знаков!
    }
    virtual_ipaddress {
        10.10.10.100/24 dev eth0
    }
    track_script {
        chk_haproxy
    }
    notify_master "/etc/keepalived/notify.sh MASTER"
    notify_backup "/etc/keepalived/notify.sh BACKUP"
    notify_fault  "/etc/keepalived/notify.sh FAULT"
}

Конфиг Keepalived на BACKUP

# /etc/keepalived/keepalived.conf на lb02 (BACKUP)
global_defs {
    router_id LB02
    script_user root
    enable_script_security
}

vrrp_script chk_haproxy {
    script "/usr/bin/killall -0 haproxy"
    interval 2
    weight 2
    fall 2
    rise 2
}

vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass ДлинныйПароль17Знаков!
    }
    virtual_ipaddress {
        10.10.10.100/24 dev eth0
    }
    track_script {
        chk_haproxy
    }
}

Отличия: state BACKUP вместо MASTER, priority 100 вместо 150. Всё остальное идентично, особенно virtual_router_id и auth_pass — иначе ноды не увидят друг друга.

Скрипт уведомления о переключении

Когда происходит транзиция MASTER-BACKUP, я всегда отправляю алерт в Telegram — это критичное событие, которое нужно знать в реальном времени.

#!/bin/bash
# /etc/keepalived/notify.sh
TYPE=$1
BOT_TOKEN="1234567890:AAA..."
CHAT_ID="-1001234567890"
HOST=$(hostname)
TIME=$(date '+%Y-%m-%d %H:%M:%S')

curl -sSf -X POST "https://api.telegram.org/bot$BOT_TOKEN/sendMessage" \
  -d "chat_id=$CHAT_ID" \
  -d "text=HAProxy VRRP transition: $HOST → $TYPE at $TIME"

logger -t keepalived-notify "Transition to $TYPE"
sudo chmod +x /etc/keepalived/notify.sh
sudo systemctl enable --now keepalived haproxy
ip addr show eth0 | grep 10.10.10.100

Таблица: рекомендации по параметрам

ПараметрЗначениеОбоснование
advert_int1 секБыстрое обнаружение падения, 3 секунды до переключения
priority (MASTER)150Запас 50 пунктов над BACKUP для track_script weight
track_script weight+2 (positive)Если HAProxy жив — даёт бонус к приоритету
fall/rise2/2Не ведёт к флапам от разовых затыков
virtual_router_id1-255Уникальный в пределах L2-домена, иначе конфликт с другим кластером

Проверка отказоустойчивости

После запуска делаем целенаправленное тестирование, не на production. Я всегда проверяю три сценария:

  1. Падение HAProxy на MASTER: sudo systemctl stop haproxy на lb01 → через 4 секунды VIP переезжает на lb02, алерт в Telegram.
  2. Перезагрузка MASTER: sudo reboot на lb01 → VIP переезжает, через 2 минуты lb01 возвращается и забирает VIP обратно (если preemption on).
  3. Падение бэкенда: останавливаем nginx на web02 → HAProxy выводит из пула, трафик идёт на web01/web03 без потерь.

Кейс: HA-балансировщик для медицинской сети

В мае 2026 клиент — сеть из 9 клиник в Москве и области, единый портал записи на приём. До нас работал один HAProxy на виртуалке, раз в пару месяцев падал на час из-за разных причин. Запись в клиники закрывалась, пациенты жаловались.

Развернули пару lb01/lb02 на виртуалках в дата-центре МТС, настроили Keepalived с VIP 10.50.0.100, за ним шесть бэкендов с Nginx и PHP-приложением. Сертификат portal.clinic.ru, http/2 включён, gzip, статика кэшируется на балансировщике. Результаты за два месяца работы:

Стоимость проекта 110 000 руб за 3 рабочих дня, без железа.

Типичные ошибки

Развернём отказоустойчивый балансировщик под ключ

Настраиваем HAProxy + Keepalived для корпоративных веб-сервисов, 1С веб-клиента, SQL-прокси, почтовых серверов. Пара виртуалок, VIP, health-checks, SSL termination, алерты — за 2-3 рабочих дня.

Телефон: +7 903 729-62-41
Telegram: @ITfresh_Boss
Семёнов Евгений Сергеевич, директор АйТи Фреш

FAQ — частые вопросы по HAProxy и Keepalived

Зачем нужен Keepalived поверх HAProxy?
HAProxy балансирует трафик между бэкендами, но сам становится точкой отказа. Keepalived через VRRP присваивает виртуальный IP активному узлу и автоматически переключает его на резервный при падении.
Что такое VRRP?
Virtual Router Redundancy Protocol — протокол выбора мастера в группе роутеров. Один узел держит виртуальный IP, остальные следят за ним через multicast. Если мастер пропал — выбирается новый по приоритету.
Active-active или active-passive?
Active-passive проще в настройке и диагностике: один узел работает, второй ждёт. Active-active с двумя VIP и round-robin DNS даёт вдвое больше ёмкости, но сложнее в отладке и требует синхронизации сессий.
Нужен ли выделенный vlan для VRRP?
Не обязательно, но желательно. На одном vlan с боевым трафиком VRRP-пакеты могут потеряться при шторме. Я выношу управляющий трафик на отдельный интерфейс или vlan.
Как проверить, что VIP переключился?
На активном узле ip addr show покажет виртуальный IP на интерфейсе. На пассивном — его не будет. journalctl -u keepalived пишет все транзиции MASTER/BACKUP.

Подпишитесь на рассылку ITfresh

Раз в неделю — практические гайды для руководителя IT и сисадмина: безопасность, 1С, миграции, резервные копии, лайфхаки из реальных проектов.

Реквизиты оператора персональных данных

ООО «АЙТИ-ФРЕШ», ИНН 7719418495, КПП 771901001. Юридический адрес: 105523, г. Москва, Щёлковское шоссе, д. 92, корп. 7. Контакт: info@itfresh.ru, +7 903 729-62-41. Оператор обрабатывает e-mail подписчика в целях рассылки информационных и рекламных материалов до момента отзыва согласия.