Apache + php + (mod_fcgid | mod_fastcgi)

Опубликовано nekit - ср, 23/03/2011 - 18:24

Прочел я недавно, что Apache в режиме FastCGI работает гораздо быстрее CGI. Работа сервера в режиме FastCGI не сильно отличается от обычного режима, с одной лишь разницей - процессы не создаются каждый раз при обращении к скриптам, а остаются в памяти, поэтому один процесс может обработать множество запросов, всвязи с чем уменьшается отклик сервера. Так вот решил я настроить подобную связку. На сервере установлена ОС FreeBSD 7.3. Ставить все будем на чистую систему, если установлены старые версии пакетов, то просто удалите их.

Итак, лезем в порты ставить php, доступная на данный момент версия 5.2.14. Версию 5.3 не хочу, так как разработчики поубирали из нее много старых функций и не весь софт ее еще держит.
  1. # cd /usr/ports/lang/php52
  2. # make install clean
В окне выбора параметров компиляции я выбрал следующие:
  • CLI
  • CGI
  • REDIRECT
  • DISCARD
  • FASTCGI
  • PATHINFO
После установки php ставим apache. Вначале укажем опции сборки для апача. В опциях я указал в роли обрабатывающего соединения модуля, модуль worker. В данной конфигурации данный модуль будет эффективнее стандартного, потому что PHP будет работать в виде отдельного независимого процесса (а не в виде модуля apache), поэтому на апач будет возложена задача отдачи только статического контента. А модуль worker с этой задачей справляется лучше (лучше, надо понимать здесь как быстрее), да и памяти съедает гораздо меньше. Правим /etc/make.conf :
  1. # cat /etc/make.conf
  2.  
  3. PORTSDIR?=/usr/ports
  4.  
  5. .if ${.CURDIR} == ${PORTSDIR}/www/apache20
  6. WITH_MPM=worker
  7. WITH_SUEXEC=yes
  8. SUEXEC_DOCROOT="/usr/home"
  9. #SUEXEC_USERDIR="www"
  10. WITH_AUTH_MODULES=yes
  11. WITH_DAV_MODULES=yes
  12. WITH_MISC_MODULES=yes
  13. WITH_SSL_MODULES=yes
  14. WITH_KQUEUE_SUPPORT=yes
  15. WITH_EXCEPTION_HOOK=yes
  16. .endif
Теперь ставим апач.
  1. # cd /usr/ports/www/apache20
  2. # 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) будет создаваться свой разделяемый сегмент памяти.
  1. Port:   ap20-mod_fcgid-2.3.5
  2. Path:   /usr/ports/www/mod_fcgid
  3. Info:   An alternative FastCGI module for Apache2
  4. Maint:  hemi@puresimplicity.net
  5. 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
  6. 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
  7. WWW:    http://httpd.apache.org/mod_fcgid/
Ставим:
  1. # cd /usr/ports/www/mod_fcgid
  2. # make install clean
Если вам больше нравится модуль mod_fastcgi, то установите его (далее будет пример конфигурации с этим модулем). Установить можно так:
  1. # cd /usr/ports/www/mod_fastcgi
  2. # make install clean
После окончания установки лезем править конфиг apache. Нужно подключить установленный модуль, для этого добавляем следующую строку:
  1. LoadModule fcgid_module libexec/apache2/mod_fcgid.so
  2. или
  3. LoadModule fastcgi_module libexec/apache2/mod_fastcgi.so
Далее пишем настройки для модуля:
  1. <IfModule mod_fcgid.c>
  2.   FcgidMaxProcesses 30
  3.   FcgidMaxProcessesPerClass 5
  4.   FcgidMinProcessesPerClass 1
  5.   FcgidIdleTimeout 100
  6.   FcgidMaxRequestLen 1048576
  7.   FcgidMaxRequestsPerProcess 300
  8.   FcgidProcessLifetime 1800
  9.   FcgidFixPathinfo 1
  10.   FcgidIOTimeout 180
  11.   FcgidIPCDir /var/run/fcgidsock
  12.   FcgidProcessTableFile /var/run/fcgid_shm
  13.   FcgidPassHeader Authorization
  14.   FcgidPassHeader Proxy-Authorization
  15.   FcgidPassHeader HTTP_AUTHORIZATION
  16.   #FcgidWrapper /usr/local/sbin/suexec
  17.  
  18.   <Location /php-bin/>
  19.     SetHandler fcgid-script
  20.     Options ExecCGI
  21.     AllowOverride None
  22.     Order allow,deny
  23.     Allow from all
  24.   </Location>
  25. </IfModule>
  26.  
  27. или
  28.  
  29. <IfModule mod_fastcgi.c>
  30.    FastCgiConfig -autoUpdate \
  31.                         -singleThreshold 10 -multiThreshold 30 \
  32.                         -idle-timeout 30 -killInterval 150 \
  33.                         -min-server-life 15 -minProcesses 1 \
  34.                         -maxClassProcesses 5 -maxProcesses 50 \
  35.                         -pass-header Authorization \
  36.                         -pass-header Proxy-Authorization \
  37.                         -pass-header HTTP_AUTHORIZATION
  38.   FastCgiIpcDir /var/run/fastcgi
  39.   FastCgiWrapper /usr/local/sbin/suexec
  40.  
  41.   <Location /php-bin/>
  42.     SetHandler fastcgi-script
  43.     Options ExecCGI
  44.     AllowOverride None
  45.     Order allow,deny
  46.     Allow from all
  47.   </Location>
  48. </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 приложений, если нагрузка ниже заданного порога, то менеджер процессов прибьет лишние экземпляры процесса, оставив только один.
Вот пример виртуального хоста:
  1. <VirtualHost *:80>
  2.   SuexecUserGroup chihpih chihpih
  3.   DocumentRoot /home/chihpih/data/www/chihpih.no-ip.org
  4.   DirectoryIndex index.php index.html
  5.  
  6.   ServerName chihpih.no-ip.org
  7.   ServerAdmin webmaster@chihpih.no-ip.org
  8.  
  9.   ErrorLog /home/chihpih/data/log/error.log
  10.   #CustomLog /home/chihpih/data/log/access.log common
  11.  
  12.   <IfModule mod_fcgid.c>
  13.     AddType application/x-httpd-php .php
  14.     AddType application/x-httpd-php-source .phps
  15.     Action application/x-httpd-php /php-bin/php.sh
  16.     Alias /php-bin/ "/home/chihpih/data/php-bin/"
  17.   </IfModule>
  18.  
  19.   <IfModule mod_fastcgi.c>
  20.     AddType application/x-httpd-php .php
  21.     AddType application/x-httpd-php-source .phps
  22.     Action application/x-httpd-php /php-bin/php.sh
  23.     Alias /php-bin/ "/home/chihpih/data/php-bin/"
  24.   </IfModule>
  25.  
  26.   <Directory "/home/chihpih/data/www/chihpih.no-ip.org/">
  27.     Options FollowSymLinks MultiViews
  28.     AllowOverride All
  29.     Order allow,deny
  30.     Allow from all
  31.   </Directory>
  32. </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:
  1. # cd /home/chihpih/data
  2. # mkdir log tmp www php-bin
  3. # chown www:www log
  4. # chown chihpih:chihpih tmp www php-bin
  5. # chmod 0755 log
  6. # chmod 0750 tmp php-bin
  7. # chmod 0751 www
  8.  
  9. # setfacl -m u:www:x /home/chihpih
  10. # setfacl -m u:www:x /home/chihpih/data
  11. # setfacl -m u:www:rx /home/chihpih/data/php-bin
  12. # setfacl -m u::rwx,g::rx,o::---,u:www:rx /home/chihpih/data/www
  13. # 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 следующего содержания:
  1. #!/bin/sh
  2.  
  3. # Для модуля mod_fastcgi можно задать следующие переменные окружения
  4. # (для mod_fcgi первую из двух задавать нельзя)
  5. #PHP_FCGI_CHILDREN=4
  6. #export PHP_FCGI_CHILDREN
  7. #PHP_FCGI_MAX_REQUESTS=300
  8. #export PHP_FCGI_MAX_REQUESTS
  9.  
  10. exec nice -n 20 /usr/local/bin/php-cgi -c "/home/chihpih/data/php-bin/php.ini"
Так же надо дать скрипту права на запуск. Оба файла должны принадлежать пользователю, но чтобы он не смог их изменить, нужно установить дополнительные атрибуты на эти файлы.
  1. # chflags schg,sunlink php.ini
  2. # chflags schg,sunlink php.sh
Для автоматизации данного процесса я написал скрипт, который делает вышеописанные действия. То есть ему нужно передать имя пользователя, имя хоста, алиасы хоста и почту админа хоста, а далее он по шаблонам сделает конфигурационные файлы и запишет их куда необходимо. Останется только сделать мелкие правки, если они необходимы, и перезапустить апач.
  1. #!/bin/sh
  2.  
  3. # Директория, в которой лежат домашние папки пользователей
  4. ROOT_DIR="/usr/home"
  5. # Директория, в которой лежат конфиги виртуальных хостов
  6. AVH_DIR="/usr/local/etc/apache22/Includes"
  7. HS_DIR="/usr/share/skel"
  8. # Шаблон виртуального хоста
  9. AVH_TEMPLATE="/root/hosting/webuser/vh.template"
  10. # Шаблон конфига PHP
  11. PHP_INI_TEMPLATE="/root/hosting/webuser/php.ini.template"
  12. # Шаблон скрипта
  13. PHP_SH_TEMPLATE="/root/hosting/webuser/php.sh.template"
  14.  
  15. # INFO: Пользователь создается с классом web_user32, измените его на свой.
  16.  
  17. if [ ! -d $ROOT_DIR ]; then
  18.     echo "Bad path to root home directroy"
  19.     exit 1
  20. fi
  21.  
  22. if [ ! -d $AVH_DIR ]; then
  23.     echo "Bad path to apache directroy"
  24.     exit 1 
  25. fi
  26.  
  27. if [ ! -d $HS_DIR ]; then
  28.     echo "Bad path to skel directory"
  29.     exit 1 
  30. fi
  31.  
  32. if [ ! -f $AVH_TEMPLATE -o \
  33.      ! -f $PHP_INI_TEMPLATE -o \
  34.      ! -f $PHP_SH_TEMPLATE ];
  35. then
  36.     echo "Bad path to template files"
  37.     exit 1
  38. fi
  39.  
  40. user_create()
  41. {
  42.     local res=0
  43.     local user_name="$1"
  44.  
  45.     if [ `cat /etc/master.passwd | grep $user_name` ]; then
  46.         echo "User already exists"
  47.         return 1
  48.     fi
  49.  
  50.     pw groupadd $user_name
  51.     if [ $? -ne 0 ]; then $res=$?; fi
  52.  
  53.     pw useradd $user_name \
  54.              -g $user_name \
  55.                -d "$ROOT_DIR/$user_name/data" \
  56.              -L web_user32 \
  57.                -s csh
  58.     if [ $? -ne 0 ]; then $res=$?; fi
  59.  
  60.     if [ $res -ne 0 ]; then
  61.         echo "Can't create user and group"
  62.         return 1
  63.     fi
  64.  
  65.     return 0
  66. }
  67.  
  68. user_create_home_skel()
  69. {
  70.     local res=0
  71.     local user_name="$1"
  72.  
  73.     mkdir -p "$ROOT_DIR/$user_name/data/php-bin"
  74.     if [ $? -ne 0 ]; then $res=$?; fi
  75.     mkdir -p "$ROOT_DIR/$user_name/data/log"
  76.     if [ $? -ne 0 ]; then $res=$?; fi
  77.     mkdir -p "$ROOT_DIR/$user_name/data/tmp"
  78.     if [ $? -ne 0 ]; then $res=$?; fi
  79.     mkdir -p "$ROOT_DIR/$user_name/data/www"
  80.     if [ $? -ne 0 ]; then $res=$?; fi
  81.  
  82.     if [ $res -ne 0 ]; then
  83.         echo "Can't create home directory"
  84.         if [ -d "$ROOT_DIR/$user_name" ]; then
  85.             rm -rf "$ROOT_DIR/$user_name"
  86.         fi
  87.  
  88.         return 1
  89.     else
  90.         for file in `ls $HS_DIR`; do
  91.             local new_name=`echo $file | sed -E 's/dot\.(.+)/.\2/gI'`
  92.             cp "$HS_DIR/$file" "$ROOT_DIR/$user_name/$new_name"
  93.         done
  94.     fi
  95.  
  96.     return 0
  97. }
  98.  
  99. user_create_config()
  100. {
  101.     local user_name="$1"
  102.     local user_mail="$2"
  103.     local host_name="$3"
  104.     local host_aliases="$4"
  105.  
  106.     sed -e "s/\$user/$user_name/g" \
  107.             $PHP_INI_TEMPLATE > "$ROOT_DIR/$user_name/data/php-bin/php.ini"
  108.     if [ $? -ne 0 ]; then
  109.         echo "Can't create php.ini config file"
  110.         return 1
  111.     fi
  112.  
  113.     sed -e "s/\$user/$user_name/g" \
  114.             $PHP_SH_TEMPLATE > "$ROOT_DIR/$user_name/data/php-bin/php.sh"
  115.     if [ $? -ne 0 ]; then
  116.         echo "Can't create php.sh script file"
  117.         return 1
  118.     fi
  119.  
  120.     sed -e "s/\$user/$user_name/g" \
  121.             -e "s/\$mail/$user_mail/g" \
  122.             -e "s/\$host/$host_name/g" \
  123.             -e "s/\$aliases/$host_aliases/g" \
  124.             $AVH_TEMPLATE > "$AVH_DIR/$host_name.conf"
  125.     if [ $? -ne 0 ]; then
  126.         echo "Can't create virtual host config file"
  127.         return 1
  128.     fi
  129.  
  130.     return 0
  131. }
  132.  
  133. user_set_permissions()
  134. {
  135.     local user_name="$1"
  136.  
  137.     chown -R $user_name:$user_name "$ROOT_DIR/$user_name"
  138.     chmod 0750 "$ROOT_DIR/$user_name"
  139.     find "$ROOT_DIR/$user_name/" -type d -exec chmod 0750 {} \;
  140.     chown www:www "$ROOT_DIR/$user_name/data/log"
  141.     chmod 0755 "$ROOT_DIR/$user_name/data/log"
  142.  
  143.     setfacl -m u:www:x "$ROOT_DIR/$user_name"
  144.     setfacl -m u:www:x "$ROOT_DIR/$user_name/data"
  145.     setfacl -m u:www:rx "$ROOT_DIR/$user_name/data/php-bin"
  146.     setfacl -m u::rwx,g::rx,o::---,u:www:rx "$ROOT_DIR/$user_name/data/www"
  147.     setfacl -d -m u::rwx,g::rx,o::---,u:www:rx "$ROOT_DIR/$user_name/data/www"
  148.  
  149.     chmod 0440 "$ROOT_DIR/$user_name/data/php-bin/php.ini"
  150.     chmod 0550 "$ROOT_DIR/$user_name/data/php-bin/php.sh"
  151.     chflags schg,sunlink "$ROOT_DIR/$user_name/data/php-bin/php.ini"
  152.     chflags schg,sunlink "$ROOT_DIR/$user_name/data/php-bin/php.sh"
  153.  
  154.     return 0
  155. }
  156.  
  157. read -p "Username: " user_name
  158. read -p "Admin mail: " user_mail
  159. read -p "Hostname: " host_name
  160. read -p "Host aliases: " host_aliases
  161.  
  162. if [ ! $user_name ] || [ ! $user_mail ] || [ ! $host_name ] || [ ! "$host_aliases" ]; then
  163.     echo "Please enter correct data"
  164.     exit 1
  165. fi
  166.  
  167. user_create_home_skel $user_name
  168. if [ $? -ne 0 ]; then
  169.     echo "error: user_create_home_skel"
  170.     exit 1
  171. fi
  172.  
  173. user_create $user_name
  174. if [ $? -ne 0 ]; then
  175.     echo "error: user_create"
  176.     exit 1
  177. fi
  178.  
  179. user_create_config $user_name $user_mail $host_name "$host_aliases"
  180. if [ $? -ne 0 ]; then
  181.     echo "error: user_create_config"
  182.     exit 1
  183. fi
  184.  
  185. user_set_permissions $user_name
  186. if [ $? -ne 0 ]; then
  187.     echo "error: user_ser_permissions"
  188.     exit 1
  189. fi
  190.  
  191. exit 0
В конце статьи я прикрепил архив со всеми необходимыми файлами для работы. Теперь можно добавить апач в автозагрузку и запустить его:
  1. # echo 'apache2_enable="YES"' >> /etc/rc.conf
  2. # /usr/local/etc/rc.d/apache2 start
Проверям запустился ли апач:
  1. # sockstat -4 -l | grep httpd
  2. www      httpd      52253 21 tcp4   192.168.7.253:80      *:*
  3. www      httpd      52252 21 tcp4   192.168.7.253:80      *:*
  4. www      httpd      50181 21 tcp4   192.168.7.253:80      *:*
  5. www      httpd      50059 21 tcp4   192.168.7.253:80      *:*
  6. www      httpd      50058 21 tcp4   192.168.7.253:80      *:*
  7. www      httpd      50057 21 tcp4   192.168.7.253:80      *:*
  8. root     httpd      20124 21 tcp4   192.168.7.253:80      *:*
Так же посмотрим, работает ли модуль fastcgi:
  1. # ps -ax | grep php-cgi
  2. 71576  ??  INJ    0:06,17 /usr/local/bin/php-cgi -c /home/xan/php-bin/php.ini
  3. 71579  ??  INJ    0:44,67 /usr/local/bin/php-cgi -c /home/chihpih/data/php-bin/php.ini
Если вы видите на экране что-то подобное, значит - все работает нормально. Если же нет, то смотрите логи и проверяйте конфиг.
Прикрепленные файлы
webuser.tar.bz221.04 КБ

вт, 14/05/2013 - 20:35

Добавить комментарий

Этот вопрос задается для того, чтобы выяснить, являетесь ли Вы человеком или представляете из себя автоматическую спам-рассылку.