Перейти к основному содержанию
`Ctrl`+`K`

# Настройка HAProxy (standalone)

## Общая информация HAProxy

HAProxy — это высокопроизводительный прокси-сервер и балансировщик нагрузки для протоколов TCP (L4) и HTTP/HTTPS ( L7). Он используется для:

- распределения трафика между несколькими приложениями (балансировка нагрузки);

- проверки работоспособности целевых серверов (health checks);

- добавления служебных заголовков (например, X-Forwarded-For, X-Forwarded-Proto);

- ограничения скорости, защиты от атак и пр.

В режиме `standalone HAProxy` работает на одном сервере и проксирует трафик к локальным или удалённым backend-службам, повышая отказоустойчивость и безопасность.

## Перед началом работ

- Получите права администратора (root) или возможность использовать sudo.

- Запустите приложение на порту 8080.

## Подготовка системы и брандмауэра (UFW)

### Обновление индексов пакетов и базовой системы

    sudo apt update && sudo apt upgrade

### Установка и включение Firewall

    sudo apt install ufw
    sudo systemctl enable ufw --now

### Базовая политика firewall

    sudo ufw default deny incoming
    sudo ufw allow ssh
    sudo ufw enable

- `default deny incoming` - по умолчанию запрещаем все входящие подключения (подход «deny by default»).

- `allow ssh` - разрешаем доступ по SSH (обычно порт 22), чтобы не потерять удалённый доступ.

- `enable` - активируем правила файрвола.

### Разрешение нужных портов

    sudo ufw allow 443/tcp
    sudo ufw allow from 127.0.0.1 to any port 8080
    sudo ufw allow 5000/tcp
    sudo ufw allow 8405/tcp

- 443/tcp - входящий HTTPS-трафик к HAProxy.

- Разрешение доступа к локальному backend на 8080 (только с 127.0.0.1).

- 5000/tcp - входящий трафик для статистики HAProxy (включить при необходимости).

- 8405/tcp - входящий трафик для статистики Prometheus (включить при необходимости).

Важно

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

### Проверка правил

    sudo ufw status verbose

## Установка HAProxy

    sudo apt install haproxy -y
    sudo systemctl enable haproxy --now

Инструкции для других версий/ОС размещены на официальном сайте HAProxy для Debian: https://haproxy.debian.net/.

## TLS/SSL: самоподписанный сертификат для теста

Важно

В продакшене используйте сертификаты от доверенного CA. Самоподписанный сертификат подходит для тестов/внутренних стендов.

    openssl genrsa -out cert.key 2048
    openssl req -new -key cert.key -out cert.csr
    openssl x509 -req -days 365 -in cert.csr -signkey cert.key -out cert.crt
    mkdir /etc/ssl/globalerp/
    cat cert.key cert.crt > /etc/ssl/globalerp/haproxy_ssl.pem

- Генерируем приватный ключ RSA 2048 бит.

- Формируем CSR.

- Выпускаем самоподписанный сертификат на 365 дней.

- Сливаем ключ и сертификат в единый PEM-файл haproxy_ssl.pem, который удобно указывать в haproxy.cfg.

Рекомендуемое расположение PEM: `/etc/ssl/globalerp/haproxy_ssl.pem`. Права: `chmod 640`, владелец/группа: `haproxy:haproxy`, чтобы процесс `haproxy` мог читать файл.

## Базовая конфигурация HAProxy

Откройте конфигурацию:

    sudo nano /etc/haproxy/haproxy.cfg

Пример конфигурации

    global
            # Логи отправляются в системный syslog (rsyslog/journald)
            log /dev/log    local0 info
            log /dev/log    local1 notice
            chroot /var/lib/haproxy
            stats socket /run/haproxy/admin.sock mode 660 level admin
            stats timeout 300s
            user haproxy
            group haproxy
            daemon

            # Default SSL material locations
            ca-base /etc/ssl/certs
            crt-base /etc/ssl/private

            # See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
            ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-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

    frontend stats
            mode http
            bind :5000
            stats enable
            stats refresh 10s
            stats uri /
            stats show-modules
            stats hide-version
            stats admin if TRUE
            stats auth <Login>:<Password>
            timeout client 10s

    defaults
            log     global
            mode    http
            option  httplog
            option  dontlognull
            option  log-health-checks
            option  abortonclose
            timeout connect 5000
            timeout client  50000
            timeout server  50000
            # Кастомные страницы ошибок
            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

    frontend haproxynode
            bind *:80
            redirect scheme https code 301 if !{ ssl_fc }
            bind *:443 ssl crt /etc/ssl/release-info.dev.local/release-info.dev.local.pem
            mode http
            default_backend backendnodes

    backend backendnodes
            balance leastconn #roundrobin
            option forwardfor # Включает X-Forwarded-For для передачи IP-адреса клиента обслуживающему серверу.
            cookie GS_BALANCER_SERVER_NAME insert #  добавлять куки. Для режима липких сессий, когда один ip подключается к одному backend-ду

            acl has_x_forwarded_port req.hdr(X-Forwarded-Port) -m found
            http-request set-header X-Forwarded-Port %[req.hdr(X-Forwarded-Port)] if has_x_forwarded_port
            http-request set-header X-Forwarded-Port %[dst_port] unless has_x_forwarded_port
            http-request add-header X-Forwarded-Proto https if { ssl_fc }
            option httpchk HEAD / HTTP/1.1\r\nHost:localhost

            server global-server-01 localhost:8080 check cookie global-server-01

## Разбор конфигурации

### Уровни логирования

Строки `log /dev/log local0 info` и `log /dev/log local1 notice` задают отправку логов в системный syslog с различным уровнем. Это позволит разнести рабочие логи и, например, административные сообщения.

    global
            log /dev/log    local0 info
            log /dev/log    local1 notice
            chroot /var/lib/haproxy
            stats socket /run/haproxy/admin.sock mode 660 level admin
            stats timeout 300s
            user haproxy
            group haproxy
            daemon

Уровень лога | Описание
--- | ---
emerg | Ошибки, такие как исчерпание файловых дескрипторов операционной системы.
alert | Некоторые редкие случаи непредвиденных событий, например, невозможность кэширования ответа.
err | Ошибки, такие как невозможность анализа файла карты, невозможность анализа файла конфигурации HAProxy и сбой операции с таблицей Stick.
warning | Некоторые важные, но некритические ошибки, такие как невозможность установить заголовок запроса или невозможность подключения к DNS-серверу имён.
notification | Изменения состояния сервера, например, «UP» (включен) или «DOWN» (отключён). Также учитываются другие события при запуске, такие как запуск прокси-серверов и загрузка модулей. Журнал проверки работоспособности, если он включен, также использует этот уровень.
info | Подробности и ошибки TCP-подключения и HTTP-запросов.
debug | Вы можете написать собственный код Lua, который будет регистрировать отладочные сообщения.

### Статистика

Доступ к странице мониторинга: http://IP_ADDRESS:5000/stats (логин/пароль из stats auth).

IP_ADDRESS - ip адрес вашего сервера с HAProxy

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

Действие | Описание
--- | ---
Set state to READY | Выбранный сервер будет получать трафик. Окончательное состояние сервера будет определено на основе настроенной проверки работоспособности.
Set state to DRAIN | Выбранный сервер не будет принимать никаких новых подключений, кроме тех, которые приняты через сохранение сеанса.
Set state to MAINT | Выбранный сервер не будет принимать никаких новых подключений, а проверки работоспособности будут остановлены.
Health: disable checks | Отключить проверки работоспособности сервера.
Health: enable checks | Включить проверки работоспособности сервера.
Health: force UP | Немедленно перевести проверку работоспособности сервера в состояние UP.
Health: force NOLB | Принудительно перевести проверку работоспособности сервера в состояние NOLB. Это останавливает сервер от приема новых непостоянных подключений.
Health: force DOWN | Немедленно перевести проверку работоспособности сервера в состояние DOWN.
Agent: disable checks | Отключить проверки агента сервера.
Agent: enable checks | Включить проверки агента сервера.

### Frontend часть

Принимает HTTP на `:80` и HTTPS на `:443`. HTTP трафик перенаправляет на HTTPS.

    frontend haproxynode
    	bind *:80
    	redirect scheme https code 301 if !{ ssl_fc }
        bind *:443 ssl crt /etc/ssl/release-info.dev.local/release-info.dev.local.pem
        mode http
        default_backend backendnodes

- `bind :80` - приём HTTP (для редиректа).

- `bind :443 ssl crt ...` - приём HTTPS с указанным PEM-сертификатом.

- `http-request redirect ... unless { ssl_fc }` - перевод всего трафика на HTTPS.

- `default_backend backendnodes` - выбор backend по умолчанию.

### Backend часть

Перенаправляет запросы от frontend части на сервера приложений:

    backend backendnodes
        balance leastconn #roundrobin
        option forwardfor # Включает X-Forwarded-For для передачи IP-адреса клиента обслуживающему серверу.
    	cookie GS_BALANCER_SERVER_NAME insert
        acl has_x_forwarded_port req.hdr(X-Forwarded-Port) -m found
        http-request set-header X-Forwarded-Port %[req.hdr(X-Forwarded-Port)] if has_x_forwarded_port
        http-request set-header X-Forwarded-Port %[dst_port] unless has_x_forwarded_port
        http-request add-header X-Forwarded-Proto https if { ssl_fc }
        option httpchk HEAD / HTTP/1.1\r\nHost:localhost

        server global-server-01 localhost:8080 check cookie global-server-01

- `balance roundrobin` — равномерная балансировка по списку `server`.

- `option forwardfor` — добавляет `X-Forwarded-For` с IP клиента.

- `cookie ... insert` + `server ... cookie ...` — липкие сессии (sticky) для привязки клиента к одному серверу.

- `server <name> <ip:port> check` — сервер backend; `check` включает `health-check`. Добавляйте столько строк `server`, сколько у вас целевых инстансов.

## Проверка конфигурации и перезапуск

    sudo haproxy -f /etc/haproxy/haproxy.cfg -c
    sudo systemctl restart haproxy

- `-c` - проверяет синтаксис и базовую корректность конфигурации без запуска.

- `restart` - перезапускает службу с разрывом соединений клиентов.

Проверка статуса работы:

    sudo systemctl status haproxy
    journalctl -u haproxy -e

## Включение логирования HAProxy

По умолчанию HAProxy пишет лог в `/dev/log`. Настроим rsyslog для работы с логами HAProxy.

Rsyslog — это быстрый, расширяемый сервис для управления логами.

Установка Syslog server:

    sudo apt install -y rsyslog

Конфигурация rsyslog для HAProxy:

    sudo nano /etc/logrotate.d/haproxy

Пример конфигурации файла:

    /var/log/haproxy/haproxy.log {
        daily
        rotate 7
        compress
        delaycompress
        missingok
        notifempty
        create 0640 haproxy adm
        postrotate
            invoke-rc.d rsyslog reload > /dev/null
        endscript
    }

## Безопасная аутентификация к странице статистики

В избежании хранения пароля для входа на страницу в открытом виде, используйте `userlist` с хэшированным паролем.

Подробнее в документации.

Для шифрования паролей в HAProxy используется функция `crypt(3)`. Это библиотечная функция, которая используется для создания и проверки хешей паролей. Она генерирует специальную строку, содержащую хеш, «соль» (случайная строка, добавленная для усиления безопасности) и информацию об алгоритме, которая хранится в файлах пользователей, например, /etc/shadow. Эта строка затем сравнивается с введённым паролем при входе пользователя для аутентификации.

Наиболее простой способ использовать данную функцию, воспользоваться утилитой mkpasswd, входящей в состав whois.

Установите утилиту и сгенерируйте хеш:

    sudo apt install -y whois
    mkpasswd -m sha-256
    # Введите пароль → получите строку $...

Замените секцию `frontend stats` и добавьте `userlist`:

    userlist stats_users
        user admin password <hash> # Вставьте сгенириуемый ранее хэш

    frontend stats
        mode http
        bind :5000
        stats enable
        stats refresh 10s
        stats uri /stats
        stats show-modules
        stats admin if TRUE
        http-request auth unless { http_auth(stats_users) }

## Подключение HAProxy к Prometheus

Предполагается что у вас развернута ВМ с Prometheus и Grafana. Подробнее об установке в руководстве.

Включим встроенный Prometheus-экспортёр HAProxy и настроим сбор метрик Prometheus. Подробнее о конфигурировании в документации

### Включить экспорт метрик в HAProxy

Добавьте во frontend отдельный блок для выдачи метрик:

    frontend prometheus
        bind :8405
        mode http
        http-request use-service prometheus-exporter

### Конфигурирование Prometheus

Отредактируйте /opt/prometheus/prometheus.yml и добавьте job c HAProxy:

    scrape_configs:
    - job_name: "haproxy"
      static_configs:
        - targets: ["<IP_ADDRESS HAPROXY>:8405"]

### Импорт готового дашборда HAProxy в Grafana

Существуют готовые дашборды. В этом примере используем JSON из репозитория, рекомендованного Grafana: rfmoz/grafana-dashboards

Шаги импорта:

1. В Grafana: `Dashboards -> New -> Import`.

2. Загрузите файл haproxy.json (предварительно скачайте его с GitHub).

3. В поле Select a data source выберите ранее добавленный источник Prometheus.

4. Нажмите Import — дашборд появится в списке и станет доступен к просмотру.