
Serwer na którym postawiony jest ten blog to "słaby" dedyk z oferty OVH, z zainstalowanym Debianem. Aktualnie obsługuje kilkanaście stron z dość małym natężeniem ruchu (głównie proste, statyczne wizytówki). Całość śmiga na serwerze Apache i mod_php. Ostatnio, spodziewając się zwiększonego ruchu na jednej z tych stron, postanowiłem zwiększyć wydajność poprzez instalację serwera WWW Nginx.
Migracja nie była taka prosta gdyż niektóre z wspomnianych serwisów korzystały z rozszerzeń Apache. Nie chcąc tracić czasu na przerabianie ich, podszedłem do problemu w następujący sposób:
- umieściłem Nginx "ponad" apache tak aby ten pierwszy kierował cały ruch do drugiego;
- dodając kolejne wirtualne hosty decydowałem która strona ma być serwowana wyłącznie przez Nginx.
Instalacja oprogramowania
Jedynym komponentem którego brakowało na serwerze był sam Nginx. Znajduje się on w repozytorium Debiana, a więc instalacja to jedno polecenie:
# apt-get install nginx
Nginx jako Reverse Proxy
Pierwszym etapem będzie postawienie Nginxa "ponad" Apachem i kierowanie całego ruchu do tego drugiego.
Najpierw zmieniamy port na którym nasłuchuje Apache z 80 na 8080. W Debianie edytujemy plik /etc/apache2/ports.conf:
NameVirtualHost *:8080 Listen *:8080
Główna konfiguracja znajduje się w pliku /etc/nginx/nginx.conf:
user www-data;
worker_processes 1;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
sendfile on;
keepalive_timeout 65; # Nad tym można się zastanowić
tcp_nodelay on;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Jak widać dołączone będą wszystkie pliki z katalogu /etc/nginx/sites-enabled. Jest to mechanizm znany z Apache: wszystkie hosty zdefiniowane są w osobnych plikach w /etc/nignx/sites-available, a włączamy je poprzez utworzenie linków w /etc/nginx/sites-enabled.
Pierwszy wirtualny host będzie nasłuchiwał na porcie 80, a następnie kierował cały ruch do Apacza:
Plik /etc/nginx/sites-available/default:
server {
listen 80;
server_name localhost;
access_log /var/log/nginx/localhost.access.log;
location / {
proxy_pass http://127.0.0.1:8080/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/www/nginx-default;
}
}
Można już zrestartować Apache i uruchomoć nginx:
# /etc/init.d/apache2 restart # /etc/init.d/nginx start
Od tej pory Nginx będzie działał ponad Apaczem, kierując do niego wszystkie żądania.
Tutaj zrobiłem mały przystanek aby sprawdzić czy wszystkie strony które obsługuje serwer dalej działają poprawnie. Jak się okazało - działały :-)
Konfiguracja hostów
Załóżmy że mamy wykupioną domenę "mojastrona.pl" i chcemy podpiąć ją do katalogu /var/www/mojastrona.
Tworzymy plik /etc/nginx/sites-available/mojastrona:
server {
listen 80;
server_name mojastrona.pl;
root /var/www/mojastrona.pl
location / {
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include /etc/nginx/fastcgi_params;
}
}
Tworzymy dowiązanie, aby włączyć stronę:
ln -s /etc/nginx/sites-available/mojastrona /etc/nginx/sites-enabled/mojastrona
Sekcja "location ~ \.php$" mówi serwerowi w jaki sposób ma traktować pliki PHP.
PHP w trybie fastcgi
Plik /etc/nginx/fastcgi_params odpowiada za parametry przekazywane interpreterowi PHP przez serwer. Warto wymienić przede wszystkim jeden z nich: SCRIPT_FILENAME. Jeśli podana tutaj wartość będzie nieprawidłowa to PHP zwróci komunikat "no input file specified".
fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; fastcgi_param REMOTE_ADDR $remote_addr; fastcgi_param REMOTE_PORT $remote_port; fastcgi_param SERVER_ADDR $server_addr; fastcgi_param SERVER_PORT $server_port; fastcgi_param SERVER_NAME $server_name; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param REDIRECT_STATUS 200;
Ostatnia sprawa to skonfigurowanie intepretera PHP aby cały czas był uruchomiony i nasłuchiwał na porcie 9000. Posłużę się tutaj gotowym skryptem który znalazłem gdzieś w sieci.
Skrypt startowy - /etc/init.d/php-fastcgi:
#! /bin/sh
### BEGIN INIT INFO
# Provides: php-fastcgi
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start and stop php-cgi in external FASTCGI mode
# Description: Start and stop php-cgi in external FASTCGI mode
### END INIT INFO
# Author: Kurt Zankl <[EMAIL PROTECTED]>
# Do NOT "set -e"
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="php-cgi in external FASTCGI mode"
NAME=php-fastcgi
DAEMON=/usr/bin/php-cgi
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
PHP_CONFIG_FILE=/etc/php5/cgi/php.ini
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions
# If the daemon is not enabled, give the user a warning and then exit,
# unless we are stopping the daemon
if [ "$START" != "yes" -a "$1" != "stop" ]; then
log_warning_msg "To enable $NAME, edit /etc/default/$NAME and set START=yes"
exit 0
fi
# Process configuration
export PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS
DAEMON_ARGS="-q -b $FCGI_HOST:$FCGI_PORT -c $PHP_CONFIG_FILE"
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \
--background --make-pidfile --chuid $EXEC_AS_USER:$EXEC_AS_GROUP --startas $DAEMON -- \
$DAEMON_ARGS \
|| return 2
}
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE > /dev/null # --name $DAEMON
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to
# sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
restart|force-reload)
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
exit 3
;;
esac
Plik konfiguracyjny: /etc/default/php-fastcgi:
START=yes # Which user runs PHP? (default: www-data) EXEC_AS_USER=www-data EXEC_AS_GROUP=www-data # Host and TCP port for FASTCGI-Listener (default: localhost:9000) FCGI_HOST=localhost FCGI_PORT=9000 # Environment variables, which are processed by PHP PHP_FCGI_CHILDREN=4 PHP_FCGI_MAX_REQUESTS=1000
Uruchamiamy PHP:
# /etc/init.d/php-fastcgi start
Zakończenie
Restartujemy nginxa:
# /etc/init.d/nginx restart
Uwaga: przedstawione pliki konfiguracyjne będą działy tylko na Debianie i debiano-podobnych odmianach Linuksa. Uruchomienie tego na innym systemie będzie wymagało kilku (niezbyt skomplikowanych) modyfikacji.
Zend Framework
Nginx zawiera mechanizm przepisywania adresów, jednak jego konfiguracja jest zupełnie inna od tej z Apache. Jeżeli chcemy przepisywać urle w stylu Zenda, a więc kierować żądania nieistniejących plików do skryptu index.php, możemy posłużyć się następującą regułą (umieszczoną w sekcji server w pliku /etc/nginx/sites-available/mojastrona):
if (!-e $request_filename) {
rewrite ^.*$ /index.php last;
}