Как я nginx ставил и вместе с apache и Cpanel работать заставил

Итак, задача – поставить 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;
}

А вообще, море полезной информации можно найти на официальном сайте.
Удачи!


Автор: Виктор Симон
Написано в 2008 году.
www.man-linux.ru
Права копирования
Опубликовано 17 сентября 2008 - информация могла устареть.

комментариев 6

  1. Вообще что-то странное получается. Если nginx слушает на 85 порту ip1, мы роутим на него трафик приходящий на ip1:80, а nginx дальше обращается к ip1:80, то мы получаем кольцо? как быть?

  2. > В конфиге 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 не переадресует их – кольца не получается и схема отлично работает.

  3. Еще вариант конфигурации, по прежнему не перегруженный, но позволяющий использовать больше возможностей:

    ./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

    Остальное по вкусу.

Ответить