Table of Contents

Задача

В двух датацентрах хостятся одинаковые web-сервисы (Microsoft Exchange 2016) на нескольких серверах (в ДЦ каждом по два сервера).
Запросы к web-сервисам могут приходить как от процессов в одном из датацентров (пользователи VDI), так и из внешнего интернета.
Датацентры неравноправны. Один из них - основной (постоянно обслуживает основную массу запросов), второй - резервный (постоянно обслуживает меньшую часть запросов).
Зачача - настроить отказоустойчивый опенсорсный балансировщик, который бы мог бы пережить следующие дизастеры:

Решение

Функционал high reliability реализуется с помощью демона, реализующего протокол VRRP, например - keepalived. Демон следит за состоянием нод кластера и если активная нода выходит из строя, то переключает нагрузку (передает IP-адрес) на запасную (пассивную) ноду.
Кроме keepalived можно использовать heartbeat.
keepalived попроще, а heartbeat пофункциональнее и имеет несколько иное назначение.

Будем настраивать active-passive связку из двух haproxy + keeapalived.
На каждой ноде будет практически однаково настроен haproxy. В конфигурациях backend-серверов будут прописаны серверы, находящиеся в одном датацентре с нодой балансировщика.
В конфигурации keepalived на каждой ноде балансировщика будет прописан скрипт, проверяющий состояние процесса haproxy и состояние сервиса. Нода балансировщика может считаться здоровой, если жив процесс haproxy и сервис доступен через балансировщик.

Настройка haproxy

haproxy - балансировщик. Осуществляет проверку состояния backend-серверов, балансирует нагрузку на них (в соответствии с кинфигурацией) и предоставляет к ним доступ через frontend.
Установка:

sudo apt-get update && sudo apt-get -y install haproxy

Вот пример настройки haproxy (/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 expose-fd listeners
        stats timeout 30s
        user haproxy
        group haproxy
        daemon

    ######## Default values for all entries till next defaults section
    defaults
      option  dontlognull             # Do not log connections with no requests
      option  redispatch              # Try another server in case of connection failure
      option  contstats               # Enable continuous traffic statistics updates
      option  logasap
      retries 3                       # Try to connect up to 3 times in case of failure
      timeout connect 5s              # 5 seconds max to connect or to stay in queue
      timeout http-keep-alive 1s      # 1 second max for the client to post next request
      timeout http-request 15s        # 15 seconds max for the client to send a request
      timeout queue 30s               # 30 seconds max queued on load balancer
      timeout tarpit 1m               # tarpit hold tim
      backlog 10000                   # Size of SYN backlog queue
      mode tcp                                #alctl: protocol analyser
      option tcplog                           #alctl: log format
      log global                              #alctl: log activation
      timeout client 300s                     #alctl: client inactivity timeout
      timeout server 300s                     #alctl: server inactivity timeout
      default-server inter 3s rise 2 fall 3   #alctl: default check parameters

    frontend ft_exchange_tcp
      bind *:443 name https          #alctl: listener https configuration.
      maxconn 100000                         #alctl: connection max (depends on capacity)
      default_backend exchange_2016_backend       #alctl: default farm to use

    backend exchange_2016_backend
        balance roundrobin
        # maximum SSL session ID length is 32 bytes.
        stick-table type binary len 32 size 30k expire 10s
        acl clienthello req_ssl_hello_type 1
        acl serverhello rep_ssl_hello_type 2
        # use tcp content accepts to detects ssl client and server hello.
        tcp-request inspect-delay 5s
        tcp-request content accept if clienthello
        # no timeout on response inspect delay by default.
        tcp-response content accept if serverhello
        # SSL session ID (SSLID) may be present on a client or server hello.
        # Its length is coded on 1 byte at offset 43 and its value starts
        # at offset 44.

        # Match and learn on request if client hello.
        stick on payload_lv(43,1) if clienthello
        # Learn on response if server hello.
        stick store-response payload_lv(43,1) if serverhello

        fullconn 100000

        option httpchk GET /ews/healthcheck.htm "HTTP/1.0\r\nHost: exch2016.domain.com"

        server exch_server1 exch1.domain.com:443 maxconn 100000 check check-ssl verify none
        server exch_server2 exch2.domain.com:443 maxconn 100000 check check-ssl verify none

        email-alert mailers mta
        email-alert level info
        email-alert from haproxy-alert@domain.com
        email-alert to admin@domain.com

    mailers mta
        mailer smtp1 smtp.domain.com:25

    frontend stats
        bind *:8080
        mode http
        stats enable
#       stats auth admin:adminpassword
        stats uri /

Тут настраивается единственный frontend для единственного backend в режиме tcp.
Балансировка - round-robin.
Прописан session sticking, чтобы клиент не прыгал по серверам в пределах одной сессии.
Проверка состояния backend-серверов осуществляется с помощью запроса:

option httpchk GET /ews/healthcheck.htm "HTTP/1.0\r\nHost: exch2016.domain.com"

Если ответ 200, то backend-сервер считается живым.

Для второй ноды конфигурация такая же, но список backend-серверов другой:

        server exch_server3 exch3.domain.com:443 maxconn 100000 check check-ssl verify none
        server exch_server4 exch4.domain.com:443 maxconn 100000 check check-ssl verify none

Настройка keepalived

keepalived следит за состоянием нод в пределах виртуального роутера (нод keepalive с одинаковым параметром virtual_router_id) и с заданным интервалом вычисляет приоритет (priority) ноды (в зависимости от результатов выполнения скрипта). Если приоритет ноды меняется - происходят выборы новой master-ноды. Если backup-нода обнаруживает недоступность master-ноды - она становится master-нодой.
Установка keepalived:

sudo apt-get update && sudo apt-get install keepalived


Вот конфигурация основной (master) ноды (/etc/keepalived/keepalived.conf):

global_defs {
   notification_email {
        admin@domain.com
   }
   notification_email_from haproxy-alert@domain.com
   smtp_server smtp.domain.com:25
   smtp_connect_timeout 30
}

vrrp_script chk_haproxy {
  script "/usr/bin/killall -0 haproxy && /usr/bin/curl -k 'https://localhost/ews/healthcheck.htm'"
  interval 2
  weight 2
}

vrrp_instance VI_1 {
    state MASTER
    interface ens160
    virtual_router_id 101
    priority 101
    advert_int 1
    smtp_alert
    authentication {
        auth_type PASS
        auth_pass superpassword
    }
    virtual_ipaddress {
        172.30.129.131
    }
    track_script {
      chk_haproxy
    }
}

Тут прописаны секции:


Вот конфигурация резервной (slave) ноды:

global_defs {
   notification_email {
        admin@domain.com
   }
   notification_email_from haproxy-alert@domain.com
   smtp_server smtp.domain.com:25
   smtp_connect_timeout 30
}

vrrp_script chk_haproxy {
  script "/usr/bin/killall -0 haproxy && /usr/bin/curl -k 'https://localhost/ews/healthcheck.htm'"
  interval 2
  weight 2
}

vrrp_instance VI_1 {
    state MASTER
    interface ens192
    virtual_router_id 101
    priority 100
    advert_int 1
    smtp_alert
    authentication {
        auth_type PASS
        auth_pass superpassword
    }
    virtual_ipaddress {
        172.30.129.131
    }
    track_script {
      chk_haproxy
    }
}

Подключение клиентов

Для подключения клиентов:

Ссылки

https://www.digitalocean.com/community/tutorials/how-to-set-up-highly-available-haproxy-servers-with-keepalived-and-floating-ips-on-ubuntu-14-04
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/load_balancer_administration/ch-initial-setup-vsa
https://tecadmin.net/setup-ip-failover-on-ubuntu-with-keepalived/