| « Hirahara Ayaka | Мой пушистый рыжий друг » |
Совсем недавно я писал о патче, позволяющем передавать реальный IP от апача 2.2 на бэкенд. Патч не совсем соответствует идеологии Apache - однако работает, и вполне может быть использован (прошел стресс-тест на нагруженном хосте, на котором, в частности, висит этот блог).
Но патч - это конечно хорошо, а вот как его использовать в реальной ситуации? Попробую описать, с примерами конфигов.
Начинаем с того, что собираем два апача. Один будет фронтендом (не забудьте его пропатчить), другой - бэкендом (тут уже опционально - можно и не патчить, если боитесь злобных багов, хотя их в указанной конфигурации не наблюдалось).
Конфигурационные строки в моем случае таковы:
Frontend:
./configure --prefix=/usr/httpd-frontend --with-mpm=worker \
--enable-actions \
--enable-alias \
--enable-auth-basic --enable-auth-digest \
--enable-authn-anon --enable-authn-dbd --enable-authn-dbm --enable-authn-default --enable-authn-file \
--enable-authz-dbm --enable-authz-default --enable-authz-groupfile --enable-authz-host --enable-authz-owner --enable-authz-user \
--enable-autoindex \
--enable-cern-meta \
--enable-charset-lite \
--enable-dir \
--enable-dumpio \
--enable-env \
--enable-expires \
--enable-ext-filter \
--enable-filter \
--enable-headers \
--enable-imagemap \
--enable-include \
--enable-info \
--enable-log-config --enable-log-forensic --enable-logio \
--enable-mime \
--enable-negotiation \
--enable-nonportable-atomics=yes \
--enable-rewrite \
--enable-setenvif \
--enable-static-support \
--enable-status \
--enable-version \
--enable-vhost-alias \
\
--enable-proxy --enable-proxy-http --enable-proxy-balancer
Backend:
./configure --prefix=/usr/httpd --with-mpm=prefork \
--enable-actions \
--enable-alias \
--enable-auth-basic --enable-auth-digest \
--enable-authn-anon --enable-authn-dbd --enable-authn-dbm --enable-authn-default --enable-authn-file \
--enable-authz-dbm --enable-authz-default --enable-authz-groupfile --enable-authz-host --enable-authz-owner --enable-authz-user \
--enable-autoindex \
--enable-cern-meta \
--enable-charset-lite \
--enable-dir \
--enable-dumpio \
--enable-env \
--enable-expires \
--enable-ext-filter \
--enable-filter \
--enable-headers \
--enable-imagemap \
--enable-include \
--enable-info \
--enable-log-config --enable-log-forensic --enable-logio \
--enable-mime \
--enable-negotiation \
--enable-nonportable-atomics=yes \
--enable-rewrite \
--enable-setenvif \
--enable-static-support \
--enable-status \
--enable-version \
--enable-vhost-alias \
\
--enable-cgi --enable-cgid \
--enable-ssl \
--enable-suexec --with-suexec-caller=www --with-suexec-docroot=/www --with-suexec-gidmin=60000 --with-suexec-uidmin=60000
Как видим, наборы модулей - похожи, но во фронте присутствует mod_proxy, а на backend - mod_cgi, mod_ssl и suexec. Кроме того, на frontend стоит mpm_worker, который использовать для запуска CGI или постановки PHP модулем весьма неудобно, а на backend - mpm_prefork, который очень удачный в плане запуска/изоляции CGI, и легко уживается с PHP в виде модуля, однако форкает множество процессов при отдаче большого количества статики.
После того, как мы собрали апач с такими параметрами, пришло время его настроить. Конфигурационные файлы я разбиваю на две секции. Одна - common - общая для всего сервера. Другая - для каждого vhost находится в отдельном файле. В случае с frontend это именно так, и сервер использует include для включения конфигов vhost'ов в основной конфиг. В случае backend все немного хитрее - backend'ы (которых один на vhost) стартуют каждый со своим конфигом, который уже и подключает к себе общий.
Итак, общие конфиги:
Frontend:
ServerRoot "/usr/httpd-frontend"
TimeOut 300
KeepAlive On
# worker (не забудьте потвикать под вашу конкретную конфигурацию)
StartServers 1
ServerLimit 64
MaxClients 2048
MinSpareThreads 16
MaxSpareThreads 128
MaxRequestsPerChild 4096
ThreadsPerChild 32
ThreadLimit 128
ThreadStackSize 131072
# common server options
UseCanonicalName Off
ServerSignature On
# как видим, для фронтенда я использую совершенно отдельную юзергруппу
User www
Group www
Listen 80
PidFile /var/run/httpd-frontend.pid
<FilesMatch "^\.ht">
Order allow,deny
Deny from all
Satisfy All
</FilesMatch>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%t %a->%A \"%r\" %>s %D %O" common
LogFormat "%h %l %u %t \"%r\" %>s %b" default
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
DefaultType text/plain
HostNameLookups Off
ExtendedStatus On
LogLevel warn
RewriteEngine On
# logging (я использую cronolog, ваш случай может отличаться)
ErrorLog "|/usr/sbin/cronolog -l /www/logs/frontend_error_log /www/logs/frontend_error_log.%Y%m%d"
# RealIP frontend
RequestHeader set X-Real-IP %a early
# proxy
ProxyIOBufferSize 65536
ProxyMaxForwards 15
ProxyPreserveHost On
ProxyStatus Full
NameVirtualHost *:80
# virtual hosts
Include /etc/httpd/vhosts-frontend/*.conf
Backend:
ServerRoot "/usr/httpd"
TimeOut 60
KeepAlive On
# prefork (не забудьте потвикать под вашу конкретную конфигурацию)
# возможно часть параметров есть смысл вынести в хостовые конфиги, если они отличаются между хостами
StartServers 1
ServerLimit 128
MaxClients 128
MaxRequestsPerChild 4096
MaxSpareServers 4
MinSpareServers 1
UseCanonicalName Off
ServerSignature On
# а вот здесь мы загружаем PHP и mod_realip2
LoadModule php5_module /www/PHP/dso/libphp5.so
LoadModule realip2_module modules/mod_realip2.so
# RealIP - получаем с фронтенда истинный IP клиента
RealIP On
RealIPHeader X-Real-IP
RealIPProxy 127.0.0.1
<FilesMatch "^\.ht">
Order allow,deny
Deny from all
Satisfy All
</FilesMatch>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%t %a->%A \"%r\" %>s %D %O" common
LogFormat "%h %l %u %t \"%r\" %>s %b" default
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
DefaultType text/plain
HostNameLookups Off
ExtendedStatus On
LogLevel warn
RewriteEngine On
# PHP
<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>
Как видим - вполне обычные конфиги. На frontend вообще ничего необычного, разве что
RequestHeader set X-Real-IP %a early
да
ProxyPreserveHost On
заслуживают особого внимания.
На бэкенде немножко хитрее - там загружаются PHP и mod_realip2. Соответственно присутствует конфигурация RealIP2, ну и ассоциация .php с обработчиком. Не забудьте
UseCanonicalName Off
на бэкенде, иначе он будет вам подставлять основное имя хоста, а не тот вхост, который пришел с клиента.
Итак, общие конфиги готовы, а теперь самое вкусное - конфиги вхостов.
Frontend:
<VirtualHost *:80>
# primary name
ServerName ff.net13.info
# names
ServerAlias forum.net13.info
ServerAlias freeforum.net13.info
# www thingy
ServerAlias www.ff.net13.info
ServerAlias www.forum.net13.info
ServerAlias www.freeforum.net13.info
DocumentRoot /www/freeforum/html
# Logging - опять же, я использую cronolog, ваша конфигурация может отличаться
CustomLog "|/usr/sbin/cronolog -l /www/freeforum/logs/access_log /www/freeforum/logs/access_log.%Y%m%d" common
ErrorLog "|/usr/sbin/cronolog -l /www/freeforum/logs/error_frontend_log /www/freeforum/logs/error_frontend_log.%Y%m%d"
# Proxy - это самое интересное, здесь перенаправляется трафик на бэкенд для .php
ProxyPassMatch /(.*\.php)$ http://127.0.0.1:8007/$1 flushpackets=auto flushwait=1 keepalive=On disablereuse=On retry=5
# Static
<Directory /www/freeforum/html>
Allow from all
AllowOverride all
Options Includes FollowSymLinks SymLinksIfOwnerMatch
DirectoryIndex index.php index.htm index.html default.htm default.html
</Directory>
</VirtualHost>
Backend:
# no virtual hosts here because we tend to use 1 Apache per 1 Host
ServerAdmin alex@net13.info
# primary name - имя всего одно, поскольку вхостов нет
ServerName ff.net13.info
# common - подключаем общий конфиг
Include "/etc/httpd/common.conf"
# server-based
PidFile /var/run/httpd-freeforum.pid
# listen port, one per host/user - это порт бэкенда, мы его видели в конфиге фронтенда, в ProxyMatch
Listen 8007
# и снова логи. access лог здесь писать смысла нет - его пишет frontend
ErrorLog "|/usr/sbin/cronolog -l /www/freeforum/logs/error_backend_log /www/freeforum/logs/error_backend_log.%Y%m%d"
# как видим - я использую для клиента свою, отдельную юзергруппу
User freeforum
Group freeforum
SuexecUserGroup freeforum freeforum
DocumentRoot /www/freeforum/html
<Directory /www/freeforum/html>
Allow from all
AllowOverride all
Options Includes FollowSymLinks SymLinksIfOwnerMatch ExecCGI
DirectoryIndex index.php index.htm index.html default.htm default.html
</Directory>
# PHP
PHPIniDir /www/freeforum/etc/php.ini
И опять - почти ничего необычного. Конфиг бэкенда вообще линейный, даже отметить нечего, все стандартное.
С фронтендом чуть хитрее - особый интерес представляет директива
ProxyPassMatch /(.*\.php)$ http://127.0.0.1:8007/$1 flushpackets=auto flushwait=1 keepalive=On disablereuse=On retry=5
Ну с хостом, портом, регэкспом и паттерном все ясно.
flushpackets и flushwait дают нам возможность отдавать клиенту контент без долговременной буферизации - удобно для файлокачалок с проверкой статуса и прочих требующих поддержки flush скриптов. keepalive - он удобен сам по себе (в случае с nginx у вас такого счастья не будет). retry=5 - тоже очевидный параметр.
А вот disablereuse - очень важный момент. Он запрещает использовать уже открытые соединения к бэкенду для других клиентов. Т.е. слегка ограничивает keepalive - если клиент закроет keepalive-соединение, соединение к бэкенду тоже закроется.
В чем здесь фокус. В том, что IP клиента для X-Real-IP определяется ОДИН РАЗ, при открытии соединения к бэкенду. И если мы разрешим использовать соединения к бэкенду для других клиентов - их запросы в X-Real-IP будут показывать ЧУЖОЙ IP - тот, от кого было первый раз открыто конкретное соединение.
Будьте осторожны - этот момент доставил мне немного проблем в начале настройки.
Ну, надеюсь, со всем разобрались. Успешного хостинга ![]()