Балансировка нагрузки HTTP с помощью HAProxy на сервере Ubuntu

Published by Leave your thoughts

HAProxy(или High Availability Proxy) – это открытый балансировщик нагрузки для сервисов TCP. Он, в частности, подходит для выравнивания нагрузки HTTP, поскольку поддерживает постоянство сессии.

Требования

Для работы понадобятся три виртуальных выделенных сервера:

Сервер 1 – балансировщик нагрузки

  • Имя хоста: haproxy;
  • OS: Ubuntu;
  • Внешний IP: 1.1.1.1;
  • Внутренний IP: 10.0.0.100

Сервер 2 – Нод 1

  • Имя хоста: lamp1;
  • OS: LAMP на Ubuntu;
  • Внутренний IP: 10.0.0.1

Сервер 3 – Нод 2

  • Имя хоста:  lamp2;
  • OS: LAMP на Ubuntu;
  • Внутренний IP: 10.0.0.2

1: Установка HAProxy

Чтобы установить HAProxy, запустите:

apt-get install haproxy

После установки нужно включить HAProxy, чтобы балансировщик мог запускаться при помощи скрипта инициализации:

nano /etc/default/haproxy

Задайте опции ENABLED значение 1:

ENABLED=1

Чтобы убедиться, что настройка прошла успешно, запустите init-скрипт HAProxy без параметров:

root@haproxy:~# service haproxy
Usage: /etc/init.d/haproxy {start|stop|reload|restart|status}

2: Настройка HAProxy

Переместите конфигурационный файл по умолчанию и создайте собственный файл.

mv /etc/haproxy/haproxy.cfg{,.original}

Чтобы создать и открыть для редактирования новый конфигурационный файл, введите:

nano /etc/haproxy/haproxy.cfg

Добавьте первый блок конфигураций:

global
log 127.0.0.1 local0 notice
maxconn 2000
user haproxy
group haproxy

Директива log задаёт системный лог сервера, в который нужно отправлять предупреждения и извещения. В системе Ubuntu утилита rsyslog, как правило, предварительно установлена и запущена, но не прослушивает ни одного IP-адреса. О настройке rsyslog – далее в руководстве.

Директива maxconn определяет количество одновременных подключений на внешнем интерфейсе. Значение по умолчанию (2000) можно изменить в соответствии с возможностями и настройками VPS.

Директивы user и group передают процесс HAProxy указанному пользователю и группе; не изменяйте эти параметры.

defaults
log     global
mode    http
option  httplog
option  dontlognull
retries 3
option redispatch
timeout connect  5000
timeout client  10000
timeout server  10000

В данном блоке определяются стандартные значения. В нём нужно отредактировать параметры timeout. Опция connect определяет максимальное время ожидания для успешного подключения к серверу. Опции client и server применяются, когда клиент или сервер должны подтвердить приём данных или отправить данные в TCP-процессе. Рекомендуется установить для них одинаковое значение.

Директива retries устанавливает количество повторных попыток подключения к серверу после сбоя подключения.

Опция option redispatch включает перераспределение сессии в случае сбоев соединения. Это переопределяет липкость сессий при сбое VPN.

listen appname 0.0.0.0:80
mode http
stats enable
stats uri /haproxy?stats
stats realm Strictly\ Private
stats auth A_Username:YourPassword
stats auth Another_User:passwd
balance roundrobin
option httpclose
option forwardfor
server lamp1 10.0.0.1:80 check
server lamp2 10.0.0.2:80 check

Этот блок кода содержит настройки фроненда и бэкенда. HAProxy будет прослушивать порт 80 (appname – имя приложения).

Директивы stats включают страницу статистики соединения и защищают её базовой аутентификацией HTTP (учётные данные заданы при помощи директивы stats auth).

Эта страница доступна по URL-адресу, указанному в директиве stats uri (в данном случае http://1.1.1.1/haproxy?stats).

Директива balance задаёт алгоритм балансировки. Доступные алгоритмы:

  • Round Robin (roundrobin),
  • Static Round Robin (static-rr),
  • Least Connections (leastconn),
  • Source (source), URI (uri),
  • URL parameter (url_param).

Примечание: Подробную информацию о каждом алгоритме можно найти в официальной документации.

Директива server определяет сервер бэкенда при помощи синтаксиса:

server <name> <address>[:port] [param*]

Имя, указанное здесь, будет появляться в логах и предупреждениях. Эта директива поддерживает много параметров; в данном примере используются параметры check и cookie. Опция check включает проверку состояния сервера.

После внесения всех настроек можно включить сервис HAProxy:

service haproxy start

3: Тестирование балансировки нагрузки и отказоустойчивости

Чтобы протестировать балансировку нагрузки, создайте PHP-скрипт на всех веб-серверах (в данном случае это серверы бэкенда  LAMP1 и LAMP2).

/var/www/file.php
<?php
header('Content-Type: text/plain');
echo "Server IP: ".$_SERVER['SERVER_ADDR'];
echo "\nClient IP: ".$_SERVER['REMOTE_ADDR'];
echo "\nX-Forwarded-for: ".$_SERVER['HTTP_X_FORWARDED_FOR'];
?>

Используйте curl и запросите этот файл несколько раз.

> curl http://1.1.1.1/file.php
Server IP: 10.0.0.1
Client IP: 10.0.0.100
X-Forwarded-for: 117.213.X.X
> curl http://1.1.1.1/file.php
Server IP: 10.0.0.2
Client IP: 10.0.0.100
X-Forwarded-for: 117.213.X.X
> curl http://1.1.1.1/file.php
Server IP: 10.0.0.1
Client IP: 10.0.0.100
X-Forwarded-for: 117.213.X.X

Обратите внимание: HAProxy переключает связь между LAMP1 и LAMP2 согласно алгоритму Round Robin. Client IP здесь – это внутренний IP-адрес балансировщика нагрузки, а X-Forwarded-For – это IP-адрес сервера.

Чтобы протестировать отказоустойчивость, перейдите на один из веб-серверов и остановите сервис:

lamp1@haproxy:~#service apache2 stop

Затем снова используйте curl для запроса файла.

4: Липкие сессии

Если приложение обслуживает динамический контент на основе сеансов входа пользователей (чего приложения не делают), посетители будут видеть на экране помехи и неисправности из-за непрерывного переключения между серверами.

Липкие сессии (sticky session) позволяют закрепить пользователя за сервером, который обслужил его первый запрос. для этого нужно отметить каждый сервер бэкенда при помощи файлов cookie.

Для демонстрации липких сессий можно использовать следующий PHP-код:

/var/www/session.php
<?php
header('Content-Type: text/plain');
session_start();
if(!isset($_SESSION['visit']))
{
echo "This is the first time you're visiting this server";
$_SESSION['visit'] = 0;
}
else
echo "Your number of visits: ".$_SESSION['visit'];
$_SESSION['visit']++;
echo "\nServer IP: ".$_SERVER['SERVER_ADDR'];
echo "\nClient IP: ".$_SERVER['REMOTE_ADDR'];
echo "\nX-Forwarded-for: ".$_SERVER['HTTP_X_FORWARDED_FOR']."\n";
print_r($_COOKIE);
?>

Этот код создаёт PHP-сессию и отображает количество просмотров страниц за одну сессию.

Метод 1: Вставка Cookie

Данный метод добавит в ответы HAProxy заголовок Set-Cookie, который содержит имя сервера бэкенда как значение cookie. Клиент (веб-браузер) включит в себя значение cookie со всеми запросами, и на основе этого значения HAProxy сможет направить запросы  на правильный сервер бэкенда.

Для этого нужно добавить директиву cookie и отредактировать директивы server после listen.

cookie SRVNAME insert
server lamp1 10.0.0.1:80 cookie S1 check
server lamp2 10.0.0.2:80 cookie S2 check

Теперь HAProxy добавляет заголовок «Set-Cookie:» с именем сервера (SRVNAME) и значением S1 или S2 в зависимости от того, какой из серверов должен ответить.

После этого перезапустите сервис:

service haproxy restart

Для тестирования настройки липких сессий используйте  curl:

> curl -i http://1.1.1.1/session.php
HTTP/1.1 200 OK
Date: Tue, 24 Sep 2013 13:11:22 GMT
Server: Apache/2.2.22 (Ubuntu)
X-Powered-By: PHP/5.3.10-1ubuntu3.8
Set-Cookie: PHPSESSID=l9haakejnvnat7jtju64hmuab5; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 143
Connection: close
Content-Type: text/plain
Set-Cookie: SRVNAME=S1; path=/
This is the first time you're visiting this server
Server IP: 10.0.0.1
Client IP: 10.0.0.100
X-Forwarded-for: 117.213.X.X
Array
(
)

Как можно понять по этой строке:

Set-Cookie: SRVNAME=S1; path=/

на первый запрос ответил сервер LAMP1. Теперь нужно эмулировать действия браузера во время второго запроса; добавьте в запрос cookie при помощи параметра –cookie:

> curl -i http://1.1.1.1/session.php --cookie "PHPSESSID=l9haakejnvnat7jtju64hmuab5;SRVNAME=S1;"
HTTP/1.1 200 OK
Date: Tue, 24 Sep 2013 13:11:45 GMT
Server: Apache/2.2.22 (Ubuntu)
X-Powered-By: PHP/5.3.10-1ubuntu3.8
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 183
Connection: close
Content-Type: text/plain
Your number of visits: 1
Server IP: 10.0.0.1
Client IP: 10.0.0.100
X-Forwarded-for: 117.213.87.127
Array
(
[PHPSESSID] => l9haakejnvnat7jtju64hmuab5
[SRVNAME] => S1
)
> curl -i http://1.1.1.1/session.php --cookie "PHPSESSID=l9haakejnvnat7jtju64hmuab5;SRVNAME=S1;"
HTTP/1.1 200 OK
Date: Tue, 24 Sep 2013 13:11:45 GMT
Server: Apache/2.2.22 (Ubuntu)
X-Powered-By: PHP/5.3.10-1ubuntu3.8
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 183
Connection: close
Content-Type: text/plain
Your number of visits: 2
Server IP: 10.0.0.1
Client IP: 10.0.0.100
X-Forwarded-for: 117.213.87.127
Array
(
[PHPSESSID] => l9haakejnvnat7jtju64hmuab5
[SRVNAME] => S1
)

Как видите, все запросы по-прежнему обслуживаются сервером LAMP1. Этот метод позволяет настроить липкие сессии для всех файлов сервера.

Метод 2: Префиксы Cookie

Данный метод позволяет настроить липкие сессии только для определённых cookies. Для этого используется опция prefix.

Для настройки этого метода используется следующая директива:

cookie PHPSESSID prefix

PHPSESSID можно заменить именем cookie; директива server остаётся такой же, как в предыдущей настройке.

Протестируйте настройку:

> curl -i http://1.1.1.1/session.php
HTTP/1.1 200 OK
Date: Tue, 24 Sep 2013 13:36:27 GMT
Server: Apache/2.2.22 (Ubuntu)
X-Powered-By: PHP/5.3.10-1ubuntu3.8
Set-Cookie: PHPSESSID=S1~6l2pou1iqea4mnhenhkm787o56; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 143
Content-Type: text/plain
This is the first time you're visiting this server
Server IP: 10.0.0.1
Client IP: 10.0.0.100
X-Forwarded-for: 117.213.X.X
Array
(
)

Обратите внимание: cookie S1 добавляется в качестве префикса к cookie сессии. Отправьте ещё два запроса:

> curl -i http://1.1.1.1/session.php --cookie "PHPSESSID=S1~6l2pou1iqea4mnhenhkm787o56;"
HTTP/1.1 200 OK
Date: Tue, 24 Sep 2013 13:36:45 GMT
Server: Apache/2.2.22 (Ubuntu)
X-Powered-By: PHP/5.3.10-1ubuntu3.8
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 163
Content-Type: text/plain
Your number of visits: 1
Server IP: 10.0.0.1
Client IP: 10.0.0.100
X-Forwarded-for: 117.213.X.X
Array
(
[PHPSESSID] => 6l2pou1iqea4mnhenhkm787o56
)
> curl -i http://1.1.1.1/session.php --cookie "PHPSESSID=S1~6l2pou1iqea4mnhenhkm787o56;"
HTTP/1.1 200 OK
Date: Tue, 24 Sep 2013 13:36:54 GMT
Server: Apache/2.2.22 (Ubuntu)
X-Powered-By: PHP/5.3.10-1ubuntu3.8
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 163
Content-Type: text/plain
Your number of visits: 2
Server IP: 10.0.0.1
Client IP: 10.0.0.100
X-Forwarded-for: 117.213.X.X
Array
(
[PHPSESSID] => 6l2pou1iqea4mnhenhkm787o56
)

Как видите, сессия работает без сбоев и полностью обслуживается сервером LAMP1.

5: Логирование HAProxy

В начале, во время настройки HAProxy, в конфигурации была добавлена строка:

log 127.0.0.1 local0 notice

которая отправляет сообщения системного лога (syslog) на IP локального хоста. По умолчанию rsyslog в Ubuntu не прослушивает никаких адресов. Это поведение нужно изменить.

Отредактируйте конфигурационный файл:

nano /etc/rsyslog.conf

Добавьте (отредактируйте или раскомментируйте) следующие строки:

$ModLoad imudp
$UDPServerAddress 127.0.0.1
$UDPServerRun 514

Теперь rsyslog будет прослушивать UDP-порт 514 адреса 127.0.0.1; однако сообщения HAProxy будут регистрироваться в /var/log/syslog. Это нужно исправить.

Создайте правило для логов HAProxy.

nano /etc/rsyslog.d/haproxy.conf
if ($programname == 'haproxy') then -/var/log/haproxy.log

Перезапустите сервис rsyslog:

service rsyslog restart

Теперь все сообщения и извещения HAProxy будут храниться в логе /var/log/haproxy.log.

6: Постоянное HTTP-соединение HAProxy

Ранее в настройках, под директивой listen, была использована директива option httpclose, которая добавляет заголовок Connection: close. Он закрывает соединения клиента (браузера) после получения ответа.

Чтобы включить повторное использование соединений HTTP (или HTTP keep-alive) для HAProxy, нужно заменить строку option httpclose следующим кодом:

option http-server-close
timeout http-keep-alive 3000

Такая настройка полезна, поскольку она помогает снизить использование ресурсов балансировки нагрузки.

Тестирование постоянного HTTP-соединения

Можно протестировать настройку при помощи curl, оправив несколько одновременных запросов. Вывод будет примерно таким (ненужные фрагменты опущены):

> curl -v http://1.1.1.1/index.html http://1.1.1.1/index.html
* About to connect() to 1.1.1.1 port 80 (#0)
*   Trying 1.1.1.1... connected
> GET /index.html HTTP/1.1
> User-Agent: curl/7.23.1 (x86_64-pc-win32) libcurl/7.23.1 OpenSSL/0.9.8r zlib/1.2.5
> Host: 1.1.1.1
> Accept: */*
>
......[Output omitted].........
* Connection #0 to host 1.1.1.1 left intact
* Re-using existing connection! (#0) with host 1.1.1.1
* Connected to 1.1.1.1 (1.1.1.1) port 80 (#0)
> GET /index.html HTTP/1.1
> User-Agent: curl/7.23.1 (x86_64-pc-win32) libcurl/7.23.1 OpenSSL/0.9.8r zlib/1.2.5
> Host: 1.1.1.1
> Accept: */*
>
.......[Output Omitted].........
* Connection #0 to host 1.1.1.1 left intact
* Closing connection #0

Найдите в выводе строку:

Re-using existing connection! (#0) with host 1.1.1.1

Это значит, что утилита curl использовала то же самое соединение для нескольких запросов.

Tags: , , ,

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *


*

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>