ActiveDirectory/LDAP-HA

Материал из ALT Linux Wiki

Постановка задачи

Представим ситуацию, что вам надо реализовать отказоустойчивый балансировщик для ldap/ldaps запросов, чтобы была единая точка входа для запросов, и выход из строя ldap бэкенда или одной ноды балансировщика не влияло на получение результата.

Примерная схема может выглядеть так.

LDAP HA

Несколько узлов балансировщика (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 - если у вас большой кластер, вероятно это лучший выбор

Для остальных я просто приложу краткую таблицу

Алгоритмы балансировки используемой 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
   }
}
Примечание: Обратите внимание, на уникальные параметры router_id, priority. Одинаковый для всех узлов балансировщика параметр virtual_router_id, имя сетевого интерфейса и ip адрес. Также важен параметр state, который может быть MASTER/BACKUP


После сделанных изменений запускаем необходимые сервисы на всех узлах балансировщика.

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 все равно работает как задумывалось. Тоже самое и с контроллерами домена