30 September 2009

Nginx + php-fpm на CentOS 5.3

Nginx
В этой статье я поделюсь собственным опытом построения веб-сервера, работающего на CentOS 5.3.

Что требовалось:
  • Полностью избавиться от Apache. Сервер должен был выдерживать хорошую нагрузку, распределяя статику и динамику.
  • Нужна была поддержка последней версии libxml, малейшее различие в версиях делало сайт абсолютно нерабочим.
  • Нужен был gzip
  • И еще некоторые особенности, о которых я расскажу в самой статье

Предыстория

В качестве «полигона для испытаний» у меня был в наличии физический сервер, на борту которого стояли 2 процессора Xeon и 2 Гб оперативной памяти. Более точный конфиг привести не могу — не помню, да и особо не волновало.

Для работы сайта требовался php ветки 5.2.х. Когда я устанавливал из стандартного репозитория, я столкнулся с такой проблемой, как libxml. По умолчанию он ставился с версией 2.6.24, что делало проект абсолютно нерабочим (требовалась версия 2.6.32). Даже с подключенным репозиторием CentALT проблема не решалась.

Вероятно, можно было пересобрать готовый пакет и установить уже его, но у меня нет в этом никакого опыта, поэтому php я решил собирать вручную.

Правильно собрать php с первого раза мне, разумеется, не удалось :). Проект требовал учета небольшого количества тонкостей. В частности, это поддержка pdo и freetype.

Изначально я решил сделать все на основе связки nginx+apache, процесс установки и настройки ничем не отличался от того, как написано в этой статье. Да и сам процесс мне казался проще, чем настройка работы с php-cgi.

Все работало прекрасно, но я решил не останавливаться на этом, тем более сервер нужно было оптимизировать. Тут и пришла задумка убрать полностью apache, взвесив все это на nginx и php-cgi.

Сборка PHP

Нужно было еще чем-то управлять самим процессами FastCGI, я решил использовать php-fpm — PHP FastCGI Process Manager — это патч для PHP, позволяющий более эффективно управлять процессами FastCGI для обеспечения высокой стабильности работы. Почему именно он? Показался прост в настройке и вообще по отзывам неплох.

Сказано — сделано, тянем последнюю версию с http://php-fpm.org/downloads/. На тот момент у меня стоял php версии 5.2.10, поэтому я тянул php-5.2.10-fpm-0.5.13.diff.gz. Казалось бы, далее все просто. Стягиваем, и делаем все в точности как указано в вики самого проекта. Кратко приведу что нужно было сделать:

$ bzip2 -cd php-5.2.10.tar.bz2 | tar xf -
$ 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


Как видите, ничего хитрого. Далее — я собирал php. Процесс сборки шел с учетом тех тонкостей, которые мне были необходимы:

./configure --enable-fastcgi --enable-fpm --with-zlib --enable-pdo --with-pdo-mysql --with-mysql --with-config-file-path=/etc --enable-calendar --with-iconv --enable-exif --enable-ftp --enable-wddx --with-zlib --with-bz2 --with-gettext --with-xmlrpc --with-xml --enable-soap --enable-filepro --enable-bcmath --enable-trans-sid --enable-mbstring --enable-dbx --enable-dba --with-openssl --with-mhash --with-mcrypt --with-xsl --with-curl --with-pcre-regex --with-gd --enable-gd-native-ttf --with-ldap --enable-pdo --with-pdo-mysql --with-mysql --with-sqlite --with-pdo-sqlite --enable-sqlite-utf8 --with-pear --with-freetype-dir=/usr --with-jpeg-dir=/usr

Болдом помечены обязательные параметры, которые необходимы для работы php-fpm. --with-config-file-path=/etc — решил четко указать где держать php-ini.
Ну, и затем:
make install all
Нужно еще отредактировать файл /etc/php-fpm.conf. В частности, я редактировал:

Pid file                                                        
<value name="pid_file">/var/run/php-fpm.pid</value>             
Error log file                                                  
<value name="error_log">/var/log/php-fpm.log</value>
В первом случае мы задаем где будет лежать pid-файла процесса, во втором куда писать логи.

<value name="listen_address">127.0.0.1:9000</value>
Адрес и порт где будет слушать php. Это мы и будем использовать при кофигурировании nginx. Либо, тут указывается путь к сокету — например:
<value name="listen_address">/tmp/php-fpm.sock</value>

И необходимо задать от чьего имени запускать сам процесс:
<code>Unix user of processes                                  
<value name="user"></value>                       
Unix group of processes                                 
<value name="group"></value>

Это уже на ваше усмотрение, главное, чтобы права были соответствующие.
Можно еще покопать конфиг, каждая строчка снабжена комментарием, разобраться несложно.

Nginx

Как я уже говорил выше, установка и установка nginx практически не отличается от того, что написано в статье, ссылку на которую я указал выше. Единственный момент — мы не используем проксирование, так как apache у нас нет, соответственно строчки proxy_* не имеют для нас никакого значения. Ну и сама специфика настройки, к примеру worker_connections или worker_processes и т.п. зависит конкретно от ваших нужд. Разумеется, я собирал вручную, потому что и тут нужно было учесть некоторые тонкости. Без них было бы вполне достаточно:

yum install nginx

Приведу пример части конфига из ветки http:
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"'
'"$gzip_ratio"'
'"$got" "$sent_http_set_coockie"';

access_log /var/log/nginx/access.log main;

'"$got" "$sent_http_set_coockie"'; — это мне требуется для логгирования с установленным модулем custom-nginx-session-module (Модуль для определения куки и установки уникальной сессии). О процессе установки и настройки могу рассказать позже, если кому-то будет интересно. А то, что выше — обычное логгирование.

Еще подразумевалось использование виртуальных хостов, поэтому для удобства я решил вынести это в отдельные файлы:
include /etc/nginx/sites-enabled/*;

Приведу пример — файл /etc/nginx/sites-enabled/example.com:
server {
listen 80;
server_name example.com www.example.com;
# Разумеется, мне нужен лог для каждого виртуального хоста
access_log /var/log/examplecom-access.log;
error_log /var/log/example.com-error.log;

# Указываем корень где будет лежать сайт
root /path/to/www;

# Всю статику отдаем nginx
location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|js|bmp)$ {
root /path/to/www;
}

UPD: Замечание от хабраюзера AlexeyK:
строчки не имеют смысла, если у вас один и тот же root, да и вообще nginx отдаст эти файлы так сказать по дефолту, если вы на них handler не повесите :) имеет смысл поставить expires побольше в этот location и возможно настроить размер буферов для отдачи (если статических файлов действительно много)

# Помните прикол с свн? :) На всякий случай учел
location ~ /.svn/ {
deny all;
}

# Переписывание всех запросов к несуществующему документу на index.php,
if (!-e $request_filename) {
rewrite ^.*$ /index.php last;
}

# Передача файлов, имена которых заканчиваются на .php на интерфейс fastCGI (php-fpm)
location ~ \.php$ {

# Адрес и порт, который мы указали в конфиге php-fpm.conf
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /path/to/www$fastcgi_script_name;
include fastcgi_params;
}
}

UPD: Или, как подсказал хабраюзер darivush:

fastcgi_pass 127.0.0.1:9000; заменить на fastcgi_pass unix:/tmp/fastcgi_sock;, так как юникс сокеты предпочительнее 127.0.0.1. Ну, и соответственно путь /tmp/fastcgi_sock должен быть правильным, конфигурится в /etc/php-fpm.conf в разделе
<value name="listen_address">


Разумеется, это пример стандартного минимального конфига, все остальное вписывается\убирается\редактируется в зависимости от поставленных задач.

Вроде ничего не забыл. Запускаем php-fpm и nginx:
/etc/init.d/php-fpm start
/etc/init.d/nginx start


И тут я столкнулся с проблемой — php у меня не пропатчился как нужно, в итоге php-fpm напрочь отказывался запускаться, ругаясь на неверный параметр при запуске php-cgi. Решение этой проблемы я так и не смог найти на тот момент. Все решилось некоторое время спустя, когда вышла версия php 5.2.11, которую я пропатчил тем же патчем, сконфигурировал и установил так, как описано выше. После чего все запустилось и заработало без проблем.

Так как мне нужно было собирать php вручную (из-за всех тонкостей, о которых я писал выше), то я не пытался решать задачу установкой всего необходимого из стандартных репозиториев. Возможно, это работает и процесс установки становится проще.

Спасибо за внимание. Статью разместил у себя в блоге, потому что ни в коем случае я не претендую на изобретение велосипеда в плане поставленной задачи. Это больше вспомогательный мануал для меня, который может понадобиться в случае необходимости. Ну, и ее окажется вполне достаточно для новичков. Но, если статья покажется вам полезной, с удовольствием перенесу это в блог nginx. В планах — рассказать о настройке custom-nginx-session-module (я позже расскажу зачем он мне нужен), а так же memcached, и pinba.

И да, пожелания, замечания и ценные советы приветствуются.

UPD: перенесено в nginx.
Tags:nginxphp-fpmcentos
Hubs: Nginx
+1
23.1k 87
Comments 22
Popular right now
PHP разработчик
from 90,000 to 100,000 ₽ИМАГМоскваRemote job
Backend-программист на PHP
from 160,000 ₽ЧемпионРязань
DevOps Engineer
from 200,000 ₽HomeappМосква
PHP разработчик
from 60,000 to 120,000 ₽EmsoftМоскваRemote job
Top of the last 24 hours