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 -y haproxy keepalived
Также нужно разрешить поддержку двух ip-адресов, т.к. логика работы keepalived основана на том, что один адрес (основная точка входа для запросов) будет «плавающим», передаваясь от узла к узлу.
Для этого изменим параметр net.ipv4.ip_nonlocal_bind:
# sysctl net.ipv4.ip_nonlocal_bind=1 # echo net.ipv4.ip_nonlocal_bind = 1 >>/etc/net/sysctl.conf
Настройка
Файл конфигурации для всех узлов будет выглядеть примерно так:
| /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 — вероятно, лучший выбор для большого кластера.
Для остальных просто приложу краткую таблицу:
| Алгоритмы балансировки HAProxy | |
|---|---|
| Алгоритм | Сфера применения |
| 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
}
}
|
- - параметр
state, принимающий значение либо MASTER, либо BACKUP; - - уникальные параметры
router_idиpriority, а также - - одинаковые для всех узлов балансировщика:
- параметр
virtual_router_id, - имя сетевого интерфейса,
- ip-адрес.
- параметр
Запуск
После сделанных изменений запускаем необходимые сервисы на всех узлах балансировщика:
# 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 'user@TEST.ALT' -w 'Qwerty1' -H ldap://1.2.3.5 'Objectclass=*'
|
|---|---|
| LDAPS | ldapsearch -b 'dc=test,dc=alt' -D 'user@TEST.ALT' -w 'Qwerty1' -H ldaps://1.2.3.5 'Objectclass=*' -o TLS_REQCERT=Never
|
Теперь можно выключать то один, то другой узел балансировщика и проверять, что ldapsearch всё равно работает как задумывалось. То же самое и с контроллерами домена.
