Итак, задача — поставить 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
Комментарий by Anton Shevtsov — 19 августа 2009 @ 9:43
Насколько я понимаю, это решение будет работать если нет виртуальных name-based хостов у апача, ведь их тоже надо описаывать в конфиге ngnix?
Комментарий by Anton Shevtsov — 26 августа 2009 @ 13:36
Вообще что-то странное получается. Если nginx слушает на 85 порту ip1, мы роутим на него трафик приходящий на ip1:80, а nginx дальше обращается к ip1:80, то мы получаем кольцо? как быть?
Комментарий by ivanko — 11 сентября 2009 @ 12:56
> В конфиге 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 не переадресует их — кольца не получается и схема отлично работает.
Комментарий by admin — 21 сентября 2009 @ 21:05
Спасибо. Даже не знал, что так можно. Хе хе, чёткий сайт! Продолжайте в том же духе =)
Комментарий by avsv — 22 апреля 2011 @ 15:30
Еще вариант конфигурации, по прежнему не перегруженный, но позволяющий использовать больше возможностей:
./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
Остальное по вкусу.
Комментарий by admin — 9 июля 2015 @ 19:25