Балансировка нагрузки с помощью NginX

Приветствую тебя, дорогой читатель. В этой статье я хочу описать настройку NginX для балансировки нагрузки на несколько back-end серверов, допустим Apache.

Итак предлагается следующая схема (картинка кликабельна):
12479dbe099929c17bf1cb19795557e0

В этом довольно простом деле нам помогут две директивы NginX:

  • upstream - директива, которая поставляется с модулем HttpUpstream и позволяет балансировать нагрузку на несколько серверов.
  • proxypass - директива, которая поставляется с модулем HttpProxy. Она позволяет корректно отправлять/проксировать запросы на сервера за балансировщиком.

Итак рассмотрим пример. Имеются 3 вэб-головы, на которых крутится один и тот же сайт. :

Apapche#1:  
ip: 192.168.10.10
Apapche#2  
ip: 192.168.10.20
Apapche#3  
ip: 192.168.10.30

В любимом текстовом редакторе создаем конфигурационный файл nginx и вносим в него следующие строки. Лично я люблю, когда настройки сайтов хранятся в отдельных файлах.

upstream http {
	server 192.168.10.10 weight=2 max_fails=2 fail_timeout=2s;
	server 192.168.10.20 weight=2 max_fails=2 fail_timeout=2s;
	server 192.168.10.30 weight=2 max_fails=2 fail_timeout=2s;
}
  • weight - определяет значимость сервера в кластере. В данном примере все сервера - одинаковы и могут обслужить одинаковое количество запросов. Если в кластере 1 сервер значительно мощнее остальных - можно указать для него высшее значение этого параметра и NginX будет слать ему больше запросов, чем другим.
  • max_fails - определяет количество неудачных попыток соединения с backend сервером.
  • fail_timeout - промежуток между неудачными соединениями.

В данном примере после 2-х неудачных соединений на протяжении 4-х секунд, сервер будет помечен как недоступный и запросы к нему не будут отправляться, пока NginX не удостоверится, что с сервером все в порядке.

Методы балансировки нагрузки (описываются в начале секции upstream):

  • ip_hash - согласно этому методу запросы от одного и того же клиента будут всегда отправляться на один и тот же backend сервер на основе информации об ip адресе клиента. Не совместим с параметром weight.
  • least_conn - запросы будут отправляться на сервер с наименьшим количеством активных соединений.
  • round-robin - режим по умолчанию. То есть если вы не задали ни один из вышеупомянутых способов балансировки - запросы будут доставляться по очереди на все сервера в равной степени.

Итак сервера с сайтом описали. Дальше нужно сказать NginX’у что с ними делать. Для этого описываем секцию location с параметрами проксирования запросов:

location / {
	proxy_read_timeout 1200;
	proxy_connect_timeout 1200;
	proxy_pass http://http/;
	proxy_set_header Host $host;
	proxy_set_header X-Real-IP $remote_addr;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Итак полный конфиг для кластера выглядит следующим образом:

upstream http {
	server 192.168.10.10:80 weight=2 max_fails=2 fail_timeout=2s;
	server 192.168.10.20:80 weight=2 max_fails=2 fail_timeout=2s;
	server 192.168.10.30:80 weight=2 max_fails=2 fail_timeout=2s;
}

server {

servername mywebsite.com www.mywebsite.com;
listen 80;

location / {
	proxy_read_timeout 1200;
	proxy_connect_timeout 1200;
	proxy_pass http://http/;
	proxy_set_header Host $host;
	proxy_set_header X-Real-IP $remote_addr;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Большой прелестью балансировки нагрузки с помощью NginX является поддержка SSL Termination. То есть защищенные сессии устанавливаются с самим балансировщиком, и от него же клиенты получают ответ. Есть два варианта настройки:

  1. По аналогии с предыдущим примером настраиваем балансировку https сессий. При этом ssl хосты должны быть описаны и на Apache серверах. В этом случае https соединения будут устанавливаться с NginX’ом, дальше он будет формировать новый пакет, устанавливать защищенное соединение с Apache серверами, получать такой же защищенный пакет в ответ, распаковывать его, формировать новый и отправлять конечный результат клиенту в защищенном виде. Довольно трудоемкий процесс, согласитесь. В отношении производительности ооочень ресурсо-затратный.

В этом случае наш конфиг будет выглядеть следующим образом:

upstream https {
	server 192.168.10.10:443 weight=2 max_fails=2 fail_timeout=2s;
	server 192.168.10.20:443 weight=2 max_fails=2 fail_timeout=2s;
	server 192.168.10.30:443 weight=2 max_fails=2 fail_timeout=2s;
}

server {
servername mywebsite.com www.mywebsite.com;
listen 443;

ssl on;
ssl_certificate /etc/nginx/SSL/hostname.pem;
ssl_certificate_key /etc/nginx/SSL/server.key;

location / {
	proxy_read_timeout 1200;
	proxy_connect_timeout 1200;
	proxy_pass https://https/;
	proxy_set_header Host $host;
	proxy_set_header X-Real-IP $remote_addr;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
  1. Настраиваем https хост в NginX’e, описываем http (без S) соединения в upstream списке. Немного лирики: многие CMS системы имеют встроенные проверки наличия безопасных сессий и принудительно переадресовывают клиентов на безопасное соединение на страницах ввода конфиденциальной информации (авторизации пользователей, страницы оплаты услуг). Как правило это производится на основе наличия HTTPS хэдэра со значением on в переменном окружении SERVER. Можно просто добавлять этот хэдэр в запросы к серверам Apache и не создавать лишней нагрузки. Если этот способ не срабатывает - смотрите пункт 1.

В этом случае наш конфиг будет выглядеть следующим образом:

upstream https {
	server 192.168.10.10:80 weight=2 max_fails=2 fail_timeout=2s;
	server 192.168.10.20:80 weight=2 max_fails=2 fail_timeout=2s;
	server 192.168.10.30:80 weight=2 max_fails=2 fail_timeout=2s;
}

server {
servername mywebsite.com www.mywebsite.com;
listen 443;

ssl on;
ssl_certificate /etc/nginx/SSL/hostname.pem;
ssl_certificate_key /etc/nginx/SSL/server.key;

location / {
	proxy_read_timeout 1200;
	proxy_connect_timeout 1200;
	proxy_pass http://https/;
	proxy_set_header Host $host;
	proxy_set_header X-Real-IP $remote_addr;
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	<strong>proxy_set_header HTTPS on;</strong>
}

Еще больше можно помочь нашим Apache серверами и снять с них ненужную нагрузку, раздавая статические файлы с помощью NginX. Для этого все папки со статическим контентом нужно скопировать (сохраняя дерево каталогов) на сам балансировщик нагрузки (я бы разместил в /var/www/html) и описать раздачу из с помощью NginX прямо перед секцией location / в описании каждого хоста:

location ~* \.(ico|gif|jpeg|jpg|png|eot|ttf|swf|woff)$ {
	root /var/www/html;
	expires 30d;
	access_log off;
}
location ~* \.(css|js)$ {
	root /var/www/html;
	expires 1d;
	access_log off;
}

Для того чтобы статический контент нормально обновлялся при загрузке через web, можно примонтировать соответствующие папки на сервера с помощью NFS.

Как-то так.