Apache + php + (mod_fcgid | mod_fastcgi)

Прочел я недавно, что 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
  • 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 КБ

Комментарии

Аватар пользователя Гость
Гость
вт, 2013-05-14 18:35

FcgidFixPathinfo - должно быть 1, если в php.ini параметр cgi.fix_pathinfo выставлен в 1

Лол. Если у тебя хоть на одном серваке пых с такой опцией как fastcgi то считай тебя похекали.

 

Аватар пользователя nekit
nekit
вт, 2013-05-14 19:37

Что же вы тогда свои слова не подкрепили ссылкой на авторитетный ресурс, что это является потенциальной уязвимостью?

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

Filtered text

CAPTCHA
Этот вопрос предназначен для предотвращения автоматизированной обработки форм.
Fill in the blank.