Apache хороший веб-сервер, распространенный, легко настраиваемый, но потребляющий немереное количество ресурсов сервера. Рано или поздно веб-мастера начинают задумываться о переходе на более легковесный веб-сервер, как правило это те, у кого высоконагруженные проекты, либо обладатели VDS/VPS серверов с ограниченными ресурсами. О настройке хорошо известного, который находится у многих на слуху, веб-сервера nginx с поддержкой PHP, я и хотел бы сегодня поговорить. Необходимо развеять миф у новичков о сложности настройки связки nginx + PHP. Не все так страшно, как многие это представляют. Сегодня я наглядно покажу начинающим администраторам, как собрать самую свежую версию nginx + spawn-fcgi.

Так как nginx не имеет своего модуля для работы с PHP, а сам PHP по умолчанию не может работать в режиме FastCGI, то для связи с nginx нужен посредник, в нашем случае посредником будет выступать spawn-fcgi.

Стоит немного пояснить, зачем это нужно. Само по себе создание процесса FastCGI прямо в веб-сервере имеет несколько недостатков: процесс FastCGI может быть запущен только локально, имеет те же права, что и веб-сервер, а так же имеет ту же base-dir, что и веб-сервер.

Как только Вы начнете использовать отдельный FastCGI сервер, чтобы снять нагрузку с веб-сервера, Вы сможете контролировать процесс FastCGI внешними программами, такими как spawn-fcgi.

spawn-fcgi используется, чтобы запустить FastCGI процесс в своём окружении, выставить ему user-id, group-id и сменить корневую директорию (chroot).

Приступим.

Если не установлен LAMP, устанавливаем пакеты без A (Apache):

sudo apt-get install php5-common php5-dev php5-mysql php5-sqlite php5-tidy php5-xmlrpc php5-xsl php5-cgi php5-mcrypt php5-curl php5-gd php5-mhash php5-pspell php5-snmp libmagick9-dev php5-cli mysql-server mysql-client libmysqlclient15-dev

Если Apache уже предустановлен, останавливаем его и убираем из загрузочных скриптов:

sudo /etc/init.d/apache2 stop
sudo update-rc.d -f apache2 remove

Используем скрипт для настройки безопасности MySQL:

sudo mysql_secure_installation

Создаем пользователя, к примеру webmaster:

sudo adduser webmaster

Лично я для сборки бинарников из исходных кодов использую виртуальную машину, чтобы не захламлять продакшн сервер, Вы в праве сами выбирать как Вам поступать. Если Вы выбираете сборку на виртуальной машине, Вам необходимо будет установить следующие пакеты:

sudo apt-get install build-essential libssl-dev

Если Вы выберете сборку непосредственно на рабочем сервере, Вам все равно придется поставить эти пакеты, тем самым захламляя систему ненужными для работы библиотеками.

Скачиваем все необходимые для конечной компиляции пакеты:

http://sysoev.ru/nginx/download.html
http://www.pcre.org/
http://www.zlib.net/

Можете воспользоваться для этого утилитой wget:

cd /usr/src
wget http://sysoev.ru/nginx/nginx-0.8.31.tar.gz
wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.00.tar.gz
wget http://www.zlib.net/zlib-1.2.3.tar.gz

Распаковываем:

tar zxvf pcre-8.00.tar.gz
tar zxvf zlib-1.2.3.tar.gz
tar zxvf nginx-0.8.31.tar.gz

Конфигурируем nginx со всеми необходимыми нам модулями:

cd nginx-0.8.31

1
./configure —user=webmaster —group=webmaster —sbin-path=/usr/local/sbin —with-http_ssl_module —with-pcre=../pcre-8.00 —with-zlib=../zlib-1.2.3

Для быстрой сборки deb-пакетов, мы воспользуемся удобной утилитой checkinstall. Если Вы предпочитаете сборку пакетов по всем правилам с включением init-скриптов в пакет, советую так и поступить, но так как все админы в какой-то степени лентяи и не любят выполнять лишние телодвижения, и я в этом случае не исключение, мы не будет собирать пакет по всем правилам, слишком маленькое приложение, чтобы тратить на это свое время. Воспользуемся checkinstall. Установим этот пакет из репозитория:

sudo apt-get install checkinstall

Конфигурацию мы уже сделали, компилируем приложение и собираем deb-пакет:

sudo make
sudo checkinstall

Ответьте на вопросы, в результате в папке с исходниками появится пакет с расширением *.deb, который, в свою очередь, и будет установлен автоматически. Для обновления на новую версию, просто скачиваем свежие соурсы, собираем и переносим на продакшн сервер готовый *.deb пакет и устанавливаем:

sudo dpkg -i *.deb

Привожу пример своего конфига nginx.conf, который находится в папке ‘/usr/local/nginx’. В результате сайты будут добавляться очень просто, в стиле Debian (sites-available, sites-enabled). Конфиг снабжен подробными комментариями. На английском, уж извините, делал пометки для себя (комменты добавлю чуть позже):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
user webmaster webmaster;
worker_processes  4;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

events {
worker_connections  1024;
}

http {
include       mime.types;
default_type  application/octet-stream;

sendfile        on;
#tcp_nopush     on;
tcp_nodelay     off;

keepalive_timeout  2;

gzip  on;
gzip_comp_level 2;
gzip_proxied any;
gzip_types      text/plain text/css application/x-javascript text/xml
application/xml application/xml+rss text/javascript;

include /usr/local/nginx/sites-enabled/*;

}

Создать папки ‘logs’, ‘sites-available’ и ‘sites-enabled’ в ‘/usr/local/nginx’. В папке ‘logs’ создать пустые файлы ‘access.log’ и ‘error.log’.

Создать и сохранить init-script с именем ‘nginx’ в ‘/etc/init.d’:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/bin/bash

### BEGIN INIT INFO
# Provides:          nginx
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the nginx web server
# Description:       starts nginx using start-stop-daemon
### END INIT INFO

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/local/sbin/nginx
NAME=nginx
DESC=nginx

test -x $DAEMON || exit 0

# Include nginx defaults if available
if [ -f /etc/default/nginx ] ; then
. /etc/default/nginx
fi

set -e

. /lib/lsb/init-functions

test_nginx_config() {
if nginx -t $DAEMON_OPTS
then
return 0
else
return $?
fi
}

case "$1" in
start)
echo -n "Starting $DESC: "
test_nginx_config
start-stop-daemon —start —quiet —pidfile /var/run/$NAME.pid \
—exec $DAEMON $DAEMON_OPTS || true
echo "$NAME."
;;
stop)
echo -n "Stopping $DESC: "
start-stop-daemon —stop —quiet —pidfile /var/run/$NAME.pid \
—exec $DAEMON || true
echo "$NAME."
;;
restart|force-reload)
echo -n "Restarting $DESC: "
start-stop-daemon —stop —quiet —pidfile \
/var/run/$NAME.pid —exec $DAEMON || true
sleep 1
test_nginx_config
start-stop-daemon —start —quiet —pidfile \
/var/run/$NAME.pid —exec $DAEMON $DAEMON_OPTS || true
echo "$NAME."
;;
reload)
echo -n "Reloading $DESC configuration: "
test_nginx_config
start-stop-daemon —stop —signal HUP —quiet —pidfile /var/run/$NAME.pid \
—exec $DAEMON || true
echo "$NAME."
;;
configtest)
echo -n "Testing $DESC configuration: "
if test_nginx_config
then
echo "$NAME."
else
exit $?
fi
;;
status)
status_of_proc -p /var/run/$NAME.pid "$DAEMON" nginx && exit 0 || exit $?
;;
*)
echo "Usage: $NAME {start|stop|restart|reload|force-reload|status|configtest}" >&2
exit 1
;;
esac

exit 0

Выставим необходимые права:

sudo chmod +x /etc/init.d/nginx

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

sudo update-rc.d -f nginx defaults

Теперь Вы можете запускать, останавливать и перезапускать nginx, используя следующие команды:

sudo /etc/init.d/nginx start
sudo /etc/init.d/nginx stop
sudo /etc/init.d/nginx restart

Привожу пример настройки виртуального хоста, комментарии присутствуют:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
server {
# 301 redirect on main domain without www
#
listen   80;
server_name  www.zerolab.net;
rewrite ^/(.*) /$1 permanent;
}

server {
listen   80;
server_name zerolab.net;

access_log /home/webmaster/logs/access.log;
error_log /home/webmaster/logs/error.log;

error_page    403    /errors/403.html;
error_page    404    /errors/404.html;
error_page    500 502 503 504    /errors/50x.html;
location    /errors/50x.html {
internal;
}

location / {
root   /home/webmaster/www/zerolab.net;
index  index.php index.html index.htm;
}

# ReWrite rule for web-based frontend to Transmission-daemon
#
location /transmission/ {
rewrite      /transmission[/]?$ /transmission/web break;
proxy_pass   http://127.0.0.1:9091;
}

# Pass the PHP scripts to FastCGI server listening on 127.0.0.1:9090
#
location ~ \.php$ {
fastcgi_pass   127.0.0.1:9090;
fastcgi_index  index.php;
fastcgi_param  SCRIPT_FILENAME  /home/webmaster/www/zerolab.net$fastcgi_script_name;
include fastcgi_params;
}

# Deny access to .htaccess files, if Apache’s document root
# concurs with nginx’s one
#
location ~ /\.ht {
deny  all;
}
}

Включаем виртуальные хосты (создаем симлинк):

sudo ln -s /usr/local/nginx/sites-available/zerolab.net /usr/local/nginx/sites-enabled/zerolab.net

Устанавливаем PHP с поддержкой FastCGI, если еще не установлен:

sudo apt-get install php5-cgi

Скачиваем исходники spawn-fcgi:

http://redmine.lighttpd.net/projects/spawn-fcgi

Воспользуемся wget:

wget http://www.lighttpd.net/download/spawn-fcgi-1.6.3.tar.gz

Распаковываем, конфигурируем и собираем deb-пакет:

tar zxvf spawn-fcgi-1.6.3.tar.gz
cd spawn-fcgi-1.6.3
./configure
sudo make
sudo checkinstall

Создать и сохранить init-script с именем ‘php5-fcgi’ в ‘/etc/init.d’:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/bin/bash
#
### BEGIN INIT INFO
#
# Provides: php5-fcgi
#
# Required-Start: $all
#
# Required-Stop: $all
#
# Default-Start: 2 3 4 5
#
# Default-Stop: 0 1 6
#
# Short-Description: starts the php5-cgi in fast-cgi mode
#
# Description: configure and starts php5-cgi processes in fast-cgi mode using spawn-fcgi
#
### END INIT INFO
#

#
PATH=/sbin:/bin:/usr/sbin:/usr/bin
#
SCRIPTNAME="/etc/init.d/php5-fcgi"
#

#
FCGI_PIDFILE="/var/run/spawn-php5-fcgi.pid"
#
## Абсолютный путь до spawn-fcgi
#
FCGI_DAEMON="/usr/local/bin/spawn-fcgi"
#

#
## PHP переключится на этого юзера и группу (делать такими же, как у nginx)
#
USER=webmaster
#
GROUP=webmaster
#
## Абсолютный путь до php
#
FCGI_PROGRAM="/usr/bin/php5-cgi"
#
## Количество запущенных процессов PHP-fcgi, см http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:PerformanceFastCGI#How-many-PHP-processes-do-I-need
#
PHP_FCGI_CHILDREN=10
#
## Максимальное количество запросов, которое обработает отдельный PHP — fcgi процесс до своего перезапуска
#
PHP_FCGI_MAX_REQUESTS=1000
#
## TCP порт, который будет слушать php-fcgi
#
FCGI_PORT="9090"
#
## IP адреса, по которым будет доступен PHP-fcgi (через запятую)
#
FCGI_IP="127.0.0.1"
#

#
test -x $FCGI_PROGRAM || exit 0
#
test -x $FCGI_DAEMON || exit 0
#

#
set -e
#

#
export PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS
#

#
. /lib/lsb/init-functions
#

#
case "$1" in
#
start)
#
log_daemon_msg "Starting spawn-fcgi"
#
if ! $FCGI_DAEMON -a $FCGI_IP -p $FCGI_PORT -f $FCGI_PROGRAM -u $USER -g $GROUP -C $PHP_FCGI_CHILDREN -P $FCGI_PIDFILE; then
#
log_end_msg 1
#
else
#
log_end_msg 0
#
fi
#
RETVAL=$?
#
;;
#
stop)
#
log_daemon_msg "Killing all spawn-fcgi processes"
#
start-stop-daemon —stop —pidfile $FCGI_PIDFILE —signal 2 && log_end_msg 0 || log_end_msg 1
#
#if killall —signal 2 php5-cgi > /dev/null 2> /dev/null; then
#
# log_end_msg 0
#
#else
#
# log_end_msg 1
#
#fi
#
RETVAL=$?
#
;;
#
restart|force-reload)
#
$0 stop
#
$0 start
#
;;
#
*)
#
echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
#
exit 1
#
;;
#
esac
#

#
exit $RETVAL

Выставим необходимые права:

sudo chmod +x /etc/init.d/php5-fcgi

Обновляем правила rc.d для автоматического запуска php5-fcgi при старте системы:

sudo update-rc.d php5-fcgi defaults

Теперь Вы можете запускать, останавливать и перезапускать spawn-fcgi, используя следующие команды:

sudo /etc/init.d/php5-fcgi start
sudo /etc/init.d/php5-fcgi stop
sudo /etc/init.d/php5-fcgi restart

Проверяем spawning:

ps xa | grep php5-cgi

Смотрим работу нашего веб-сервера:

Каждый раз, когда Вы открываете страничку динамического веб-приложения, веб-сервер обращается к PHP, который загружает запрошенный php-файл, все include() и require(), затем парсит их, компилирует в промежуточный байт-код (opcode) и исполняет. Причем в больших проектах процесс включения всех include файлов может занимать весьма продолжительное время. Поэтому были разработаны многочисленные PHP-кэшеры. Все они позволяют сохранять и повторно использовать скомпилированный байт-код PHP, что позволяет экономить время на сборку всех включений и их компиляцию, экономит процессорное время и оперативную память (причем весьма значительно). Помимо этого, они позволяют хранить в кэше переменные PHP и обращаться к ним при следующем вызове скрипта. Наиболее популярные из кэшеров – APC (Alternative PHP Cache), XCache и eAccelerator. Какой из этих кэшеров использовать — не особо принципиально, по производительности они не сильно отличаются. Я выбрал XCache.

Устанавливаем пакет php5-xcache:

sudo apt-get install php5-xcache

Создаем файл настроек ‘xcache.ini’ в ‘/etc/php5/conf.d’, на примере моего файла настроек:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
[xcache-common]
# configuration for php Xcache module
extension = xcache.so

[xcache.admin]
xcache.admin.auth = On
# Configure this to use admin pages
xcache.admin.user = "ZeroChaos"
; xcache.admin.pass = md5($your_password)
xcache.admin.pass = "7885cccb9b01ee06462b98b2e1aa366b"

[xcache]
; ini only settings, all the values here is default unless explained

; select low level shm/allocator scheme implemenation
xcache.shm_scheme =        "mmap"
; to disable: xcache.size=0
; to enable : xcache.size=64M etc (any size > 0) and your system mmap allows
xcache.size  =                128M
; set to cpu count (cat /proc/cpuinfo |grep -c processor)
xcache.count =                 4
; just a hash hints, you can always store count(items) > slots
xcache.slots =                8K
; ttl of the cache item, 0=forever
xcache.ttl   =                 0
; interval of gc scanning expired items, 0=no scan, other values is in seconds
xcache.gc_interval =           0

; same as aboves but for variable cache
xcache.var_size  =            8M
xcache.var_count =             4
xcache.var_slots =            8K
; default ttl
xcache.var_ttl   =             0
xcache.var_maxttl   =          0
xcache.var_gc_interval =     300

xcache.test =                Off
; N/A for /dev/zero
xcache.readonly_protection = Off
; for *nix, xcache.mmap_path is a file path, not directory.
; Use something like "/tmp/xcache" if you want to turn on ReadonlyProtection
; 2 group of php won’t share the same /tmp/xcache
; for win32, xcache.mmap_path=anonymous map name, not file path
xcache.mmap_path =    "/dev/zero"

; leave it blank(disabled) or "/tmp/phpcore/"
; make sure it’s writable by php (without checking open_basedir)
xcache.coredump_directory =   ""

; per request settings
xcache.cacher =               On
xcache.stat   =               On
xcache.optimizer =            On

[xcache.coverager]
; per request settings
; enable coverage data collecting for xcache.coveragedump_directory and xcache_coverager_start/stop/get/clean() functions (will hurt executing performance)
xcache.coverager =          Off

; ini only settings
; make sure it’s readable (care open_basedir) by coverage viewer script
; requires xcache.coverager=On
xcache.coveragedump_directory = ""

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