ActiveDirectory/LDAP-HA
Постановка задачи
Представим ситуацию, что вам надо реализовать отказоустойчивый балансировщик для ldap/ldaps запросов, чтобы была единая точка входа для запросов, и выход из строя ldap бэкенда или одной ноды балансировщика не влияло на получение результата.
Примерная схема может выглядеть так.
Несколько узлов балансировщика (balancer1..N) и несколько ldap бэкендов (LDAP1..N)
HA-реализация
Рассмотрим реализацию в которой у нас будет
- Два LDAP сервера (в реализации Альт Домен) - dc1, dc2, rodc1
- Два узла балансировки - balancer1, balancer2
Конфигурацию LDAP серверов пропустим. Будем считать, что они есть и работают.
Сетевая конфигурация узлов балансировщика будет такой
- balancer1 - 1.2.3.10
- balancer2 - 1.2.3.11
- "Плавающий" общий адрес - 1.2.3.5
На узлах балансировки нужно установить пакеты ha-proxy, keepalived
# apt-get update # apt-get install haproxy keepalived
Также нужно разрешить поддержку двух ip адресов т.к. логика работы keepalived основана на том, что один адрес (основная точка входа для запросов) будет "плавающим" и переходить от узла к узлу.
Для этого в файле /etc/net/sysctl.conf добавим параметр net.ipv4.ip_nonlocal_bind = 1
# echo net.ipv4.ip_nonlocal_bind = 1 >> /etc/net/sysctl.conf # sysctl net.ipv4.ip_nonlocal_bind=1
Файл конфигурации /etc/haproxy/haproxy.cfg для всех узлов будет выглядеть примерно так
global log /dev/log local0 log /dev/log local1 notice chroot /var/lib/haproxy pidfile /run/haproxy.pid stats socket /var/lib/haproxy/stats stats timeout 30s user _haproxy group _haproxy daemon maxconn 10000 defaults log global mode tcp option tcplog option dontlognull timeout connect 5000ms timeout client 50000ms timeout server 50000ms retries 3 frontend ldap_frontend bind *:389 default_backend ldap_servers frontend ldaps_frontend bind *:636 default_backend ldaps_backend backend ldaps_backend mode tcp # balance roundrobin option ssl-hello-chk option tcp-check balance leastconn server dc1 dc1.test.alt:636 check port 636 inter 2000 rise 2 fall 3 server dc2 dc2.test.alt:636 check port 636 inter 2000 rise 2 fall 3 server rodc rodc.test.alt:636 check port 636 inter 2000 rise 2 fall 3 backend ldap_servers mode tcp option ldap-check # balance roundrobin balance leastconn #tcp-check connect port 389 #tcp-check send-binary 300c020101600702010304008000 #tcp-check expect binary 300c02010161070a010004000400 server dc1 dc1.test.alt:389 check port 389 inter 2000 rise 2 fall 3 server dc2 dc2.test.alt:389 check port 389 inter 2000 rise 2 fall 3 server rodc rodc.test.alt:389 check port 389 inter 2000 rise 2 fall 3
Поясню некоторые моменты относительно конфигурации haproxy. Как видно, настроено два бэкенда - для LDAP и LDAPS. Параметры схожи.
balance roundrobin - Алгоритм балансировки. Просто круговой перебор - первый запрос → сервер 1, второй → сервер 2, третий → сервер 3, четвертый → снова сервер 1. Вполне понятный и предсказуемый, но есть минусы
- Игнорирует текущую нагрузку серверов
- Не учитывает время выполнения запросов
- Проблемы с "длинными" соединениями - если один запрос выполняется 10 секунд, а другой 0.1 секунды, серверы будут загружены неравномерно
balance leastconn - в этом случае подсчитываются активные соединения на каждом сервере и новый запрос всегда отправляется на сервер с наименьшим количеством текущих соединений. Очень удобно при использовании долгих соединений (LDAP !)
Кроме этих двух режимов есть и другие
- Source - Привязка к ip т.е. один клиент (по IP) всегда попадает на один сервер
- URI - Запросы к одному URI идут на один сервер
- Random - если у вас большой кластер, вероятно это лучший выбор
Для остальных я просто приложу краткую таблицу
| Алгоритм | Сфера применения |
|---|---|
| roundrobin | Сглаженное и справедливое распределение при равномерном времени обработки серверов. Динамический алгоритм с ограничением 4095 серверов на бэкенд. |
| static-rr | Аналогичен roundrobin, но статический — изменение весов серверов на лету не действует. Нет ограничения на количество серверов. Не используется в режиме LOG. |
| leastconn | Рекомендуется для очень длинных сессий: LDAP, SQL, TSE и т.д. Не очень хорошо подходит для протоколов с короткими сессиями (например, HTTP). Учитывает как установленные, так и ожидающие соединения. |
| first | Первый сервер с доступными слотами соединений получает запрос. Цель — использовать минимальное количество серверов. Игнорирует вес серверов. Полезен для длинных сессий (RDP, IMAP). |
| hash | Универсальный алгоритм хеширования по заданному выражению. Может заменить source, uri, hdr() и т.д. Позволяет использовать конвертеры или извлекать данные из локальных переменных. |
| source | Хеширует исходный IP-адрес. Гарантирует, что один клиент (по IP) будет попадать на один сервер, пока состав серверов не изменится. Часто используется в TCP-режиме. |
| uri | Хеширует часть URI или весь URI. Используется с прокси-кешами и антивирусными прокси для максимизации hit rate. Только для HTTP-бэкендов. |
| url_param | Ищет указанный параметр в строке запроса HTTP GET (или в теле POST с модификатором check_post). Используется для отслеживания идентификаторов пользователей. Только для HTTP-бэкендов. |
| hdr(<имя>) | Балансировка на основе значения указанного HTTP-заголовка. Если заголовок отсутствует или пуст, используется roundrobin. Только для HTTP-бэкендов. |
| random | Использует случайное число в качестве ключа для консистентного хеширования. Полезен при больших фермах или частом добавлении/удалении серверов (избегает "эффекта молота"). |
| rdp-cookie | Ищет и хеширует указанную RDP-куку (по умолчанию "mstshash"). Используется как упрощённый механизм сохранения сессии для протокола RDP. |
| log-hash | Применяет конвертеры к лог-сообщению, хеширует результат для выбора сервера. Только для бэкендов в режиме LOG. |
| sticky | Старается отправлять все сообщения на первый доступный сервер в списке. При его отказе переходит к следующему. Используется для логирования. |
Параметры у опции server (кроме и так понятных) - inter 2000 rise 2 fall 3. Это означает, что проверка выполняется каждые 2 секунды (inter 2000). Чтобы сервер был признан нерабочим, нужно 3 неудачные проверки подряд (fall 3). Только после этого HAProxy исключит его из балансировки.
Файл /etc/keepalived/keepalived.conf для каждых хостов будет отличатся.
Для хоста с ролью MASTER он будет примерно таким
global_defs {
router_id haproxy_ldap_100
}
# Script used to check if HAProxy is running
vrrp_script check_haproxy {
script "killall -0 haproxy"
interval 3
weight 2
fall 2
rise 1
}
# Виртуальный интерфейс
vrrp_instance VI_01 {
state MASTER
interface ens19
virtual_router_id 51
priority 100
# Виртуальный IP-адрес
virtual_ipaddress {
1.2.3.5/24
}
track_script {
check_haproxy
}
}
Для хоста с ролью BACKUP он будет примерно таким
global_defs {
router_id haproxy_ldap_100_passive
}
# Script used to check if HAProxy is running
vrrp_script check_haproxy {
script "killall -0 haproxy"
interval 3
weight 2
fall 2
rise 1
}
# Виртуальный интерфейс
vrrp_instance VI_01 {
state BACKUP
interface ens19
virtual_router_id 51
priority 90
# Виртуальный IP-адрес
virtual_ipaddress {
1.2.3.5/24
}
track_script {
check_haproxy
}
}
После сделанных изменений запускаем необходимые сервисы на всех узлах балансировщика.
systemctl enable --now haproxy keepalived
После чего на узле MASTER в выводе команды ip a появится еще один ip адрес.
Проверка
Точка входа для запросов у нас 1.2.3.5. Если одна из нод балансировщика становится недоступной, адрес 1.2.3.5 автоматически поднимается на другой ноде. Проверить можно обычным ldapsearch
LDAP запрос
ldapsearch -b 'dc=test,dc=alt' -D 'domainuser1@TEST.ALT' -w Qwerty1 -H ldap://1.2.3.5 'Objectclass=*'
LDAPS запрос
ldapsearch -b 'dc=test,dc=alt' -D 'domainuser1@TEST.ALT' -w Qwerty1 -o TLS_REQCERT=Never -H ldaps://1.2.3.5 'Objectclass=*'
Теперь можно выключить то один то другой узел балансировщика и проверить, что ldapsearch все равно работает как задумывалось. Тоже самое и с контроллерами домена
