27 января 2010 г. FastCGI php-fpm Nginx CGI PHP

Nginx. Использование PHP в режиме FastCGI с помощью php-fpm

php-fastcgi

У меня стоял Apache 2.2 и mod_php, так как Apache жрет не мало ресурсов, я решил постепенно переводить проекты на сервере на связку Nginx + PHP-FastCGI, а в качестве спаунера php-fpm.

Вкратце, что такое FastCGI и почему он лучше чем mod_php?

FastCGI это высокопроизводительный и масштабируемый интерфейс для взаимодействия web-сервера и приложений, дальнейшее развитие технологии CGI. Ознакомиться с более подробной информацией о FastCGI вы можете на официальном сайте или в Википедии.

Основное преимущество FastCGI в изолировании динамического языка от web-сервера. Например, запуск FastCGI процесса под пользователем, отличным от пользователя web-сервера, а также процесс может находиться в chroot'е, отличном от chroot'а web-сервера. Помимо всего прочего, эта технология позволяет запускать web-сервера и CGI процессы (теже php скрипты) на различных хостах, что улучшает масштабируемость и также способствует безопасности без существенной потери в производительности.

Ну а зачем нам php-fpm, если PHP и так поддерживает работу в режиме FastCGI?

php-fpm - это патч для PHP, для использования PHP как FastCGI процесса в высоконагруженных системах. Устраняет ряд проблем мешающих использовать PHP в режиме FastCGI. Андрей Нигматулин представил набор патчей php-fpm к PHP 4/5, устраняющих ряд проблем, которые мешают использовать PHP в режиме FastCGI на высоконагруженных системах.

Возможности php-fpm:

* Управление процессами. Возможность "плавно" останавливать и перезапускать php воркеры без потери запросов. Возможность плавно обновлять конфигурацию и binary без потери запросов;
* Ограничение ip адресов, с которых могут приходить запросы от web сервера;
* Динамическое количество процессов, в зависимости от нагрузки (TODO);
* Запуск воркеров с разными uid/gid/chroot/environment и разными php.ini опциями;
* Логирование stdout & stderr рабочих процессов;
* Аварийный перезапуск всех процессов при случайном разрушении shared memory opcode cache, если используется акселератор;
* Принудительное завершение подвисших процессов, если set_time_limit() не срабатывает (TODO);

http://php-fpm.org/wiki/What_is_PHP-FPM

Андрей Нигматулин / php-fpm / PHPConf 08 from Mihail Andreev on Vimeo.



Кстати, в видео я тоже поучаствовал, на 5-ой минуте и 20-ой секунде я там прохожу перед камерой в костюме и красной футболке... :D


Итак, приступим!

Установка Nginx

На самом деле Nginx у меня был уже установлен, но для полноты статьи я расскажу как и его установить. В зависимости от необходимого функционала, либо от стабильности версии, вам надо выбрать подходящую версию Nginx. Стабильная находится в каталоге "/usr/ports/www/nginx/", а более новая в "/usr/ports/www/nginx-devel/", у меня стоит именно вторая.

# cd /usr/ports/www/nginx-devel/
# make install clean

Всё, Nginx установлен, добавьте его в автозагрузку:

# echo 'nginx_enable="YES"' >> /etc/rc.conf

Установка php-fpm

Процесс заключается в пропатчивании и переустановки PHP интерпретатора с поддержкой FastCGI и php-fpm.

Установка из сорцов

Заходим на оф. сайт PHP и качаем необходимую версию PHP интерпретатора со страницы http://www.php.net/downloads.php, у меня это PHP 5.2.10.

# cd /tmp
# fetch http://ru2.php.net/get/php-5.2.10.tar.gz/from/ru.php.net/mirror

Заходим на оф. сайт проекта php-fpm, в раздел загрузок http://php-fpm.org/download и выбираем подходящую для вашего PHP интерпретатора версию патча. У меня это PHP 5.2.10, поэтому я буду ставить именно его.

# cd /tmp
# fetch http://php-fpm.org/downloads/php-5.2.10-fpm-0.5.13.diff.gz
# tar -xzf php-5.2.10.tar.gz
# gzip -cd php-5.2.10-fpm-0.5.13.diff.gz | patch -d php-5.2.10 -p1
# cd php-5.2.10
# ./configure --enable-fastcgi --enable-fpm
# make all install

Теперь, скопируем скрипт инициализации php-fpm в каталог "/usr/local/etc/rc.d" и назначим ему права на запуск:

# cp /tmp/php-5.2.10/sapi/cgi/fpm/php-fpm /usr/local/etc/rc.d/
# chmod +x /usr/local/etc/rc.d/php-fpm

Установка из портов

Тут все намного проще... :)

# cd /usr/ports/lang/
# fetch http://php-fpm.org/downloads/freebsd-port/php-5.2.10-fpm-0.5.13.tar.gz
# tar -xzvf php-5.2.10-fpm-0.5.13.tar.gz
# rm php-5.2.10-fpm-0.5.13.tar.gz
# cd php5-fpm
# make install

Завершение установки

После установки, проверьте версию php:

# php -v
PHP 5.2.10 (cli) (built: Sep  2 2009 12:46:58)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2009 Zend Technologies

Если "built" сегодняшний, то всё оки, если нет - отпишите в комментарии, помогу разобраться.
Я ставил и из сорцов и из портов, так что все проверено на себе! :)

Добавим в автозагрузку:

# echo 'php_fpm_enable="YES"' >> /etc/rc.conf

Настройка Nginx

Теперь настраиваем Nginx, отредактируйте конфиг "/usr/local/etc/nginx/nginx.conf" или свой "vhost":

server {

        listen 80;
        server_name demo.adw0rd.ru;

        location ~* \.(jpg|jpeg|gif|png|ico|css|zip|js|swf)$ {
                root /home/adw0rd/adw0rd.ru/demo;
        }

        location / {
            fastcgi_pass   82.146.63.195:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /home/adw0rd/adw0rd.ru/demo$fastcgi_script_name;
            include        fastcgi_params;
        }
}

Чуть более подробно о конфигурации nginx тут и тут.

Настройка php-fpm

Я покажу части конфига, которые только отличаются от "php-fpm.conf.default", весь конфиг можно взять тут.

<value name="listen_options">

        Set listen(2) backlog
        <value name="backlog">-1</value>

        Set permissions for unix socket, if one used.
        In Linux read/write permissions must be set in order to allow connections from web server.
        Many BSD-derrived systems allow connections regardless of permissions.
        <value name="owner">www</value>
        <value name="group">www</value>
        <value name="mode">0666</value>
</value>
...
Unix user of processes
<value name="user">www</value>

Unix group of processes
<value name="group">www</value>
...
Comma separated list of ipv4 addresses of FastCGI clients that allowed to connect.
Equivalent to FCGI_WEB_SERVER_ADDRS environment in original php.fcgi (5.2.2+)
Makes sense only with AF_INET listening socket.
<value name="allowed_clients">127.0.0.1,82.146.63.195</value>

"82.146.63.195" - это ип моего сервера, на котором крутится nginx. Ему то мы и разрешим доступ.

Запуск

Теперь осталось только запустить nginx и php-fpm:

/usr/local/etc/rc.d/nginx restart
/usr/local/etc/rc.d/php-fpm restart

Если не запустилось, то смотрите "/var/log/php-fpm.log". Если нету файла лога вообще, то создайте его и права на запись выставите.
Вот и все, в следующей статье расскажу о spawn-fcgi (раньше входил в lighttpd).

Комментарии

Блииин, че нету в пакетах nginx и php шоле?

Я не понял вопроса.

Хай) Мы в последнее время используем spawn-fcgi - он есть в портах и с ним не надо патчить PHP и, соответственно, можно завести с любой версией. Конфиг nginx я обычно стараюсь писать как то так (с try_files):

server {
listen 80;
server_name demo.adw0rd.ru;

    location / {
        root /home/adw0rd/adw0rd.ru/demo/public;
        expires 7d;
        try_files $uri @php;
    }

    location @php {
        expires off;
        fastcgi_pass   82.146.63.195:9000;
        fastcgi_param  SCRIPT_FILENAME  /home/adw0rd/adw0rd.ru/demo/index.php;
        include        fastcgi_params;
    }

}

Плюсы:
- не надо заморачиваться на расширения файлов. Можно указать expires и т.п.
Минусы:
- надо следить чтобы try_files не отдал .php как статику. Решается легко - в директории, указанной в root, не должно быть .php файлов. У меня обычно в проекте есть папка public где лежит только статика. Все .php файлы лежат в другом месте (см. конфиг выше). Вот :)

Еще я обычно задаю SCRIPT_FILENAME жестко. Ну это от проекта конечно зависит.

Смысл то не в том же, что fastcgi много лучше модуля (в основной массе всем на chroot, других пользователей и другие хосты наплевать), а в том что апач прожорлив слишком, нужно от него избавляться.

ash2k, я под убунтой на работе юзаю spawn-fcgi, скоро и про него напишу. Думаю даже что он побыстрее должен быть... Надо бы его потестить с fpm

Можно указать expires и т.п.

Ну в моей конструкции тоже можно, я так и юзаю, но в примере не показал :)

У меня обычно в проекте есть папка public где лежит только статика.
У меня она называется "static" :)

vasa_c, ну а я о чем?)

Жду когда php-fpm, вмерженная в PHP, стабилизируется и будет уже в релизе вместе с PHP идти. Вот тогда будет действительно клево. Ты ведь в курсе, что патч php-fpm приняли в основной реп PHP?

p.s. а вообще мы тут планируем на Python перейти :) задолбал PHP костылями!

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

ash2k, я не в курсе был что fpm патчи внесут в core. Ты кстати слышал о Facebook и перепатчиваниие PHP ими?

Камменты глючили из-за того, что я переводил свой блог на fpm )

p.s. а вообще мы тут планируем на Python перейти :) задолбал PHP костылями!
да, меня тоже достал PHP и я перешел на Python :) но по работе все еще приходится иметь дело с PHP, а также некоторые свои проекты на PHP поддерживаем/дорабатываем

А вот и про фейсбуковский HipHop http://rmcreative.ru/blog/post/hiphop

да я читал про этот хипхоп. интересно что быстрее - написаное на пхп и "скомпиленное" этим хипхопом или напимер джава.

Думаю скоро бенчмарки появятся.

# tar -xzf php-5.2.10.tgz

Наверное, имелось ввиду:

tar -xzf php-5.2.10.tar.gz

Спасибо что заметили, исправил

нужно в конфиге php-fpm.conf прописать

Address to accept fastcgi requests on.
                    Valid syntax is 'ip.ad.re.ss:port' or just 'port' or '/path/to/unix/socket'
                    х.х.х.х:9000

где x.x.x.x ваш ip адрес
Строка номер 41 Это типа на каком адресе слушать

А это вы на видео?)

Это все конечно хорошо, вот заинтересовался полным отказом от апача, но вот такой вопрос меня интересует, будет ли nginx распознавать .htaccess файлы? интересует deny,allow и мод реврайт. заранее спс.

На видео не я.
.htaccess - это только для апач, конечно nginx не будет их обрабатывать из коробки. Погуглите расширение для nginx которое нечто подобно делает.
По поводу рерайтов - это есть, deny/allow тоже, читайте документацию на http://sysoev.ru/nginx/docs/

Походу .htaccess читаться не будет... а что же тогда делать, прописывать подобные вещи

location  /  {
    deny    192.168.1.1;
    allow   192.168.1.0/24;
    allow   10.1.1.0/16;
    deny    all;
}

неудобно

да, спасибо, вот уже читаю доки (не прочитал пред. ваш пост, слишком быстро ответили :) )... еще такой вопрос, а подобным образом пропатченный php будет работать как mod apache?

а при использовании php-fpm как описано в этой статье будут идти ероры согласно параметру в php.ini?

; Log errors to specified file.
error_log = /file.log

Еще подскажите пожалуйста, как сделать короче такие правила

location  /page1.php  {
     deny    all;
}

location  /page2.php  {
        deny    all;
}

то есть в 1 location это можно записать?

так правильно?

     location ~* ^/(page1.php|page2.php)$ {
            deny    all;
        }

Правильно так, как вы написали в посте от 03.01.2011.
Работать будет и второй вариант, но такого написания лучше избегать.

Очень рекомендую посмотреть http://video.yandex.ru/users/ibondarets/view/1/

Fighter,
1. Тут про error.log - http://adw0rd.ru/2010/ubuntu-php53/
2. Лучше делать на каждое событие свой location, как и сказал ash2k: "в посте от 03.01.2011". Когда будет много location - вы сами запутаетесь, я сталкиваюсь постоянно с этим на работе, постепенно переделывая location из регулярок в просто location, если конечно это возможно... Так что посмотрите видео и избегайте регулярок и if'ов

Да просто правил море :)
а в инклуд не хочется выносить...

еще такой вопрос, вот в апаче когда php как модуль можно указать эти переменные

 php_admin_value open_basedir "/home/user/data:."
    php_admin_value sendmail_path "/usr/sbin/sendmail -t -i -f webmaster@host"
    php_admin_value session.save_path "/home/user/data/mod-tmp"
    php_admin_value upload_tmp_dir "/home/user/data/mod-tmp"

а где их указать когда используется PHP-FPM? нужно чтоб сендмаил работал...

извиняюсь, про сендмаил уже нашел здесь http://adw0rd.ru/2010/ubuntu-php53/ :)

я так полагаю это необходимо раскомментировать
<!-- /usr/sbin/sendmail -t -i -->

еще вопрос насчет этих настроек

Chroot to this directory at the start, absolute path
            <value name="chroot"></value>

            Chdir to this directory at the start, absolute path
            <value name="chdir"></value>

нужно указать по аналогии с php_admin_value open_basedir
если эти параметры не указаны, тогда какой дир берется за "изоляцию"?

не зпускается(
пишет
/usr/local/etc/rc.d/php-fpm start
Starting php_fpm ................................... failed

а в логе

Jan 04 15:15:35.042340 [NOTICE] fpm_got_signal(), line 56: received SIGTERM
Jan 04 15:15:35.042369 [NOTICE] fpm_pctl(), line 256: switching to 'terminating' state
Jan 04 15:15:35.042389 [NOTICE] fpm_pctl_kill_all(), line 172: sending signal 15 SIGTERM to child 50506 (pool default)
Jan 04 15:15:35.042397 [NOTICE] fpm_pctl_kill_all(), line 172: sending signal 15 SIGTERM to child 50505 (pool default)
Jan 04 15:15:35.042405 [NOTICE] fpm_pctl_kill_all(), line 172: sending signal 15 SIGTERM to child 50504 (pool default)
Jan 04 15:15:35.043023 [NOTICE] fpm_pctl_kill_all(), line 172: sending signal 15 SIGTERM to child 50503 (pool default)
Jan 04 15:15:35.043031 [NOTICE] fpm_pctl_kill_all(), line 172: sending signal 15 SIGTERM to child 50502 (pool default)
Jan 04 15:15:35.043441 [NOTICE] fpm_pctl_kill_all(), line 181: 5 children are still alive
Jan 04 15:15:35.043486 [NOTICE] fpm_got_signal(), line 48: received SIGCHLD
Jan 04 15:15:35.043611 [WARNING] fpm_children_bury(), line 215: child 50506 (pool default) exited on signal 15 SIGTERM after 764.874366 seconds from start
Jan 04 15:15:35.043659 [WARNING] fpm_children_bury(), line 215: child 50505 (pool default) exited on signal 15 SIGTERM after 764.875014 seconds from start
Jan 04 15:15:35.043676 [WARNING] fpm_children_bury(), line 215: child 50504 (pool default) exited on signal 15 SIGTERM after 764.875615 seconds from start
Jan 04 15:15:35.043690 [WARNING] fpm_children_bury(), line 215: child 50503 (pool default) exited on signal 15 SIGTERM after 764.876131 seconds from start
Jan 04 15:15:35.043703 [WARNING] fpm_children_bury(), line 215: child 50502 (pool default) exited on signal 15 SIGTERM after 764.876558 seconds from start
Jan 04 15:15:35.043720 [NOTICE] fpm_pctl_exit(), line 81: exiting, bye-bye!
Jan 04 15:16:02.249955 [NOTICE] fpm_unix_init_main(), line 284: getrlimit(nofile): max:200000, cur:200000
Jan 04 15:16:02.250074 [ERROR] fpm_sockets_new_listening_socket(), line 221: bind() for address '127.0.0.1:9000' failed: Address already in use (48)

Перезагрузите систему

можно как-то из портов поставить php-5.2.10? а то у меня более поздняя, тогда я смогу портом поставить патч...

зачем перезагрузить, без этого никак?

можно как-то из портов поставить php-5.2.10?

можно "5.2.*", смотрите /usr/ports/lang/php52/

а то у меня более поздняя, тогда я смогу портом поставить патч...

почему не поставите "более позднюю"?

Сейчас уже не надо ничего патчить, так как c версией php 5.3 - fpm внесли в состав дистрибутива php.
И через порты он ставится путем указания галочки в make config
Также есть пункт и для php 5.2

Комментарии закрываю в силу того, что:
* у PHP-FPM изменился формат конфига
* теперь можно ставить из портов (см. выше комментарий), а не патчить

Возможно вас заинтерисует:
http://adw0rd.ru/tag/nginx/
http://adw0rd.ru/tag/php-fpm/
http://adw0rd.ru/tag/fastcgi/
http://adw0rd.ru/tag/php/

Блииин! Ребята, низкий поклон и огромнейшее спасибо за статью и обзор!
Недавно столкнулся с переносом корявоработающего сайта на новый сервер, где уже крутится nginx и php 5.5
Уже все настроено работает как часики. Но корявый сайт работает ТОЛЬКО под php 5.2, написан невероятно криворуко левой рукой из под правой коленки, а на переписывание времени нет. Было выбрано решение поставить параллельно с 5.5 5.2. Долго мучился с настройками пока не наткнулся на эту статью! На все вопросы в раз нашлись ответы!

Все не нарадуюсь этим строчкам:

[root@web:chroot websrv]# ll
srw-rw-rw- 1 root root php-fpm52.sock
srw-rw-rw- 1 root root php-fpm55.sock

adw0rd, спасибо вам! =)
С уважением, Val Man

Не за что, статья правда уже устарела, конфиги fpm уже давно в ini-формате)

Оставьте свой комментарий

Markdown