UNIX
online
pulse
logo
| dzenspace.ru | статьи |

Plesk + Nginx + Apache

Если вы это читаете, то вам уже известно, что панель управления Plesk является "вещью в себе", с которой шаг в сторону считается побегом и подлежит карательным санкциям в виде восстановления всех конфигов в то состояние, которое разработчики плеска считают единственно правильным. Таким образом, смещение веб-сервера Apache на порт отличный от 80-го порта по умолчанию для реализации возможности проксирования запросов через Nginx в условиях часто меняющейся конфигурации является не такой простой задачей, как этого хотелось бы.

Наиболее простой способ обхода данного ограничения можно проиллюстрировать на схеме:

plesk + nginx via firewall

Упрощенно эти шаги можно описать следующим образом:

  • Пакет от клиента приходит на некоторый IP-адрес и 80-й порт сервера;
  • Пакетный фильтр (firewall) подменяет у пакета порт назначения на 8080 и перенаправляет (forwarding) пакет выше по стеку, где его принимает nginx;
  • Nginx обрабатывает пакет и, в случае необходимости, устанавливает соединение с Apache (upstream) на тот же IP-адрес и порт 80;
  • Пакетный фильтр игнорирует пакет пришедший с внутреннего IP-адреса - пакет принимается и обрабатывается Apache.

Теперь продемонстрируем это на практике. Предположим, что у нас на сервере для обслуживания HTTP используются два IP-адреса: 11.22.33.44 и 55.66.77.88. Для nginx будем использовать порт 8080 как на схеме, но никто не мешает использовать любой другой из доступных.

FreeBSD

Для включения перенаправления (forwarding) пакетов в пакетном фильтре ipfw необходимо пересобрать ядро со следующими опциями:

options IPFIREWALL
options LIBALIAS
options IPFIREWALL_FORWARD
options IPFIREWALL_VERBOSE

Возможно, что ваше ядро уже собрано с этими опциями. Подробнее о сборке и установке собственного ядра в FreeBSD можно ознакомиться в документации или обратиться к системному администратору.

Далее, необходимо добавить правила для пакетного фильтра ipfw:

# ipfw add 100 fwd 11.22.33.44,8080 tcp from not me to 11.22.33.44 80
# ipfw add 200 fwd 55.66.77.88,8080 tcp from not me to 55.66.77.88 80

И, при необходимости, сохранить их в файл конфигурации фильтра, чтобы они не сбросились при возможной перезагрузке сервера (/etc/rc.firewall или собственный скрипт конфигурации).

Linux

В Linux для перенаправления пакетов воспользуемся стандартной утилитой iptables:

# iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -d 11.22.33.44 \
-j DNAT --to-destination 11.22.33.44:8080
# iptables -t nat -A PREROUTING -p tcp -m tcp --dport 80 -d 55.66.77.88 \
-j DNAT --to-destination 55.66.77.88:8080

И, при необходимости, сохраним правила при помощи iptables-save или в файле /etc/sysconfig/iptables.

Nginx

В самом простейшем случае конфигурация nginx (не относящиеся к задаче параметры пропущены) будет выглядит как:

http {
   server {
      listen 11.22.33.44:8080;
      location / {
         proxy_pass                       http://11.22.33.44;
         proxy_set_header Host            $host;
         proxy_set_header X-Forwarded-For $remote_addr;
      }
   }
   server {
      listen 55.66.77.88:8080;
      location / {
         proxy_pass                       http://55.66.77.88;
         proxy_set_header Host            $host;
         proxy_set_header X-Forwarded-For $remote_addr;
      }
   }
}

В данном примере nginx просто прозрачно проксирует все соединения на apache, добавляя в заголовки HTTP запросов необходимые значения полей Host для определения имени виртуального хоста и X-Forwarded-For для получения реального IP-адреса клиента на стороне apache вместо локального IP-адреса, с которого nginx устанавливает соединение.

Apache

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

В противном случае, требуется преобразование значения поля X-Forwarded-For из HTTP заголовка в фиктивный IP-адрес клиента (т.к. реальный IP-адрес - это адрес с которого установил соединение nginx). Для решения этой задачи необходима установка модуля mod_rpaf.

Для FreeBSD устанавливаем модуль из портов (порты, естественно, должны быть установлены):

# cd /usr/ports/www/mod_rpaf2
# make install && make clean

Для Linux используется способ установки принятый в вашем дистрибутиве. Например:

$ sudo urpmi --auto apache-mod_rpaf

Далее, необходимо внести изменения в конфигурацию apache:

LoadModule      rpaf_module libexec/apache2/mod_rpaf.so

RPAFenable      On
RPAFsethostname On
RPAFheader      X-Forwarded-For
RPAFproxy_ips   11.22.33.44 55.66.77.88

Здесь приведен пример конфигурации для FreeBSD и Apache 2.0. В других случаях путь или имя модуля в инструкции LoadModule может отличаться.

Настройку можно считать завершенной - запускаем nginx, перезагружаем конфигурацию apache и проверяем работоспособность.

Заключение

На простом проксировании всех запросов через nginx останавливаться не обязательно. Плеск хранит все данные о хостинге в базе данных (обычно она называется `psa`). Путем простого выполнения нескольких запросов к базе данных и небольшого скрипта генерации конфига можно создать более гибкую схему работы - например, выделить отдачу статики минуя apache.

При использовании скрипта автоматической генерации конфигурационного файла nginx возникает проблема его обновления при обновлении данных в plesk. Наиболее простое решение состоит в периодическом пересоздании конфига и его сравнением с текущим - если они отличаются, то произвести замену файла и перезагрузить конфигурацию.

#!/bin/sh

# имя файла конфига
FILE="/usr/local/etc/nginx/nginx.plesk"

# пересоздание конфига
/usr/local/etc/nginx/make_config.sh > "${FILE}.new"

# проверка изменений
DIFF=$(/usr/bin/diff "${FILE}.new" "${FILE}")

if [ -n "${DIFF}" ]
then
   mv -f "${FILE}.new" "${FILE}"
   /usr/local/etc/rc.d/nginx reload
else
   rm -f "${FILE}.new"
fi

Так же может иметь смысл установить параметры ядра:

# FreeBSD
net.inet.tcp.nolocaltimewait = 1

# Linux
net.ipv4.tcp_tw_recycle = 1

Для уменьшения количества локальных tcp-соединений, порождаемых nginx и находящихся в состоянии TIME_WAIT.