Итак, задача – поставить nginx, что бы он работал в качестве фронтэнд-прокси и разгружал apache от медленных коннектов.
Цель – nginx обрабатывает все запросы, при этом он перенаправляет запросы apache и позволяет не висеть по долгу тяжелым процессам apache, даже если клиент медленный, поскольку мгновенно забирает ответ apache и отдает его клиенту уже из собственного буфера.
Задача осложняется тем, что сделать это нужно на сервере с панелью управления Cpanel, которая генерирует httpd.conf каждый раз при изменениях на сервере (например, при добавлении сабдомена в панели) и совершенно ничего не знает о конфиге nginx.
Можно повесить apache на 81 порт, например, а nginx на 80 и дальше проксировать все запросы от nginx апачу, но тогда придется научить Cpanel генерировать httpd.conf с настройкой на 81 порт. Это не сложно – достаточно поправить темплайт виртуалхоста Cpanel, но есть риск потери совместимости, поэтому я избрал другой путь – более универсальный и менее рискованный.
Итак, поехали:
Допустим apache уже установлен и настроен. Если это не так – установите и настройте, это не тема данной статьи.
Апач есть.
Ставим старую версию nginx (ниже, предоставлен также вариант установки новой версии):
Офсайт:
http://sysoev.ru
Качаем отсюда:
http://sysoev.ru/nginx/download.html
Вперед:
cd /usr/local/src
yum install gcc -y
wget http://sysoev.ru/nginx/nginx-0.6.32.tar.gz
tar -zxvf nginx-0.6.32.tar.gz
cd nginx-0.6.32
./configure --prefix=/usr/local/nginx --without-http_charset_module --without-http_ssi_module --without-http_userid_module --without-http_access_module --without-http_auth_basic_module --without-http_empty_gif_module --without-http_gzip_module --without-http_rewrite_module --without-pcre
make
make install
nginx поставили.
Теперь конфигурируем nginx:
Чаще всего конфиг находится тут:
/usr/local/nginx/conf/nginx.conf
ну в любом случае найдете.
Допустим IP вашего сервера 123.456.654.321
Тогда примерно так:
worker_processes 3;
events {
worker_connections 1024;
use epoll; # типа быстрее других
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
reset_timedout_connection on; # не все клиенты умеют закрывать соединения...
client_body_buffer_size 256k;
limit_zone lconn $binary_remote_addr 10m;
client_max_body_size 10m;
proxy_buffers 8 16k;
proxy_buffer_size 32k;
large_client_header_buffers 8 32k;
server {
listen 123.456.654.321:85;
access_log off;
limit_conn lconn 7;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://123.456.654.321:80;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
Примечание:
В новых версиях nginx, а так же в отдельных ситуациях, конфиг может отличаться, например:
listen 85;
Т.е. указать только порт, вместо IP-адреса – помогает при проблемах запуска на VPS с OVZ.
Так же
в секцию httpd:
limit_req_zone $binary_remote_addr zone=one:30m rate=4r/s;
в секцию server (location /)
limit_req zone=one burst=6;
Для лимитирования, вместо
limit_zone lconn $binary_remote_addr 5m;
limit_conn lconn 7;
Однако, если вы ставили указанную версию nginx то проблем с конфигом быть не должно.
Установка и конфиг новой версии:
Установка:
cd /usr/local/src
yum install gcc -y
wget http://nginx.org/download/nginx-1.6.2.tar.gz
tar -zxvf nginx-1.6.2.tar.gz
cd nginx-1.6.2
./configure --prefix=/usr/local/nginx --without-http_charset_module --without-http_ssi_module --without-http_userid_module --without-http_access_module --without-http_auth_basic_module --without-http_empty_gif_module --without-http_gzip_module --without-http_rewrite_module --without-pcre
make
make install
#для кеширования:
mkdir /var/cache/nginx
chown root:nobody /var/cache/nginx
chmod g+w /var/cache/nginx
chmod g+r /var/cache/nginx
Конфиг с кешированием:
worker_processes 3;
events {
worker_connections 1024;
use epoll; # типа быстрее других
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
reset_timedout_connection on; # не все клиенты умеют закрывать соединения...
client_body_buffer_size 256k;
limit_req_zone $binary_remote_addr zone=one:30m rate=7r/s;
client_max_body_size 10m;
proxy_buffers 8 16k;
proxy_buffer_size 32k;
large_client_header_buffers 8 32k;
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=static_cache:100m inactive=150m max_size=1G; #кеш
proxy_cache_min_uses 1; #кеш
server {
listen 123.456.654.321:85;
access_log off;
limit_req zone=one burst=18;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache static_cache; #кеш
proxy_cache_key "$request_method|$http_if_modified_since|$http_if_none_match|$host|$request_uri"; #кеш
proxy_cache_valid 7m; #кеш
proxy_cache_valid 404 1m; #кеш
proxy_hide_header "Set-Cookie"; #кеш
proxy_pass http://123.456.654.321:80;
}
#запретить кеширование админки WP (пример):
#если кеширование не используется - удалите два блока ниже
location /wp-login.php {
add_header Pragma "no-cache";
add_header Last-Modified $sent_http_Expires;
add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
expires -1;
proxy_cache off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://123.456.654.321:80;
}
location /wp-admin/ {
add_header Pragma "no-cache";
add_header Last-Modified $sent_http_Expires;
add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
expires -1;
proxy_cache off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://123.456.654.321:80;
}
#запретили кеширование
#если кеширование не используется - удалите два блока ВЫШЕ
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
Для того, чтобы отключить кеширование – достаточно удалить из конгфига строки заканчивающиеся на #кеш
Этого конфига в принципе достаточно. Заметьте – мы повесили nginx на 85-ый порт, apache мы оставляем на 80-ом, это позволит сохранить максимальную совместимость в самых разных, нестандартных ситуациях.
Стартуем nginx:
/usr/local/nginx/sbin/nginx
Проверяем:
telnet 123.456.654.321 85
Если соединяется и не пишет, что connectiob refused – значит nginx нормально запустился и слушает 85-ый порт.
Теперь, с помощью iptables перенаправляем трафик с 80 порта нашего IP на 85, т.е. перенаправляем трафик на nginx, откуда он уже будет отдавать его apache на 80 порт.
iptables -t nat -A PREROUTING -p tcp -m tcp -d 123.456.654.321 --dport 80 -j DNAT --to-destination 123.456.654.321:85
Для этого необходимо иметь iptables и поддержку NAT в нем.
После этого, желательно сохранить конфигурацию iptables, что бы при рестартах данное перенаправление не терялось:
iptables-save > /etc/sysconfig/iptables
Внимание, если на сервере установлен apf то вместо того, что бы выполнять эту команду в терминале, открываем файл /etc/apf/preroute.rules и вбиваем эту строчку туда, после чего рестартуем apf apf -r
Осталось установить модуль апача mod_rpaf:
Качать отсюда:
http://stderr.net/apache/rpaf/download/
Поехали:
cd /usr/local/src
wget http://stderr.net/apache/rpaf/download/mod_rpaf-0.6.tar.gz
tar -zxvf mod_rpaf-0.6.tar.gz
cd mod_rpaf-0.6
apxs -i -c -n mod_rpaf-2.0.so mod_rpaf-2.0.c
После этого модуль должен появиться в директории модулей apache, например тут:
/usr/lib/httpd/modules/mod_rpaf-2.0.so
Остается прописать включение этого модуля в конфиге apache:
LoadModule rpaf_module modules/mod_rpaf-2.0.so
RPAFenable On
RPAFsethostname On
RPAFproxy_ips 123.456.654.321
Если несколько IP – их можно вбивать в строке RPAFproxy_ips через пробел.
Это можно сделать прямо в httpd.conf, но я для этого создал файл /etc/httpd/conf.d/rpaf-2.0.conf и прописал в нем.
Вместо rpaf будем использовать более эффективный модуль realip:
cd /usr/local/src
wget http://man-linux.ru/files/realip_c.tar.gz
tar -zxvf realip_c.tar.gz
cd realip_c
echo 'LoadModule realip_module modules/mod_realip.so' >> /etc/httpd/conf.d/reailip.conf
#убедимся, что все в порядке
httpd -t
#перезагрузим апач
service httpd restart
Здесь представлена максимально облегченная версия для Apache 2 которая не требует дополнительных настроек – достаточно установить модуль.
На серверах с Cpanel, возможно понадобится прописывать в другом файле, поскольку Cpanel сама генерит httpd.conf из собственных темплейтов, но в ее httpd.conf есть include несколько файлов конфигов по умолчанию.
Ну и напоследок, полезная инфа:
Управление nginx:
Управлять nginx можно с помощью сигналов. Номер главного процесса по умолчанию записывается в файл /usr/local/nginx/logs/nginx.pid. Изменить имя этого файла можно при конфигурации сборки или же в nginx.conf директивой pid. Главный процесс поддерживает следующие сигналы:
TERM, INT
– быстрое завершениеQUIT
– плавное завершениеHUP
– изменение конфигурации, обновление изменившейся временной зоны (только для FreeBSD и Linux), запуск новых рабочих процессов с новой конфигурацией, плавное завершение старых рабочих процессовUSR1
– переоткрытие лог-файловUSR2
– обновление исполняемого файлаWINCH
– плавное завершение рабочих процессов
Управлять рабочими процессами по отдельности не нужно. Тем не менее, они тоже поддерживают некоторые сигналы:TERM, INT
– быстрое завершениеQUIT
– плавное завершениеUSR1
– переоткрытие лог-файлов
Например, команда для перезапуска nginx может выглядеть так:
kill -HUP `cat /usr/local/etc/nginx/logs/nginx.pid`
Что еще. Для мониторинга nginx написал скрипт, который проверяет его состояние и если нужно перезапускает, а так же может высылать уведомление админу. Cкрипт конечно не идеальный, сделан топориком на коленке, но работает:
#!/bin/bash
email='mail@mail.ru'
if [[ -e /usr/local/nginx/logs/nginx.pid ]]
then
pid=`cat /usr/local/nginx/logs/nginx.pid`
hn=`hostname`
if [[ $1 ]]
then
if [[ $1 == 'start' ]]
then
/usr/local/nginx/sbin/nginx
echo 'nginx started'
fi
if [[ $1 == 'restart' ]]
then
kill -HUP $pid
echo 'nginx restarted'
fi
if [[ $1 == 'stop' ]]
then
kill -QUIT $pid
echo 'nginx stoped'
fi
else
if ! ps $pid > /dev/null > /dev/null;
then
/usr/local/nginx/sbin/nginx > /dev/null
# echo "nginx on $hn was DOWN, restarted " `date` | mail -s 'nginx down' $email
else
echo 'nginx UP and have PID:'
echo $pid
fi
fi
else
/usr/local/nginx/sbin/nginx > /dev/null
#echo "nginx on $hn was DOWN, restarted " `date` | mail -s 'nginx down' $email
fi
Обратите внимание на пути – возможно их придется подредактировать. Если надо, что бы скрипт отсылал емайлы при падениях nginx – нужно расскомментировать две строчки (32 и 40) а также, в начале скрипта указать нужный email, ну и команда mail должна в системе поддерживаться.
Если скрипт вызвать без параметров – он проверяет состояние nginx и в случае необходимости запускает его.
Так же поддерживаются параметры start|stop|restart выполняющие соответсвующие действия.
Достаточно поставить скрипт без параметров на крон, например раз в три минуты – он будет раз в три минуты проверять состояние nginx и в случае необходимости запустит его.
Полезные параметры конфигурации:
Например, ограничить число коннектов к одному сайту 100 и с одного IP 10:
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server {
...
limit_conn perip 10;
limit_conn perserver 100;
}
А вообще, море полезной информации можно найти на официальном сайте.
Удачи!
Автор: Виктор Симон
В конфиге nginxа, сдается мне неверно указан ip в директивах
listen, proxy_pass
Насколько я понимаю, это решение будет работать если нет виртуальных name-based хостов у апача, ведь их тоже надо описаывать в конфиге ngnix?
Вообще что-то странное получается. Если nginx слушает на 85 порту ip1, мы роутим на него трафик приходящий на ip1:80, а nginx дальше обращается к ip1:80, то мы получаем кольцо? как быть?
> В конфиге nginxа, сдается мне неверно указан ip в директивах
> listen, proxy_pass
Да, при редактировани статьи остался IP с одного из моих серверов, поправил :)
> Насколько я понимаю, это решение будет работать если нет виртуальных name-based хостов у апача, ведь > их тоже надо описаывать в конфиге ngnix?
В том то и дело, что nginx работает ровно как прокси и ничего иного для сайтов висящих на данном конкретном IP описывать не надо – апач отдает nginx’у, он уже отдает клиенту.
> Вообще что-то странное получается. Если nginx слушает на 85 порту ip1, мы роутим на него трафик приходящий на ip1:80, а nginx дальше обращается к ip1:80, то мы получаем кольцо? как быть
Кольца не получается, поскольку запрос на 80 порт идущий из “вне” с помощью iptables переадресуется прямиком на nginx (85 порт), тот же в свою очередь запрашивает данные уже локально с того же 80 порта и поскольку данные запрашиваются локально iptables не переадресует их – кольца не получается и схема отлично работает.
Спасибо. Даже не знал, что так можно. Хе хе, чёткий сайт! Продолжайте в том же духе =)
Еще вариант конфигурации, по прежнему не перегруженный, но позволяющий использовать больше возможностей:
./configure –prefix=/usr/local/nginx –without-http_charset_module –without-http_ssi_module –without-http_userid_module –without-http_access_module –without-http_auth_basic_module –without-http_empty_gif_module –with-pcre
Остальное по вкусу.