Nginx, PHP y FastCGI

sysadmin Deja un comentario

Hace poco adquirí un VPS de 256 MB en slicehost. Para ahorrar RAM decidí utilizar nginx como servidor web en lugar de Apache.

nginx

La web que estoy montando en este VPS constará de una aplicación rails y de un blog basado en WordPress, por lo que será necesario PHP. En este post contaré cómo configurar un virtual host de nginx para que sirva una aplicación PHP mediante FastCGI. La distribución que utilizo es Debian Etch.

Instalación de nginx

Para poder disponer de la última versión estable de nginx (0.5.35) compilaremos el código. El primer paso será instalar algunos paquetes necesarios:

sudo aptitude install libpcre3 libpcre3-dev libpcrecpp0 libssl-dev zlib1g-dev

El “sbin-path” por defecto es “/usr/local/nginx/sbin”, así que al configure le indicaremos uno más conveniente: “/usr/local/sbin”. Asimismo, la compilación la realizaremos con soporte para SSL:

wget http://sysoev.ru/nginx/nginx-0.5.35.tar.gz
tar zxf nginx-0.5.35.tar.gz
cd nginx-0.5.35/
./configure --sbin-path=/usr/local/sbin --with-http_ssl_module
make
sudo make install

Cuando termina el configure obtenemos un resumen con los ficheros más importantes:

nginx path prefix: "/usr/local/nginx"
nginx binary file: "/usr/local/sbin"
nginx configuration file: "/usr/local/nginx/conf/nginx.conf"
nginx pid file: "/usr/local/nginx/logs/nginx.pid"
nginx error log file: "/usr/local/nginx/logs/error.log"
nginx http access log file: "/usr/local/nginx/logs/access.log"
nginx http client request body temporary files: "/usr/local/nginx/client_body_temp"
nginx http proxy temporary files: "/usr/local/nginx/proxy_temp"
nginx http fastcgi temporary files: "/usr/local/nginx/fastcgi_temp"

Arrancamos nginx:

sudo /usr/local/sbin/nginx

y comprobamos que funciona introduciendo en el navegador nuestra URL:

http://midominio.com/

Lo único que nos queda para tener nginx funcionando es crear un script que arranque el servidor web cuando arranque el VPS:

sudo kill `cat /usr/local/nginx/logs/nginx.pid`
sudo vi /etc/init.d/nginx
sudo chmod +x /etc/init.d/nginx
sudo /usr/sbin/update-rc.d -f nginx defaults

Siendo /etc/init.d/nginx:

#! /bin/sh
 
### 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
 
case "$1" in
  start)
        echo -n "Starting $DESC: "
        start-stop-daemon --start --quiet --pidfile /usr/local/nginx/logs/nginx.pid \
                --exec $DAEMON -- $DAEMON_OPTS
        echo "$NAME."
        
  stop)
        echo -n "Stopping $DESC: "
        start-stop-daemon --stop --quiet --pidfile /usr/local/nginx/logs/nginx.pid \
                --exec $DAEMON
        echo "$NAME."
        
  restartforce-reload)
        echo -n "Restarting $DESC: "
        start-stop-daemon --stop --quiet --pidfile \
                /usr/local/nginx/logs/nginx.pid --exec $DAEMON
        sleep 1
        start-stop-daemon --start --quiet --pidfile \
                /usr/local/nginx/logs/nginx.pid --exec $DAEMON -- $DAEMON_OPTS
        echo "$NAME."
        
  reload)
      echo -n "Reloading $DESC configuration: "
      start-stop-daemon --stop --signal HUP --quiet --pidfile /usr/local/nginx/logs/nginx.pid \
          --exec $DAEMON
      echo "$NAME."
      
  *)
        N=/etc/init.d/$NAME
        echo "Usage: $N {startstoprestartforce-reload}" >&2
        exit 1
        
esac
 
exit 0

Configuración del virtual host de nginx que servirá PHP

Supongamos que el virtual host de nginx en el que funcionará el blog basado en WordPress es blog.midominio.com. En este caso, en el fichero de configuración de nginx, que será /usr/local/nginx/conf/nginx.conf, tendremos que añadir, de momento:

    server {
        listen       80
        server_name  blog.midominio.com
 
        access_log /var/log/nginx/blog.midominio.com/access.log
        error_log  /var/log/nginx/blog.midominio.com/error.log
 
        location / {
            root   /var/www/blog.midominio.com
            index  index.html
        }
 
        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html
        location = /50x.html {
            root   html
        }
    }

El directorio para los logs, /var/log/nginx/blog.midominio.com, y el directorio para las páginas, /var/www/blog.midominio.com, pueden ser otros. En cualquier caso, los deberemos crear a mano. Después de cualquier cambio en el fichero de configuración de nginx es necesario un restart:

sudo /etc/init.d/nginx restart

Llegados a este punto, podemos crear un index.html en /var/www/blog.midominio.com y ver que carga correctamente en el navegador:

http://blog.midominio.com/

Instalación de PHP

La instalación de PHP es realmente sencilla:

$ sudo apt-get install php5-cli php5-cgi php5-mysql
$ php -v
PHP 5.2.0-8+etch9 (cli) (built: Dec 29 2007 14:49:25) 
Copyright (c) 1997-2006 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2006 Zend Technologies

Instalación de FastCGI

La implementación de FastCGI que vamos a utilizar es la que viene con lighttpd. Lo que haremos será compilar lighttpd, pero no instalarlo. Copiaremos a mano el proceso spawn-fcgi:

wget http://www.lighttpd.net/download/lighttpd-1.4.18.tar.gz
gunzip lighttpd-1.4.18.tar.gz
tar xvf lighttpd-1.4.18.tar
cd lighttpd-1.4.18/
./configure
make
sudo cp src/spawn-fcgi /usr/bin/spawn-fcgi

Ahora que ya tenemos FastCGI, crearemos un script para lanzarlo, al que llamaremos /usr/bin/php-fastcgi, y cuyo contenido será:

#!/bin/sh
/usr/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -u www-data -f /usr/bin/php5-cgi

Crearemos también el script de arranque y parada para cuando rearranquemos el servidor. Le llamaremos /etc/init.d/php-fastcgi, y su contenido será:

#!/bin/bash
PHP_SCRIPT=/usr/bin/php-fastcgi
RETVAL=0
case "$1" in
    start)
      $PHP_SCRIPT
      RETVAL=$?
  
    stop)
      killall -9 php5-cgi
      RETVAL=$?
  
    restart)
      killall -9 php5-cgi
      $PHP_SCRIPT
      RETVAL=$?
  
    *)
      echo "Usage: php-fastcgi {startstoprestart}"
      exit 1
  
esac      
exit $RETVAL

Damos los permisos adecuados y añadimos php-fastcgi a los scripts de arranque y parada:

sudo chmod 755 /etc/init.d/php-fastcgi
sudo chmod 755 /usr/bin/php-fastcgi
sudo /usr/sbin/update-rc.d -f php-fastcgi defaults

Nos queda volver a editar el fichero de configuración de nginx, /usr/local/nginx/conf/nginx.conf, donde añadiremos, en la sección del servidor virtual que hemos creado, la configuración necesaria para que los scripts PHP sean redireccionados al proceso FastCGI. También en esta sección, deberemos cambiar la página de inicio de index.html a index.php y añadir una regla de reescritura para que WordPress funcione correctamente. Con todos estos cambios, la sección completa del servidor virtual de nginx para el blog quedaría:

    server {
        listen       80
        server_name  blog.midominio.com
 
        access_log /var/log/nginx/blog.midominio.com/access.log
        error_log  /var/log/nginx/blog.midominio.com/error.log
 
        location / {
            root   /var/www/blog.midominio.com
            index  index.php
            if (-e $request_filename) {
              break
            }
            rewrite ^/(.+)$ /index.php?q=$1 last
        }
 
        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        location ~ \.php$ {
            fastcgi_pass   127.0.0.1:9000
            fastcgi_index  index.php
            fastcgi_param  SCRIPT_FILENAME  /var/www/blog.midominio.com$fastcgi_script_name
            include        conf/fastcgi.conf
        }
 
        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html
        location = /50x.html {
            root   html
        }
    }

El fichero de configuración de FastCGI será /usr/local/nginx/conf/fastcgi.conf:

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
 
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
 
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200

Para probar que funcionar, después de rearrancar nginx, podemos crear en /var/www/blog.midominio.com el siguiente fichero index.php:

<? phpinfo(); ?>

Si introducimos en el navegador la URL http://blog.midominio.com obtendremos una página con información sobre nuestra instalación de PHP.

Enlaces relacionados

4 comentarios a “Nginx, PHP y FastCGI”

  1. miguel dice:

    Hola!, me ha sido de gran ayuda tu articulo, tengo todo funcionando en su lugar :D,
    nginx + php + rail , gracias!

  2. Jaime Iniesta dice:

    Hey Javier, muchas gracias por tu artículo! Me viene genial porque ahora uso nginx+mongrel pero estoy pensando meter también php para algunos dominios con phpbb y wordpress, y me daba pereza tener que cambiar a Apache. No sabía que se podía hacer con nginx…

    Bueno, ahora tendré que decidir si cambiar a mod_rails o seguir un tiempo con mongrel.

  3. Alejandro dice:

    Hola, interesante artículo. Pero me da curiosidad que no lo hayas instalado desde los repositorios oficiales de debian, ¿a que se debe?

    Saludos

  4. Javier Vidal Postigo dice:

    Si no recuerdo mal, la versión de nginx disponible en el repositorio de debian era bastante inferior a la última versión estable disponible de nginx.

Deja un comentario