Прочел я недавно, что Apache в режиме FastCGI работает гораздо быстрее CGI. Работа сервера в режиме FastCGI не сильно отличается от обычного режима, с одной лишь разницей - процессы не создаются каждый раз при обращении к скриптам, а остаются в памяти, поэтому один процесс может обработать множество запросов, в связи с чем уменьшается отклик сервера. Так вот решил я настроить подобную связку. На сервере установлена ОС FreeBSD 7.3. Ставить все будем на чистую систему, если установлены старые версии пакетов, то просто удалите их.
Итак, лезем в порты ставить php, доступная на данный момент версия 5.2.14. Версию 5.3 не хочу, так как разработчики поубирали из нее много старых функций и не весь софт ее еще держит.
# cd /usr/ports/lang/php52
# make install clean
В окне выбора параметров компиляции я выбрал следующие:
- CLI
- CGI
- REDIRECT
- DISCARD
- FASTCGI
- PATHINFO
После установки php ставим apache. Вначале укажем опции сборки для апача. В опциях я указал в роли обрабатывающего соединения модуля, модуль worker. В данной конфигурации данный модуль будет эффективнее стандартного, потому что PHP будет работать в виде отдельного независимого процесса (а не в виде модуля apache), поэтому на апач будет возложена задача отдачи только статического контента. А модуль worker с этой задачей справляется лучше (лучше, надо понимать здесь как быстрее), да и памяти съедает гораздо меньше.
Правим /etc/make.conf :
# cat /etc/make.conf
PORTSDIR?=/usr/ports
.if ${.CURDIR} == ${PORTSDIR}/www/apache20
WITH_MPM=worker
WITH_SUEXEC=yes
SUEXEC_DOCROOT="/usr/home"
#SUEXEC_USERDIR="www"
WITH_AUTH_MODULES=yes
WITH_DAV_MODULES=yes
WITH_MISC_MODULES=yes
WITH_SSL_MODULES=yes
WITH_KQUEUE_SUPPORT=yes
WITH_EXCEPTION_HOOK=yes
.endif
Теперь ставим апач.
# cd /usr/ports/www/apache20
# make install clean
После завершения установки так же надо поставить модуль FastCGI, в портах их несколько - mod_fcgid и mod_fastcgi, я выбрал mod_fcgid. Мой выбор пал на него, так как данный модуль позволяет индивидуально для каждого виртуального хоста установить минимальное/максимальное количество запущенных экземляров FastCGI приложения, в принципе большинство параметров можно задать индивидуально (в сравнении с mod_fastcgi). В тоже время mod_fastcgi умеет работать со встроенным в PHP менеджером процессов, а mod_fcgid не умеет (смотрите переменную окружения PHP_FCGI_CHILDREN в документации). Такое поведение mod_fastcgi позволяет PHP работать с различными акселераторами. В случае же mod_fcgid акселераторы менее эффективны, потому что для каждого экземляра FastCGI приложения (в данном случае процесс PHP) будет создаваться свой разделяемый сегмент памяти.
Port: ap20-mod_fcgid-2.3.5
Path: /usr/ports/www/mod_fcgid
Info: An alternative FastCGI module for Apache2
Maint: hemi@puresimplicity.net
B-deps: apache-2.0.63_15 apr-ipv6-devrandom-gdbm-db42-0.9.18.0.9.17 db42-4.2.52_5 expat-2.0.1_1 gdbm-1.8.3_3 libiconv-1.13.1_1 pcre-8.10 perl-5.10.1_2
R-deps: apache-2.0.63_15 apr-ipv6-devrandom-gdbm-db42-0.9.18.0.9.17 db42-4.2.52_5 expat-2.0.1_1 gdbm-1.8.3_3 libiconv-1.13.1_1 pcre-8.10 perl-5.10.1_2
WWW: http://httpd.apache.org/mod_fcgid/
Ставим:
# cd /usr/ports/www/mod_fcgid
# make install clean
Если вам больше нравится модуль mod_fastcgi, то установите его (далее будет пример конфигурации с этим модулем). Установить можно так:
# cd /usr/ports/www/mod_fastcgi
# make install clean
После окончания установки лезем править конфиг apache. Нужно подключить установленный модуль, для этого добавляем следующую строку:
LoadModule fcgid_module libexec/apache2/mod_fcgid.so
# или
LoadModule fastcgi_module libexec/apache2/mod_fastcgi.so
Далее пишем настройки для модуля:
<IfModule mod_fcgid.c>
FcgidMaxProcesses 30
FcgidMaxProcessesPerClass 5
FcgidMinProcessesPerClass 1
FcgidIdleTimeout 100
FcgidMaxRequestLen 1048576
FcgidMaxRequestsPerProcess 300
FcgidProcessLifetime 1800
FcgidFixPathinfo 1
FcgidIOTimeout 180
FcgidIPCDir /var/run/fcgidsock
FcgidProcessTableFile /var/run/fcgid_shm
FcgidPassHeader Authorization
FcgidPassHeader Proxy-Authorization
FcgidPassHeader HTTP_AUTHORIZATION
#FcgidWrapper /usr/local/sbin/suexec
<Location /php-bin/>
SetHandler fcgid-script
Options ExecCGI
AllowOverride None
Order allow,deny
Allow from all
</Location>
</IfModule>
или
<IfModule mod_fastcgi.c>
FastCgiConfig -autoUpdate \
-singleThreshold 10 -multiThreshold 30 \
-idle-timeout 30 -killInterval 150 \
-min-server-life 15 -minProcesses 1 \
-maxClassProcesses 5 -maxProcesses 50 \
-pass-header Authorization \
-pass-header Proxy-Authorization \
-pass-header HTTP_AUTHORIZATION
FastCgiIpcDir /var/run/fastcgi
FastCgiWrapper /usr/local/sbin/suexec
<Location /php-bin/>
SetHandler fastcgi-script
Options ExecCGI
AllowOverride None
Order allow,deny
Allow from all
</Location>
</IfModule>
Прокомментирую опции для модулей:
mod_fcgid:
- FcgidFixPathinfo - должно быть 1, если в php.ini параметр cgi.fix_pathinfo выставлен в 1. В большинстве случаев включение данного параметра необходимо для правильной работы софта. Существует мнение, что установка данного параметра в 1 небезопасна, из-за возможной подстановки на исполнение вредоносного кода. В сети много дискуссий на эту тему. Если хотите разобраться, то ищите по ключевой фразе cgi.fix_pathinfo security.
- FcgidIOTimeout - максимальное время ожидание ответа от FastCGI приложения.
- FcgidMaxProcesses - максимальное количество процессов FastCGI.
- FcgidMaxProcessesPerClass - максимальное количество процессов на программу FastCGI.
- FcgidMinProcessesPerClass - минимальное количество процессов на программу FastCGI.
- FcgidIdleTimeout - если процесс не обрабатывал запросы в течении указанного здесь времени в секундах и текущее количество процессов на программу больше, чем FcgidMinProcessesPerClass, то он прибьется.
- FcgidMaxRequestLen - максимальный размер запроса HTTP.
- FcgidMaxRequestsPerProcess - приложение FastCGI будет уничтожено, после того как обработает указанное здесь количество запросов.
- FcgidPassHeader - запрос, который передается в переменные окружения.
- FcgidProcessLifeTime - максимальное время жизни процесса FastCGI.
mod_fastcgi:
- autoUpdate - заставляет модуль проверять время модификации приложений на диске перед каждым запросом. Если приложение было изменено, то менеджер процессов оповестит все запущенные процессы связанные с приложением. Могут возникнуть проблемы при одновременном использовании с ключом -restart.
- idle-timeout - время простоя приложения перед принудительным завершением и записи об этом в лог файл.
- killInterval - время, по истечению которого менеджер будет прибивать процессы.
- minProcesses - минимальное количество процессов, запущенных одновременно.
- maxClassProcesses - максимальное количество процессов для одной FastCGI программы.
- maxProcesses - максимальное количество процессов FastCGI.
- pass-header - пропускать указанные заголовки HTTP в переменные окружения cgi приложения.
- restart - заставляет менеджер процессов перезапускать приложения после сбоев.
- singleThreshold - целое число между 0 и 100, задающее минимальный порог загрузки для последнего экземпляра fastcgi приложения, если текущая загрузка приложения ниже этого порога, то менеджер процессов прибьет это приложение.
- multiThreshold - целое число между 0 и 100, задающее минимальный порог загрузки для fastcgi приложений, если нагрузка ниже заданного порога, то менеджер процессов прибьет лишние экземпляры процесса, оставив только один.
Вот пример виртуального хоста:
<VirtualHost *:80>
SuexecUserGroup chihpih chihpih
DocumentRoot /home/chihpih/data/www/chihpih.no-ip.org
DirectoryIndex index.php index.html
ServerName chihpih.no-ip.org
ServerAdmin webmaster@chihpih.no-ip.org
ErrorLog /home/chihpih/data/log/error.log
#CustomLog /home/chihpih/data/log/access.log common
<IfModule mod_fcgid.c>
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps
Action application/x-httpd-php /php-bin/php.sh
Alias /php-bin/ "/home/chihpih/data/php-bin/"
</IfModule>
<IfModule mod_fastcgi.c>
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps
Action application/x-httpd-php /php-bin/php.sh
Alias /php-bin/ "/home/chihpih/data/php-bin/"
</IfModule>
<Directory "/home/chihpih/data/www/chihpih.no-ip.org/">
Options FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
Обновление от 18.12.2014г.
Хотелось бы отметить следующий момент по связке Apache 2.2 и mod_fcgid: при включенной опции daily_clean_tmps_enable в файле /etc/periodic.conf, после очередной чистки директории с временными файлами (/tmp), в логах появляется следующая ошибка — No such file or directory: mod_fcgid: apr_global_mutex_child_init error, которая исправляется перезапуском демона httpd. Возникает она по одной простой причине — модуль mod_fcgid собирается в FreeBSD с реализацией мьютексов (mutex) через "файлы". Естественно такие файлы создаются в директории с временными файлам (/tmp). После очередной чистки директории /tmp данные файлы удаляются и возникает вышеописанная ситуация. Решений здесь несколько:
- если используется apache 2.4, тогда возможно через переменную Mutex задать другой режим блокировки, не через "файлы";
- вручную, при сборке модуля mod_fcigd, задать в файле fcgid_mutex_unix.c режим блокировки по умолчанию (см. строку apr_lockmech_e mechanism = APR_LOCK_DEFAULT;);
- ну и банально — отключить автоматическую чистку директории /tmp.
После настройки апача, еще нужно создать иерархию директорий для работы сайта в домашней папке пользователя. Создадим пользователя и группу chihpih, домашняя папка у него будет /home/chihpih с правами 0750. Чтобы апач мог получить к этой директории доступ нужно добавить пользователя www в группу пользователя или можно воспользоваться ACL,ками, как сделал я.
Создаем необходимые папки в домашней директории и даем необходимые права пользователю www:
# cd /home/chihpih/data
# mkdir log tmp www php-bin
# chown www:www log
# chown chihpih:chihpih tmp www php-bin
# chmod 0755 log
# chmod 0750 tmp php-bin
# chmod 0751 www
# setfacl -m u:www:x /home/chihpih
# setfacl -m u:www:x /home/chihpih/data
# setfacl -m u:www:rx /home/chihpih/data/php-bin
# setfacl -m u::rwx,g::rx,o::---,u:www:rx /home/chihpih/data/www
# setfacl -d -m u::rwx,g::rx,o::---,u:www:rx /home/chihpih/data/www
В папке log будут храниться логи виртуальных хостов, в tmp временные файлы для php, в php-bin скрипт и конфиг для php, а в www сами сайты. В папку php-bin нужно положить конфиг php.ini, и создать скрипт php.sh следующего содержания:
#!/bin/sh
# Для модуля mod_fastcgi можно задать следующие переменные окружения
# (для mod_fcgi первую из двух задавать нельзя)
#PHP_FCGI_CHILDREN=4
#export PHP_FCGI_CHILDREN
#PHP_FCGI_MAX_REQUESTS=300
#export PHP_FCGI_MAX_REQUESTS
exec nice -n 20 /usr/local/bin/php-cgi -c "/home/chihpih/data/php-bin/php.ini"
Так же надо дать скрипту права на запуск. Оба файла должны принадлежать пользователю, но чтобы он не смог их изменить, нужно установить дополнительные атрибуты на эти файлы.
# chflags schg,sunlink php.ini
# chflags schg,sunlink php.sh
Для автоматизации данного процесса я написал скрипт, который делает вышеописанные действия. То есть ему нужно передать имя пользователя, имя хоста, алиасы хоста и почту админа хоста, а далее он по шаблонам сделает конфигурационные файлы и запишет их куда необходимо. Останется только сделать мелкие правки, если они необходимы, и перезапустить апач.
#!/bin/sh
# Директория, в которой лежат домашние папки пользователей
ROOT_DIR="/usr/home"
# Директория, в которой лежат конфиги виртуальных хостов
AVH_DIR="/usr/local/etc/apache22/Includes"
HS_DIR="/usr/share/skel"
# Шаблон виртуального хоста
AVH_TEMPLATE="/root/hosting/webuser/vh.template"
# Шаблон конфига PHP
PHP_INI_TEMPLATE="/root/hosting/webuser/php.ini.template"
# Шаблон скрипта
PHP_SH_TEMPLATE="/root/hosting/webuser/php.sh.template"
# INFO: Пользователь создается с классом web_user32, измените его на свой.
if [ ! -d $ROOT_DIR ]; then
echo "Bad path to root home directroy"
exit 1
fi
if [ ! -d $AVH_DIR ]; then
echo "Bad path to apache directroy"
exit 1
fi
if [ ! -d $HS_DIR ]; then
echo "Bad path to skel directory"
exit 1
fi
if [ ! -f $AVH_TEMPLATE -o \
! -f $PHP_INI_TEMPLATE -o \
! -f $PHP_SH_TEMPLATE ];
then
echo "Bad path to template files"
exit 1
fi
user_create()
{
local res=0
local user_name="$1"
if [ `cat /etc/master.passwd | grep $user_name` ]; then
echo "User already exists"
return 1
fi
pw groupadd $user_name
if [ $? -ne 0 ]; then $res=$?; fi
pw useradd $user_name \
-g $user_name \
-d "$ROOT_DIR/$user_name/data" \
-L web_user32 \
-s csh
if [ $? -ne 0 ]; then $res=$?; fi
if [ $res -ne 0 ]; then
echo "Can't create user and group"
return 1
fi
return 0
}
user_create_home_skel()
{
local res=0
local user_name="$1"
mkdir -p "$ROOT_DIR/$user_name/data/php-bin"
if [ $? -ne 0 ]; then $res=$?; fi
mkdir -p "$ROOT_DIR/$user_name/data/log"
if [ $? -ne 0 ]; then $res=$?; fi
mkdir -p "$ROOT_DIR/$user_name/data/tmp"
if [ $? -ne 0 ]; then $res=$?; fi
mkdir -p "$ROOT_DIR/$user_name/data/www"
if [ $? -ne 0 ]; then $res=$?; fi
if [ $res -ne 0 ]; then
echo "Can't create home directory"
if [ -d "$ROOT_DIR/$user_name" ]; then
rm -rf "$ROOT_DIR/$user_name"
fi
return 1
else
for file in `ls $HS_DIR`; do
local new_name=`echo $file | sed -E 's/dot\.(.+)/.\2/gI'`
cp "$HS_DIR/$file" "$ROOT_DIR/$user_name/$new_name"
done
fi
return 0
}
user_create_config()
{
local user_name="$1"
local user_mail="$2"
local host_name="$3"
local host_aliases="$4"
sed -e "s/\$user/$user_name/g" \
$PHP_INI_TEMPLATE > "$ROOT_DIR/$user_name/data/php-bin/php.ini"
if [ $? -ne 0 ]; then
echo "Can't create php.ini config file"
return 1
fi
sed -e "s/\$user/$user_name/g" \
$PHP_SH_TEMPLATE > "$ROOT_DIR/$user_name/data/php-bin/php.sh"
if [ $? -ne 0 ]; then
echo "Can't create php.sh script file"
return 1
fi
sed -e "s/\$user/$user_name/g" \
-e "s/\$mail/$user_mail/g" \
-e "s/\$host/$host_name/g" \
-e "s/\$aliases/$host_aliases/g" \
$AVH_TEMPLATE > "$AVH_DIR/$host_name.conf"
if [ $? -ne 0 ]; then
echo "Can't create virtual host config file"
return 1
fi
return 0
}
user_set_permissions()
{
local user_name="$1"
chown -R $user_name:$user_name "$ROOT_DIR/$user_name"
chmod 0750 "$ROOT_DIR/$user_name"
find "$ROOT_DIR/$user_name/" -type d -exec chmod 0750 {} \;
chown www:www "$ROOT_DIR/$user_name/data/log"
chmod 0755 "$ROOT_DIR/$user_name/data/log"
setfacl -m u:www:x "$ROOT_DIR/$user_name"
setfacl -m u:www:x "$ROOT_DIR/$user_name/data"
setfacl -m u:www:rx "$ROOT_DIR/$user_name/data/php-bin"
setfacl -m u::rwx,g::rx,o::---,u:www:rx "$ROOT_DIR/$user_name/data/www"
setfacl -d -m u::rwx,g::rx,o::---,u:www:rx "$ROOT_DIR/$user_name/data/www"
chmod 0440 "$ROOT_DIR/$user_name/data/php-bin/php.ini"
chmod 0550 "$ROOT_DIR/$user_name/data/php-bin/php.sh"
chflags schg,sunlink "$ROOT_DIR/$user_name/data/php-bin/php.ini"
chflags schg,sunlink "$ROOT_DIR/$user_name/data/php-bin/php.sh"
return 0
}
read -p "Username: " user_name
read -p "Admin mail: " user_mail
read -p "Hostname: " host_name
read -p "Host aliases: " host_aliases
if [ ! $user_name ] || [ ! $user_mail ] || [ ! $host_name ] || [ ! "$host_aliases" ]; then
echo "Please enter correct data"
exit 1
fi
user_create_home_skel $user_name
if [ $? -ne 0 ]; then
echo "error: user_create_home_skel"
exit 1
fi
user_create $user_name
if [ $? -ne 0 ]; then
echo "error: user_create"
exit 1
fi
user_create_config $user_name $user_mail $host_name "$host_aliases"
if [ $? -ne 0 ]; then
echo "error: user_create_config"
exit 1
fi
user_set_permissions $user_name
if [ $? -ne 0 ]; then
echo "error: user_ser_permissions"
exit 1
fi
exit 0
В конце статьи я прикрепил архив со всеми необходимыми файлами для работы. Теперь можно добавить апач в автозагрузку и запустить его:
# echo 'apache2_enable="YES"' >> /etc/rc.conf
# /usr/local/etc/rc.d/apache2 start
Проверяем запустился ли апач:
# sockstat -4 -l | grep httpd
www httpd 52253 21 tcp4 192.168.7.253:80 *:*
www httpd 52252 21 tcp4 192.168.7.253:80 *:*
www httpd 50181 21 tcp4 192.168.7.253:80 *:*
www httpd 50059 21 tcp4 192.168.7.253:80 *:*
www httpd 50058 21 tcp4 192.168.7.253:80 *:*
www httpd 50057 21 tcp4 192.168.7.253:80 *:*
root httpd 20124 21 tcp4 192.168.7.253:80 *:*
Так же посмотрим, работает ли модуль fastcgi:
# ps -ax | grep php-cgi
71576 ?? INJ 0:06,17 /usr/local/bin/php-cgi -c /home/xan/php-bin/php.ini
71579 ?? INJ 0:44,67 /usr/local/bin/php-cgi -c /home/chihpih/data/php-bin/php.ini
Если вы видите на экране что-то подобное, значит - все работает нормально. Если же нет, то смотрите логи и проверяйте конфиг.
вт, 14/05/2013 - 20:35
Лол. Если у тебя хоть на одном серваке пых с такой опцией как fastcgi то считай тебя похекали.
вт, 14/05/2013 - 21:37
Что же вы тогда свои слова не подкрепили ссылкой на авторитетный ресурс, что это является потенциальной уязвимостью?
Добавить комментарий