headermask image

Notice: Undefined variable: t in /var/www/user97185/data/www/system-administrators.info/yandex-ad.php on line 15

Notice: Undefined variable: r in /var/www/user97185/data/www/system-administrators.info/yandex-ad.php on line 15
Рекомендую: Фриланс-биржа | Кэшбэк-сервис | Интернет-бухгалтерия

Построение отказоустойчивого балансировщика нагрузки на базе Perlbal/Heartbeat

В этой статье описывается процесс настройки отказоустойчивого двухузлового балансировщика нагрузки с активной/пассивной конфигурацией, поддержкой сессий и механизма Failover на базе Perlbal/Heartbeat под управлением Debian. Балансировщик работает между конечным пользователем и двумя backend-серверами, которые отдают некий контент. (В нашем примере это два сервера с установленным Apache). Балансировщик не только проксирует запросы к бэкэнду, он еще и проверяет состояние бэкэнда и, в случае отказа, перенаправляет запросы к другому серверу (failover). Вдобавок, ведется постоянный мониторинг бэкэнд-серверов при помощи Heartbeat и, если master-сервер “лежит”, то slave автоматически становится мастером. Ваши пользователи не заметят сбоев в работе сервиса. Perlbal поддерживает механизм сессий, так что вы можете использовать ПО, завязанное на них (например форумы, онлайн-магазины etc)

Наша конфигурация
Мы используем следующую конфигурацию:

  • Узел балансировки 1: lb1.example.com, IP адрес: 192.168.0.100
  • Узел балансировки 2: lb3.example.com, IP адрес: 192.168.0.101
  • Web-сервер 1: http1.example.com, IP адрес: 192.168.0.102
  • Web-сервер 2: http3.example.com, IP адрес: 192.168.0.103
  • Также нам понадобится виртуальный IP, который будет объединять узлы балансировки: 192.168.0.99

Вот схематичное изображение нашего кусочка сети:

    shared IP=192.168.0.99
 192.168.0.100  192.168.0.101 192.168.0.102 192.168.0.103
 -------+------------+--------------+-----------+----------
        |            |              |           |
     +--+--+      +--+--+      +----+----+ +----+----+
     | lb1 |      | lb2 |      |  http1  | |  http2  |
     +-----+      +-----+      +---------+ +---------+
     Perlbal      Perlbal      2 web servers (Apache)
     heartbeat    heartbeat

Общий (виртуальный) IP-адрес не является проблемой, если вы сами являетесь администратором своей сети и можете самостоятельно выделять IP-адреса. Если же вы используете публичные адреса, то вам придется найти хостера, который даст вам два сервера (узлы балансировки) в одной подсети. Для общего IP вы можете использовать свободный адрес из той же подсети.
http1 и http2 – это “стандартные” сервера с Debian Etch и установленным Apache. (Конфигурация по умолчанию расположена в /etc/apache2/sites-available/default. Будьте внимательны, если вы используете другие пути, не забывайте делать на это поправку)

Подготовка бэкэнда
Мы сконфигурируем Perlbal как прозрачный прокси. Т.е. он будет транслировать запросы к бэкэнд-серверам, сохраняя оригинальный адрес клиента в заголовке X-Forwarded-For Конечно же, мы хотим видеть в логах оригинальные адреса, а не IP наших балансировочных узлов. Поэтому немного поправим конфигурацию логов Apache. Отредактируем файл /etc/apache2/apache2.conf
http1/http2:

[...]
#LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
[...]

Затем перезапустим Apache

/etc/init.d/apache2 restart

И на этом настройка бэкэндов закончена. Перейдем к балансировочным узлам.

Установка Perlbal
На момент написания статьи, perlbal не был доступен в репозиториях Debian, поэтому выполним его установку вручную.
Сначала установим необходимые зависимости:

lb1/lb2:

apt-get install build-essential unzip lynx ncftp perl

Затем запустим оболочку Perl

perl -MCPAN -e shell

И выполним в ней три команды

force install HTTP::Date
install IO::AIO
force install Perlbal

Perlbal установлен. Нажмите q для выхода из оболочки Perl

Настройка узлов балансировки
Конфигурационный файл Perlbal находится в /etc/perlbal/perlbal.conf, но его еще надо создать :) Создаем директорию и файл, и помещаем туда следующие строки:

CREATE POOL webfarm
  POOL webfarm ADD 192.168.0.102:80
  POOL webfarm ADD 192.168.0.103:80

CREATE SERVICE balancer
  SET listen          = 192.168.0.99:80
  SET role            = reverse_proxy
  SET pool            = webfarm
  SET persist_client  = on
  SET persist_backend = on
  SET verify_backend  = on
ENABLE balancer

Вероятно, вы захотите узнать больше об опциях конфигурации Perlbal. Лучший способ это сделать – скачать последнюю версию с http://code.google.com/p/perlbal/downloads/list, распоковать архив и обратить внимание на поддиректории conf/ и doc/. Там вы найдете описание параметров и различные примеры конфигурации.

Настройка Heartbeat
Мы настроили Perlbal на прослушивание нашего виртуального IP 192.168.0.99, но кто-то должен “сообщить” узлам балансировки, что они также должны слушать этот IP. Это сделает Heartbeat. Установим его:
lb1/lb2:

apt-get install heartbeat

Для того, чтобы разрешить Perlbal слушать IP, мы должны добавить в /etc/sysctl.conf следующую строчку:

[...]
net.ipv4.ip_nonlocal_bind=1

И выполнить команду

sysctl -p

Теперь создадим три конфигурационных файла для Heartbeat: /etc/ha.d/authkeys, /etc/ha.d/ha.cf, и /etc/ha.d/haresources./etc/ha.d/authkeys и /etc/ha.d/haresources должны быть одинаковыми на lb1 и lb2, а /etc/ha.d/ha.cf отличается всего одной строчкой!

vi /etc/ha.d/authkeys

auth 3
3 md5 somerandomstring

somerandomstring – это пароль, который используется heartbeat на lb1 и lb2 для взаимной аутентификации. Задайте ваш собственный пароль. Вы можете использовать три различных механизма шифрования, в данном примере используется md5

/etc/ha.d/authkeys должен быть доступен для чтения только пользователю root
lb1/lb2:

chmod 600 /etc/ha.d/authkeys

lb1:

vi /etc/ha.d/ha.cf

#
#       keepalive: how many seconds between heartbeats
#
keepalive 2
#
#       deadtime: seconds-to-declare-host-dead
#
deadtime 10
#
#       What UDP port to use for udp or ppp-udp communication?
#
udpport        694
bcast  eth0
mcast eth0 225.0.0.1 694 1 0
ucast eth0 192.168.0.101
#       What interfaces to heartbeat over?
udp     eth0
#
#       Facility to use for syslog()/logger (alternative to log/debugfile)
#
logfacility     local0
#
#       Tell what machines are in the cluster
#       node    nodename ...    -- must match uname -n
node    lb1.example.com
node    lb2.example.com

ВАЖНО: В качестве имен узлов (nodenames) используется вывод команды

uname -n

на обоих узлах балансировки.
Опции udpport, bcast, mcast, и ucast определяют способы коммуникации между узлами и задают параметры определения состояния узла. Вы можете оставить значения udpport, bcast и mcast такими же, как в примере, но опция ucast очень важна. В ней задается адрес второго узла балансировщика. В нашем примере это 192.168.0.101 (lb2.example.com)

На lb2 все точно также, за исключением опции ucast, значением для которой должен выступать адрес lb1
lb2:

vi /etc/ha.d/ha.cf

#
#       keepalive: how many seconds between heartbeats
#
keepalive 2
#
#       deadtime: seconds-to-declare-host-dead
#
deadtime 10
#
#       What UDP port to use for udp or ppp-udp communication?
#
udpport        694
bcast  eth0
mcast eth0 225.0.0.1 694 1 0
ucast eth0 192.168.0.100
#       What interfaces to heartbeat over?
udp     eth0
#
#       Facility to use for syslog()/logger (alternative to log/debugfile)
#
logfacility     local0
#
#       Tell what machines are in the cluster
#       node    nodename ...    -- must match uname -n
node    lb1.example.com
node    lb2.example.com

lb1/lb2:

vi /etc/ha.d/haresources

lb1.example.com 192.168.0.99

Первый параметр – это вывод

uname -n

Не имеет значения на каком узле вы создадите этот файл. Он указывает на виртуальный IP 192.168.0.99
Теперь запустим Heartbeat на обоих узлах

/etc/init.d/heartbeat start

Затем выполняем
lb1:

ip addr sh eth0

Мы должны увидеть, что хост lb1 слушает наш виртуальный IP

lb1:~# ip addr sh eth0
2: eth0:  mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:0c:29:a5:5b:93 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.100/24 brd 192.168.0.255 scope global eth0
    inet 192.168.0.99/24 brd 192.168.0.255 scope global secondary eth0:0
    inet6 fe80::20c:29ff:fea5:5b93/64 scope link
       valid_lft forever preferred_lft forever
lb1:~#

Это можно проверить, выполнив команду ifconfig

lb1:~# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:0C:29:A5:5B:93
          inet addr:192.168.0.100  Bcast:192.168.0.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fea5:5b93/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:63983 errors:0 dropped:0 overruns:0 frame:0
          TX packets:31480 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:92604963 (88.3 MiB)  TX bytes:2689903 (2.5 MiB)
          Interrupt:177 Base address:0x1400

eth0:0    Link encap:Ethernet  HWaddr 00:0C:29:A5:5B:93
          inet addr:192.168.0.99  Bcast:192.168.0.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          Interrupt:177 Base address:0x1400

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:56 errors:0 dropped:0 overruns:0 frame:0
          TX packets:56 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:3888 (3.7 KiB)  TX bytes:3888 (3.7 KiB)

lb1:~#

lb2 – это пассивный балансировочный узел. Он не должен слушать виртуальный IP, пока lb1 “жив”. Проверим это:
lb2:

ip addr sh eth0

Вывод должен быть примерно таким:

lb2:~# ip addr sh eth0
2: eth0:  mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:0c:29:e0:78:92 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.101/24 brd 192.168.0.255 scope global eth0
    inet6 fe80::20c:29ff:fee0:7892/64 scope link
       valid_lft forever preferred_lft forever
lb2:~#

ifconfig

не должен показать виртуальный IP

eth0      Link encap:Ethernet  HWaddr 00:0C:29:E0:78:92
          inet addr:192.168.0.101  Bcast:192.168.0.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fee0:7892/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:75127 errors:0 dropped:0 overruns:0 frame:0
          TX packets:42144 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:109669197 (104.5 MiB)  TX bytes:3393369 (3.2 MiB)
          Interrupt:169 Base address:0x1400

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:56 errors:0 dropped:0 overruns:0 frame:0
          TX packets:56 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:3888 (3.7 KiB)  TX bytes:3888 (3.7 KiB)

lb2:~#

Запускаем Perlbal
lb1/lb2:

perlbal –daemon

Конечно же, нам не хочется каждый раз запускать демон руками. Поэтому пропишем его автоматический запуск в /etc/rc.local

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

/usr/local/bin/perlbal --daemon
exit 0

Для остановки демона выполните команду

killall perlbal

Тестируем
Наш балансировочный узел настроен, запущен и готов к работе.
Вы можете выполнять HTTP-запросы к виртуальному IP 192.168.0.99 (или к тому адресу или доменному имени, с которым работаете вы) и в ответ будете получать контент с бэкэнд-серверов.
Для проверки отказоустойчивости и механизма failover остановите один из бэкэнд-серверов. Все запросы автоматически будут транслироваться к оставшемуся. Попробуйте остановить master-узел балансировщика (lb1), узел lb2 сразу возьмет на себя роль активного узла. вы можете проверить это, выполнив
lb2:

ip addr sh eth0

Теперь в этом выводе присутствует виртуальный адрес:

lb2:~# ip addr sh eth0
2: eth0:  mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:0c:29:e0:78:92 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.101/24 brd 192.168.0.255 scope global eth0
    inet 192.168.0.99/24 brd 192.168.0.255 scope global secondary eth0:0
    inet6 fe80::20c:29ff:fee0:7892/64 scope link
       valid_lft forever preferred_lft forever
lb2:~#

Как только узел lb1 будет вновь запущен, он опять будет выполнять функции master-узла.

Поддержка виртуальных хостов в Perlbal
Perlbal поддерживает виртуальные хосты. Допустим, мы хотим чтобы все запросы на *.site.com обслуживались узлами с адресами 192.168.0.102 и 192.168.0.103, а запросы на *.example.com узлами с адресами 192.168.0.104 и 192.168.0.105. Вот как это будет выглядеть в конфиге /etc/perlbal/perlbal.conf

LOAD vhosts

CREATE POOL webfarm1
  POOL webfarm1 ADD 192.168.0.102:80
  POOL webfarm1 ADD 192.168.0.103:80

CREATE SERVICE balancer1
  SET role            = reverse_proxy
  SET pool            = webfarm1
  SET persist_client  = on
  SET persist_backend = on
  SET verify_backend  = on
ENABLE balancer1

CREATE POOL webfarm2
  POOL webfarm2 ADD 192.168.0.104:80
  POOL webfarm2 ADD 192.168.0.105:80

CREATE SERVICE balancer2
  SET role            = reverse_proxy
  SET pool            = webfarm2
  SET persist_client  = on
  SET persist_backend = on
  SET verify_backend  = on
ENABLE balancer2

CREATE SERVICE vdemo
  SET listen         = 192.168.0.99:80
  SET role           = selector
  SET plugins        = vhosts
  SET persist_client = on

  VHOST *.site.com     = balancer1
  VHOST *.example.com  = balancer2
ENABLE vdemo

Постовой

Самая лучшая мебель для ванной комнаты в интернет-магазине www.mebel-vanna.ru.