Apache + php + (mod_fcgid | mod_fastcgi)

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

Если вы видите на экране что-то подобное, значит - все работает нормально. Если же нет, то смотрите логи и проверяйте конфиг.

Прикрепленные файлы
Гость (не проверено)

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

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



 

nekit

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

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

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