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).


Комментарии (37) на запись «Nginx. Использование PHP в режиме FastCGI с помощью php-fpm»

» Трекбеки скрыты, показать их?
  1. Имя | 27.01.2010 в 18:21

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

  2. adw0rd | 27.01.2010 в 18:29

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

  3. ash2k | 27.01.2010 в 20:20

    Хай) Мы в последнее время используем 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 жестко. Ну это от проекта конечно зависит.

  4. vasa_c | 27.01.2010 в 21:15

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

  5. adw0rd | 28.01.2010 в 00:50

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

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

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

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

    У меня она называется «static» :)
  6. adw0rd | 28.01.2010 в 00:51

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

  7. ash2k | 03.02.2010 в 15:52

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

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

  8. ash2k | 03.02.2010 в 15:53

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

  9. adw0rd | 03.02.2010 в 16:33

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

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

  10. adw0rd | 03.02.2010 в 16:35

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

    да, меня тоже достал PHP и я перешел на Python :) но по работе все еще приходится иметь дело с PHP, а также некоторые свои проекты на PHP поддерживаем/дорабатываем
  11. adw0rd | 03.02.2010 в 20:24

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

  12. ash2k | 03.02.2010 в 21:41

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

  13. adw0rd | 03.02.2010 в 22:33

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

  14. sd | 20.02.2010 в 19:55

    # tar -xzf php-5.2.10.tgz

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

    # tar -xzf php-5.2.10.tar.gz

  15. adw0rd | 20.02.2010 в 22:32

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

  16. xapmc | 29.05.2010 в 23:46

    нужно в конфиге 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 Это типа на каком адресе слушать

  17. Fighter | 03.01.2011 в 00:00

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

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

  18. adw0rd | 03.01.2011 в 00:21

    На видео не я.

    .htaccess — это только для апач, конечно nginx не будет их обрабатывать из коробки. Погуглите расширение для nginx которое нечто подобно делает.

    По поводу рерайтов — это есть, deny/allow тоже, читайте документацию на sysoev.ru/nginx/docs/

  19. Fighter | 03.01.2011 в 00:33

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

    location / {

    deny 192.168.1.1;

    allow 192.168.1.0/24;

    allow 10.1.1.0/16;

    deny all;

    }

    неудобно

  20. Fighter | 03.01.2011 в 01:16

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

  21. adw0rd | 03.01.2011 в 13:19

    Погуглите apache php fastcgi

  22. Fighter | 03.01.2011 в 19:57

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

    ; Log errors to specified file.

    error_log = /file.log

  23. Fighter | 03.01.2011 в 21:25

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

    location /page1.php {

    deny all;

    }

    location /page2.php {

    deny all;

    }

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

  24. Fighter | 04.01.2011 в 00:06

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

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

  25. ash2k | 04.01.2011 в 09:16

    Правильно так, как вы написали в посте от 03.01.2011.

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

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

  26. adw0rd | 04.01.2011 в 11:45

    Fighter,

    1. Тут про error.log — adw0rd.ru/2010/ubuntu-php53/

    2. Лучше делать на каждое событие свой location, как и сказал ash2k: «в посте от 03.01.2011». Когда будет много location — вы сами запутаетесь, я сталкиваюсь постоянно с этим на работе, постепенно переделывая location из регулярок в просто location, если конечно это возможно... Так что посмотрите видео и избегайте регулярок и if'ов

  27. Fighter | 04.01.2011 в 14:09

    Да просто правил море :)

    а в инклуд не хочется выносить...

  28. Fighter | 04.01.2011 в 14:54

    еще такой вопрос, вот в апаче когда 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? нужно чтоб сендмаил работал...

  29. Fighter | 04.01.2011 в 14:57

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

  30. Fighter | 04.01.2011 в 14:58

    я так полагаю это необходимо раскомментировать

    <!-- /usr/sbin/sendmail -t -i -->

  31. Fighter | 04.01.2011 в 15:03

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

    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

    если эти параметры не указаны, тогда какой дир берется за «изоляцию»?

  32. Fighter | 04.01.2011 в 16:17

    не зпускается(

    пишет

    /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)

  33. adw0rd | 04.01.2011 в 16:36

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

  34. Fighter | 04.01.2011 в 16:49

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

  35. Fighter | 04.01.2011 в 16:50

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

  36. adw0rd | 04.01.2011 в 20:33

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

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

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

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

    Сейчас уже не надо ничего патчить, так как c версией php 5.3 — fpm внесли в состав дистрибутива php.

    И через порты он ставится путем указания галочки в make config

    Также есть пункт и для php 5.2

  37. adw0rd | 04.01.2011 в 20:36

    Комментарии закрываю в силу того, что:

    * у PHP-FPM изменился формат конфига

    * теперь можно ставить из портов (см. выше комментарий), а не патчить

    Возможно вас заинтерисует:

    adw0rd.ru/tag/nginx/

    adw0rd.ru/tag/php-fpm/

    adw0rd.ru/tag/fastcgi/

    adw0rd.ru/tag/php/

Наша фирма представляет подвески из золота по 1005 - заходите