500 000 посетителей и одна точка отказа: HAProxy + Keepalived для маркетплейса

Задача клиента: маркетплейс на грани отказа

Весной 2026 года к нам в АйТи Фреш обратился B2B-маркетплейс ОптТорг из Нижнего Новгорода — платформа для оптовой торговли промышленными товарами. На площадке было зарегистрировано более 12 000 поставщиков и 45 000 закупщиков. Среднемесячная посещаемость — 500 000 уникальных визитов, а в периоды тендерных кампаний нагрузка возрастала вдвое.

Вся архитектура держалась на одном сервере nginx, который одновременно выполнял роль reverse proxy, SSL termination и статик-сервера. За ним стояли три бэкенд-сервера с приложением на Django, но единственный nginx превратился в классический single point of failure.

«В ноябре nginx упал на 47 минут. Мы потеряли сделки на 2,3 миллиона рублей — закупщики ушли к конкурентам и больше не вернулись» — технический директор ОптТорг.

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

Аудит текущей инфраструктуры

Наши инженеры провели аудит и выявили следующие проблемы:

  • Single point of failure — один nginx-сервер без резервирования
  • Нет health checks — nginx продолжал направлять трафик на упавшие бэкенды
  • SSL на каждом бэкенде — сертификаты дублировались, обновлялись вручную
  • Нет sticky sessions — пользователи теряли сессию корзины при переключении между бэкендами
  • Ручной деплой — обновление приложения требовало остановки nginx и 2–5 минут простоя

Исходная топология выглядела так:

# Текущая архитектура (до внедрения)
Интернет → 1× nginx (proxy + SSL) → 3× Django backend
                                    → 1× PostgreSQL
                                    → 1× Redis (сессии)

# IP-адреса
nginx:    185.120.30.10 (единственная точка входа)
backend1: 10.0.1.11
backend2: 10.0.1.12
backend3: 10.0.1.13

Целевая архитектура решения

Мы спроектировали архитектуру с двумя уровнями отказоустойчивости:

# Целевая архитектура
Интернет → Floating IP (VRRP)
           ├── HAProxy MASTER (haproxy1) — активный
           └── HAProxy BACKUP (haproxy2) — горячий резерв
               ├── backend1 (10.0.1.11)
               ├── backend2 (10.0.1.12)
               └── backend3 (10.0.1.13)

Ключевые решения:

  • HAProxy вместо nginx — специализированный балансировщик с продвинутыми health checks, ACL и статистикой
  • Keepalived с протоколом VRRP — автоматическое переключение floating IP между двумя HAProxy за 2–3 секунды
  • SSL termination на уровне HAProxy — один сертификат, автоматическое обновление
  • Sticky sessions через cookies — пользователь привязывается к конкретному бэкенду

Установка и настройка HAProxy

HAProxy — это высокопроизводительный TCP/HTTP-балансировщик, который используется такими компаниями как GitHub, Stack Overflow и Reddit. Мы установили его на двух серверах с Ubuntu 22.04 LTS для обеспечения резервирования.

Установка HAProxy 2.9 из официального репозитория

На обоих серверах (haproxy1 и haproxy2) мы выполнили идентичную установку:

# Добавляем официальный PPA HAProxy (версия 2.9 LTS)
sudo apt-get update
sudo apt-get install -y software-properties-common
sudo add-apt-repository -y ppa:vbernat/haproxy-2.9
sudo apt-get update
sudo apt-get install -y haproxy=2.9.*

# Проверяем установленную версию
haproxy -v
# HAProxy version 2.9.7-1ppa1~jammy

# Включаем автозапуск
sudo systemctl enable haproxy

# Разрешаем HAProxy привязываться к нелокальным IP (для floating IP)
echo 'net.ipv4.ip_nonlocal_bind = 1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Параметр ip_nonlocal_bind критически важен — без него HAProxy не сможет слушать на floating IP, который ещё не назначен интерфейсу (на backup-ноде).

Конфигурация глобальных параметров и дефолтов

Конфигурацию мы хранили в /etc/haproxy/haproxy.cfg. Начнём с секций global и defaults:

# /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 50000
    nbthread 4

    # SSL: современные параметры
    ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
    ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
    tune.ssl.default-dh-param 2048

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-keep-alive 10s
    timeout check   5s
    errorfile 400 /etc/haproxy/errors/400.http
    errorfile 403 /etc/haproxy/errors/403.http
    errorfile 408 /etc/haproxy/errors/408.http
    errorfile 500 /etc/haproxy/errors/500.http
    errorfile 502 /etc/haproxy/errors/502.http
    errorfile 503 /etc/haproxy/errors/503.http
    errorfile 504 /etc/haproxy/errors/504.http

Обратите внимание на maxconn 50000 — мы рассчитали это значение исходя из пиковых 500K визитов в месяц (примерно 300 одновременных соединений в штатном режиме и до 3000 в пик).

Frontend: SSL termination и маршрутизация

Frontend принимает все входящие соединения, терминирует SSL и маршрутизирует запросы:

# Frontend — приём входящего трафика
frontend ft_web
    bind *:80
    bind *:443 ssl crt /etc/haproxy/certs/opttorg.ru.pem alpn h2,http/1.1

    # Редирект HTTP → HTTPS
    http-request redirect scheme https unless { ssl_fc }

    # Заголовки безопасности
    http-response set-header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
    http-response set-header X-Frame-Options SAMEORIGIN
    http-response set-header X-Content-Type-Options nosniff

    # Логирование реального IP
    http-request set-header X-Forwarded-Proto https if { ssl_fc }
    http-request set-header X-Real-IP %[src]

    # ACL для маршрутизации
    acl is_api path_beg /api/
    acl is_static path_beg /static/ /media/
    acl is_websocket hdr(Upgrade) -i WebSocket

    # Маршруты
    use_backend bk_api if is_api
    use_backend bk_static if is_static
    use_backend bk_websocket if is_websocket
    default_backend bk_web

# Страница статистики HAProxy (доступ только из внутренней сети)
frontend ft_stats
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 10s
    stats admin if TRUE
    acl is_internal src 10.0.0.0/8 172.16.0.0/12
    http-request deny unless is_internal

Для SSL мы подготовили единый PEM-файл, объединяющий сертификат и ключ:

# Подготовка SSL-сертификата для HAProxy
sudo mkdir -p /etc/haproxy/certs

# Объединяем сертификат, промежуточный CA и ключ в один файл
sudo cat /etc/letsencrypt/live/opttorg.ru/fullchain.pem \
        /etc/letsencrypt/live/opttorg.ru/privkey.pem \
        | sudo tee /etc/haproxy/certs/opttorg.ru.pem > /dev/null

sudo chmod 600 /etc/haproxy/certs/opttorg.ru.pem

Backend: health checks и sticky sessions

Самая важная часть конфигурации — бэкенды с продвинутыми проверками здоровья:

# Backend — основное веб-приложение
backend bk_web
    balance roundrobin
    option httpchk
    http-check send meth GET uri /health ver HTTP/1.1 hdr Host opttorg.ru
    http-check expect status 200

    # Sticky sessions через cookie
    cookie SERVERID insert indirect nocache httponly secure

    # Бэкенд-серверы с health checks
    server web1 10.0.1.11:8000 check inter 3s fall 3 rise 2 cookie web1 weight 100
    server web2 10.0.1.12:8000 check inter 3s fall 3 rise 2 cookie web2 weight 100
    server web3 10.0.1.13:8000 check inter 3s fall 3 rise 2 cookie web3 weight 100

    # Сервер-резерв для graceful degradation
    server backup 10.0.1.11:8000 check backup

# Backend — API (с более строгими таймаутами)
backend bk_api
    balance leastconn
    option httpchk
    http-check send meth GET uri /api/health ver HTTP/1.1 hdr Host opttorg.ru
    http-check expect status 200
    timeout server 60s
    retry-on conn-failure empty-response response-timeout
    retries 2

    server api1 10.0.1.11:8000 check inter 5s fall 2 rise 3
    server api2 10.0.1.12:8000 check inter 5s fall 2 rise 3
    server api3 10.0.1.13:8000 check inter 5s fall 2 rise 3

# Backend — статика (через nginx на бэкендах)
backend bk_static
    balance roundrobin
    option httpchk
    http-check send meth HEAD uri /static/healthcheck.txt ver HTTP/1.1
    http-check expect status 200
    timeout server 10s

    http-request set-header Cache-Control "public, max-age=86400"

    server static1 10.0.1.11:80 check inter 10s
    server static2 10.0.1.12:80 check inter 10s
    server static3 10.0.1.13:80 check inter 10s

# Backend — WebSocket
backend bk_websocket
    balance source
    timeout tunnel 3600s
    server ws1 10.0.1.11:8001 check inter 5s
    server ws2 10.0.1.12:8001 check inter 5s

Разберём ключевые параметры health checks:

  • inter 3s — проверка здоровья каждые 3 секунды
  • fall 3 — сервер считается упавшим после 3 неудачных проверок (9 секунд)
  • rise 2 — сервер возвращается после 2 успешных проверок (6 секунд)
  • cookie SERVERID — HAProxy ставит cookie с ID бэкенда, обеспечивая sticky sessions

Для API-бэкенда мы выбрали алгоритм leastconn вместо roundrobin, так как API-запросы имели разную длительность (от 50ms для каталога до 5s для генерации отчётов).

Keepalived: автоматический failover через VRRP

HAProxy сам по себе не обеспечивает отказоустойчивость балансировщика. Если единственный HAProxy упадёт, ситуация будет ещё хуже, чем с nginx — ведь за ним вся инфраструктура. Поэтому мы развернули два HAProxy-сервера и связали их через Keepalived с протоколом VRRP.

Принцип работы VRRP и floating IP

VRRP (Virtual Router Redundancy Protocol) — протокол, позволяющий нескольким серверам делить один виртуальный IP-адрес. В любой момент времени IP принадлежит только одному серверу (MASTER). Если MASTER перестаёт отправлять heartbeat-пакеты, BACKUP забирает IP себе за 2–3 секунды.

# Установка Keepalived на обоих серверах
sudo apt-get install -y keepalived

# Проверяем версию
keepalived --version
# Keepalived v2.2.8

В облаке провайдера клиента мы заказали дополнительный IP-адрес 185.120.30.100 — он станет floating IP, который DNS-записи opttorg.ru будут указывать.

Конфигурация Keepalived на MASTER-ноде

Конфигурация на первом сервере (haproxy1, 185.120.30.11):

# /etc/keepalived/keepalived.conf на haproxy1 (MASTER)

global_defs {
    router_id haproxy1
    script_user root
    enable_script_security
}

# Скрипт проверки HAProxy
vrrp_script check_haproxy {
    script "/usr/bin/killall -0 haproxy"
    interval 2
    weight -20
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 110
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass S3cureVRRP!key
    }

    virtual_ipaddress {
        185.120.30.100/24 dev eth0
    }

    track_script {
        check_haproxy
    }

    # Уведомление при смене состояния
    notify_master "/etc/keepalived/notify.sh MASTER"
    notify_backup "/etc/keepalived/notify.sh BACKUP"
    notify_fault  "/etc/keepalived/notify.sh FAULT"
}

Обратите внимание на скрипт check_haproxy: команда killall -0 haproxy проверяет, запущен ли процесс HAProxy. Если процесс мёртв, weight -20 снижает приоритет ноды на 20 пунктов, что приводит к переключению IP на BACKUP.

Конфигурация BACKUP-ноды и скрипт уведомлений

На втором сервере (haproxy2, 185.120.30.12) конфигурация почти идентична, но с другим приоритетом:

# /etc/keepalived/keepalived.conf на haproxy2 (BACKUP)

global_defs {
    router_id haproxy2
    script_user root
    enable_script_security
}

vrrp_script check_haproxy {
    script "/usr/bin/killall -0 haproxy"
    interval 2
    weight -20
    fall 3
    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 S3cureVRRP!key
    }

    virtual_ipaddress {
        185.120.30.100/24 dev eth0
    }

    track_script {
        check_haproxy
    }

    notify_master "/etc/keepalived/notify.sh MASTER"
    notify_backup "/etc/keepalived/notify.sh BACKUP"
    notify_fault  "/etc/keepalived/notify.sh FAULT"
}

Скрипт уведомлений отправляет алерт в Telegram при каждом переключении:

#!/bin/bash
# /etc/keepalived/notify.sh

STATE=$1
HOSTNAME=$(hostname)
DATE=$(date '+%Y-%m-%d %H:%M:%S')
BOT_TOKEN="YOUR_BOT_TOKEN"
CHAT_ID="YOUR_CHAT_ID"
MSG="⚠️ Keepalived: $HOSTNAME перешёл в состояние $STATE в $DATE"

curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
  -d chat_id="${CHAT_ID}" \
  -d text="${MSG}" \
  -d parse_mode="HTML" > /dev/null 2>&1

logger "keepalived: переход в состояние $STATE"
# Делаем скрипт исполняемым и запускаем Keepalived
sudo chmod +x /etc/keepalived/notify.sh
sudo systemctl enable keepalived
sudo systemctl start keepalived

# Проверяем, что floating IP появился на MASTER
ip addr show eth0 | grep 185.120.30.100
# inet 185.120.30.100/24 scope global secondary eth0

Тестирование failover

Мы провели серию тестов переключения прямо в присутствии клиента:

# Тест 1: Остановка HAProxy на MASTER
# На haproxy1:
sudo systemctl stop haproxy

# На haproxy2 через 3-6 секунд:
ip addr show eth0 | grep 185.120.30.100
# inet 185.120.30.100/24 scope global secondary eth0  ✓ IP переехал

# Тест 2: Полная остановка MASTER
# На haproxy1:
sudo systemctl stop keepalived

# Тест 3: Возврат MASTER (preemption)
# На haproxy1:
sudo systemctl start haproxy
sudo systemctl start keepalived
# IP возвращается на MASTER через 1-2 секунды

# Непрерывный мониторинг во время тестов
# С отдельной машины:
while true; do
  curl -s -o /dev/null -w "%{http_code} %{time_total}s\n" https://opttorg.ru/health
  sleep 0.5
done
# 200 0.043s
# 200 0.041s
# 000 0.000s   ← момент переключения (1-2 запроса)
# 200 0.052s
# 200 0.044s

Результат: переключение занимало 2–4 секунды, терялось не более 2–3 запросов. Для B2B-маркетплейса, где пользователи не обновляют страницу каждую секунду, это было приемлемо.

Мониторинг HAProxy и Keepalived

Балансировщик без мониторинга — как самолёт без приборной панели. Мы настроили несколько уровней наблюдения за кластером.

HAProxy Stats Page и Prometheus exporter

Встроенная страница статистики HAProxy доступна по порту 8404. Но для долгосрочного мониторинга мы развернули Prometheus exporter:

# Установка haproxy_exporter
wget https://github.com/prometheus/haproxy_exporter/releases/download/v0.15.0/haproxy_exporter-0.15.0.linux-amd64.tar.gz
tar xvf haproxy_exporter-0.15.0.linux-amd64.tar.gz
sudo mv haproxy_exporter-0.15.0.linux-amd64/haproxy_exporter /usr/local/bin/

# Systemd unit
sudo tee /etc/systemd/system/haproxy-exporter.service > /dev/null <<'EOF'
[Unit]
Description=HAProxy Exporter
After=network.target

[Service]
ExecStart=/usr/local/bin/haproxy_exporter \
  --haproxy.scrape-uri="unix:/run/haproxy/admin.sock"
Restart=always
User=haproxy

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now haproxy-exporter

# Проверяем метрики
curl -s http://localhost:9101/metrics | head -20

В Grafana мы создали дашборд с ключевыми метриками:

  • haproxy_frontend_current_sessions — текущие сессии
  • haproxy_backend_http_responses_total — HTTP-ответы по кодам (2xx, 4xx, 5xx)
  • haproxy_backend_response_time_average_seconds — средний response time
  • haproxy_server_status — статус каждого бэкенда (UP/DOWN)
  • haproxy_backend_retry_warnings_total — количество ретраев

Алерты в Prometheus и Telegram

Мы настроили алерты на критические ситуации:

# /etc/prometheus/rules/haproxy_alerts.yml
groups:
  - name: haproxy
    rules:
      - alert: HAProxyBackendDown
        expr: haproxy_server_status{state="UP"} == 0
        for: 30s
        labels:
          severity: critical
        annotations:
          summary: "Backend {{ $labels.server }} в {{ $labels.proxy }} недоступен"

      - alert: HAProxyHighErrorRate
        expr: >
          sum(rate(haproxy_backend_http_responses_total{code="5xx"}[5m]))
          /
          sum(rate(haproxy_backend_http_responses_total[5m])) > 0.05
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "Доля 5xx ошибок превышает 5% (текущее: {{ $value | humanizePercentage }})"

      - alert: HAProxyHighLatency
        expr: haproxy_backend_response_time_average_seconds > 2
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Средний response time бэкенда {{ $labels.proxy }} > 2s"

      - alert: KeepalivedFailover
        expr: changes(node_systemd_unit_state{name="keepalived.service",state="active"}[5m]) > 0
        labels:
          severity: critical
        annotations:
          summary: "Keepalived failover произошёл на {{ $labels.instance }}"

Zero-downtime deploy через HAProxy

Одной из ключевых проблем клиента был простой при деплое. Раньше обновление приложения на трёх серверах занимало 5–10 минут с полным отключением трафика. Мы реализовали rolling deploy через управление HAProxy.

Скрипт rolling deploy

Деплой происходил поочерёдно: сервер выводился из балансировки, обновлялся, проходил health check и возвращался в строй:

#!/bin/bash
# /opt/scripts/rolling-deploy.sh

SERVERS=("web1" "web2" "web3")
BACKEND="bk_web"
HAPROXY_SOCKET="/run/haproxy/admin.sock"
HEALTH_URL="http://localhost:8000/health"
APP_DIR="/opt/opttorg"

for server in "${SERVERS[@]}"; do
    echo "=== Деплой на $server ==="

    # 1. Выводим сервер из балансировки (drain mode)
    echo "disable server ${BACKEND}/${server}" | socat stdio "$HAPROXY_SOCKET"
    echo "[$(date)] $server выведен из балансировки"

    # 2. Ждём завершения текущих соединений (max 30 секунд)
    echo "set server ${BACKEND}/${server} state drain" | socat stdio "$HAPROXY_SOCKET"
    sleep 10

    # 3. Обновляем приложение через SSH
    ssh deploy@${server}.internal "cd $APP_DIR && \
        git pull origin main && \
        source venv/bin/activate && \
        pip install -r requirements.txt --quiet && \
        python manage.py migrate --noinput && \
        sudo systemctl restart gunicorn"

    # 4. Ждём, пока health check пройдёт
    echo "Ожидаем готовность $server..."
    for i in $(seq 1 30); do
        HTTP_CODE=$(ssh deploy@${server}.internal "curl -s -o /dev/null -w '%{http_code}' $HEALTH_URL")
        if [ "$HTTP_CODE" == "200" ]; then
            echo "$server готов (попытка $i)"
            break
        fi
        sleep 2
    done

    # 5. Возвращаем в балансировку
    echo "enable server ${BACKEND}/${server}" | socat stdio "$HAPROXY_SOCKET"
    echo "set server ${BACKEND}/${server} state ready" | socat stdio "$HAPROXY_SOCKET"
    echo "[$(date)] $server возвращён в балансировку"

    # 6. Пауза перед следующим сервером
    sleep 5
done

echo "=== Деплой завершён на всех серверах ==="

Обновление конфигурации HAProxy без простоя

HAProxy поддерживает горячую перезагрузку конфигурации через механизм -sf (seamless reload):

# Проверяем новую конфигурацию (обязательно перед reload!)
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
# Configuration file is valid

# Горячая перезагрузка без потери соединений
sudo systemctl reload haproxy

# Или через команду напрямую (для старых систем)
sudo haproxy -f /etc/haproxy/haproxy.cfg -sf $(cat /run/haproxy.pid)

# Проверяем, что перезагрузка прошла
sudo journalctl -u haproxy -n 5 --no-pager
# haproxy[4521]: Proxy ft_web started.
# haproxy[4521]: Proxy bk_web started.

При reload HAProxy запускает новый процесс, который принимает новые соединения, а старый процесс дообрабатывает существующие — ни одно соединение не теряется.

Оптимизация производительности и безопасность

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

Rate limiting и защита от DDoS

HAProxy позволяет реализовать rate limiting без внешних модулей:

# Добавляем в frontend ft_web
frontend ft_web
    # ... (предыдущая конфигурация)

    # Stick table для подсчёта запросов по IP
    stick-table type ip size 200k expire 30s store http_req_rate(10s),conn_cur

    # Отслеживаем запросы
    http-request track-sc0 src

    # Лимит: 100 запросов за 10 секунд с одного IP
    http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 }

    # Лимит одновременных соединений: 50 с одного IP
    http-request deny deny_status 429 if { src_conn_cur ge 50 }

    # Тарпит для подозрительных запросов (замедление ответа)
    http-request tarpit if { sc_http_req_rate(0) gt 200 }
    timeout tarpit 5s

Оптимизация системных параметров ядра

Для обработки большого количества соединений мы настроили параметры ядра Linux:

# /etc/sysctl.d/99-haproxy.conf

# Увеличиваем лимиты TCP-соединений
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 65535

# Быстрая утилизация TIME_WAIT соединений
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15

# Расширяем диапазон эфемерных портов
net.ipv4.ip_local_port_range = 1024 65535

# Увеличиваем буферы
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 87380 16777216

# Keep-alive
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 60
net.ipv4.tcp_keepalive_probes = 3
# Применяем параметры
sudo sysctl --system

# Увеличиваем лимит файловых дескрипторов для HAProxy
sudo tee /etc/security/limits.d/haproxy.conf > /dev/null <<'EOF'
haproxy  soft  nofile  131072
haproxy  hard  nofile  131072
EOF

# Дополнительно в systemd unit
sudo mkdir -p /etc/systemd/system/haproxy.service.d
sudo tee /etc/systemd/system/haproxy.service.d/limits.conf > /dev/null <<'EOF'
[Service]
LimitNOFILE=131072
EOF
sudo systemctl daemon-reload
sudo systemctl restart haproxy

Результаты внедрения

Проект был завершён за 5 рабочих дней. Вот что изменилось для маркетплейса ОптТорг после внедрения HAProxy и Keepalived специалистами АйТи Фреш:

МетрикаДо внедренияПосле внедрения
Uptime за квартал99.2% (3 инцидента)99.98% (0 инцидентов)
Время failover5–47 минут (ручное)2–4 секунды (автоматическое)
Время деплоя5–10 минут с даунтаймом3 минуты без даунтайма
Средний response time380 мс210 мс (балансировка нагрузки)
Потерянные запросы при отказеВсе (до ручного восстановления)2–3 запроса за 2 секунды
Мониторинг бэкендовОтсутствовалReal-time в Grafana + алерты

Через месяц после внедрения произошёл реальный инцидент — на MASTER-сервере вышел из строя сетевой интерфейс. Keepalived переключил floating IP на BACKUP за 3 секунды. Клиент узнал об инциденте только из Telegram-уведомления — пользователи ничего не заметили.

«Раньше каждый деплой — это стресс. Сейчас мы деплоим по три раза в день и спим спокойно. HAProxy + Keepalived — лучшая инвестиция за год» — CTO ОптТорг.

Часто задаваемые вопросы

HAProxy — специализированный балансировщик с продвинутыми health checks (HTTP-запросы с проверкой кода ответа), stick tables для rate limiting, детальной статистикой по каждому бэкенду и поддержкой протокола PROXY. Nginx лучше для статики и reverse proxy простых конфигураций. Для mission-critical балансировки с требованием мгновенного failover и zero-downtime deploy HAProxy предпочтительнее.

HAProxy легко обслуживает сотни бэкендов. В нашей практике максимальная конфигурация включала 120 бэкенд-серверов. Ограничением обычно является не HAProxy, а пропускная способность сети и количество файловых дескрипторов. С настройкой maxconn 50000 и увеличенным лимитом nofile один HAProxy обрабатывает до 100 000 одновременных соединений на обычном 4-ядерном сервере.

Это крайне маловероятный сценарий, но его надо предусмотреть. Мы рекомендуем: 1) размещать HAProxy-серверы в разных стойках или датацентрах; 2) настроить внешний мониторинг (UptimeRobot, Pingdom), который оповестит даже при двойном отказе; 3) для критичных систем добавить третий HAProxy с приоритетом 90 в другом ЦОД. В проекте ОптТорг серверы были в разных стойках одного ЦОД.

HAProxy поддерживает горячее обновление сертификатов через runtime API: echo "set ssl cert /etc/haproxy/certs/domain.pem <<EOF\n$(cat new.pem)\nEOF" | socat stdio /run/haproxy/admin.sock, затем echo "commit ssl cert /etc/haproxy/certs/domain.pem" | socat stdio /run/haproxy/admin.sock. Альтернативно — systemctl reload haproxy, который тоже не прерывает существующие соединения.

В облаке VRRP обычно заблокирован на уровне сети, поэтому классический Keepalived не работает. Вместо него используются облачные балансировщики (ALB, NLB) или API облака для переключения Elastic IP. В Yandex Cloud мы используем Network Load Balancer перед двумя HAProxy — он выполняет роль Keepalived. Для on-premise и colocation Keepalived остаётся лучшим решением.

Нужна помощь с настройкой?

Специалисты АйТи Фреш помогут с внедрением и настройкой — 15+ лет опыта, обслуживание от 15 000 ₽/мес

📞 Связаться с нами
#HAProxy настройка#Keepalived VRRP#балансировка нагрузки#high availability Linux#floating IP failover#SSL termination HAProxy#sticky sessions#zero-downtime deploy