Opción: Configurar PHP-FPM FastCGI

Otro enfoque para la configuración de Apache + PHP es usar el nuevo PHP-FPM (FPM siglas para FastCGI Process Manager), gestiona los procesos de PHP lanzados en su propio servidor (fuera de Apache) lo que te permite hacer algunas cosas interesantes:

  • Mejor control de procesos (mínimo y máximo número de procesos y procesos de control disponibles)
  • Mejor gestión de plugins PHP como APC frente a mod_php
  • chrooting (enjaulado), por lo que podemos aislar aplicaciones o hosts virtuales.
  • Ejecutar procesos con un uid/gid específico (esto se podría hacer con el módulo de apache suExec)
  • Mejor rendimiento, en mis pruebas, se ejecuta un 10% más rápido que CGI.
  • Etc.

No podemos usar mod_fcgid (como lo hicimos desde el enfoque CGI), porque no podemos conectarnos a cualquier servidor externo FastCGI, por ello, utilizaremos mod_fastcgi (un poco más antiguo pero útil). En Apache 2.4 podríamos utilizar el nuevo mod_proxy_fcgi.

Instalación de mod_fastcgi (Apache 2.2) y php-fpm

Si usamos una distribución Debian/Ubuntu:

$ sudo apt-get install php5-fpm libapache2-mod-fastcgi

Configuración de Apache 2.2

Lo primero que debemos hacer es habilitar el módulo:

$ sudo a2enmod fastcgi

También tendremos que habilitar el módulo de acciones (si no está ya habilitado):

$ sudo a2enmod actions

Tendremos que crear un archivo de configuración de php-FPM para Apache, como el siguiente (/etc/apache2/conf.d/php-fpm.conf):

<IfModule mod_fastcgi.c>
       Alias /php5-fpm-cgi /usr/lib/cgi-bin/php5-fcgi
       FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -socket /var/run/php-fpm.sock -pass-header Authorization
</IfModule>

Ahora, vamos a editar nuestra configuración del VirtualHost (he tomado el ejemplo que se encuentra en el documento de Instalación y configuración de un servidor web, y he resaltado los cambios en negrita):

<VirtualHost *:80>
        ServerAdmin info@totaki.com
        ServerName totaki.com
        ServerAlias www.totaki.com

        DocumentRoot /home/cloud/www/totaki.com/www
        <Directory />
                Options FollowSymLinks
                AllowOverride All
        </Directory>
        <Directory /home/cloud/www/totaki.com/www/>
                Options FollowSymLinks MultiViews
                AllowOverride All
                Order allow,deny
                allow from all
        </Directory>
        <IfModule mod_fastcgi.c>
                <FilesMatch ".+\.ph(p[345]?|t|tml)$">
                    SetHandler application/x-httpd-php
                </FilesMatch>

                Action application/x-httpd-php /php5-fpm-cgi
        </IfModule>
        ErrorLog    "/home/cloud/www/totaki.com/logs/error.log"        
        LogLevel warn        
        CustomLog "/home/cloud/www/totaki.com/logs/access.log" combined       
</VirtualHost>

Esto también se puede hacer de modo que afecte a todo el servidor con tan solo poner un nuevo archivo /etc/apache2/conf.d/, pero he preferido hacerlo a través de un VirtualHost para lograr un mayor control.

Utilizaremos el servidor cgi en el socket /var/run/php-ftm.sock (podemos configurar un servidor remoto, para distribuir la carga, preferiblemente en el mismo datacenter, podríamos acceder a través de LAN, por lo que será más rápido), pero cuando se configura en la misma máquina se ejecutará sin problemas como un socket UNIX.

Configuración en Apache 2.4

En esta nueva versión de Apache, hay ocasiones en las que mod_fcgi no se puede instalar en nuestros servidores, por lo que debemos usar otro módulo como mod_proxy_fcgi. Esto nos ofrece una gran cantidad de novedades interesantes que podemos utilizar.

En versiones anteriores a Apache 2.4.4 sólo podemos usarlo con PHP-FPM escuchando en un puerto (no en un socket de UNIX). Podemos editar nuestro VirtualHost de la siguiente manera:

<VirtualHost *:80>
        ServerAdmin info@totaki.com
        ServerName totaki.com
        ServerAlias www.totaki.com

        DocumentRoot /home/cloud/www/totaki.com/www
        ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/home/cloud/www/totaki.com/www/$1

        <Directory />
                Options FollowSymLinks
                AllowOverride All
        </Directory>
        <Directory /home/cloud/www/totaki.com/www/>
                Options FollowSymLinks MultiViews
                AllowOverride All
                Require all grantes
        </Directory>

        ErrorLog    "/home/cloud/www/totaki.com/logs/error.log"        
        LogLevel warn        
        CustomLog "/home/cloud/www/totaki.com/logs/access.log" combined       
</VirtualHost>

Y eso es todo. Lo peor es que hay que especificar la ruta complete en cada VirtualHost. Pero hay ciertas cosas importantes que podemos hacer como usar un equilibrador de carga para ese proxy, o hacer una configuración global en las últimas versiones. Más información aquí.

Configurando PHP-FPM

Tenemos que editar /etc/php5/fpm/pool.d/www.conf para cambiar algunos ajustes. Las opciones del servidor para el número de procesos pueden depender del conjunto CPU+RAM de tu servidor, por lo que puedes ajustarlo más tarde.
El archivo más o menos sería como el siguiente (he marcado los cambios en negrita):

; Start a new pool named 'www'.
; the variable $pool can we used in any directive and will be replaced by the
; pool name ('www' here)
[www]

; Per pool prefix
; It only applies on the following directives:
; - 'slowlog'
; - 'listen' (unixsocket)
; - 'chroot'
; - 'chdir'
; - 'php_values'
; - 'php_admin_values'
; When not set, the global prefix (or /usr) applies instead.
; Note: This directive can also be relative to the global prefix.
; Default Value: none
;prefix = /path/to/pools/$pool

; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
;       will be used.
user = www-data
group = www-data
; The address on which to accept FastCGI requests.
; Valid syntaxes are:
;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific address on
;                            a specific port;
;   'port'                 - to listen on a TCP socket to all addresses on a
;                            specific port;
;   '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
;listen = 127.0.0.1:9000
listen = /var/run/php-fpm.sock
; Set listen(2) backlog. A value of '-1' means unlimited.
; Default Value: 128 (-1 on FreeBSD and OpenBSD)
;listen.backlog = -1

; Set permissions for unix socket, if one is used. In Linux, read/write
; permissions must be set in order to allow connections from a web server. Many
; BSD-derived systems allow connections regardless of permissions.
; Default Values: user and group are set as the running user
;                 mode is set to 0666
listen.owner = www-data
listen.group = www-data
;listen.mode = 0666

; List of ipv4 addresses of FastCGI clients which are allowed to connect.
; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address
; must be separated by a comma. If this value is left blank, connections will be
; accepted from any ip address.
; Default Value: any
;listen.allowed_clients = 127.0.0.1

; Choose how the process manager will control the number of child processes.
; Possible Values:
;   static  - a fixed number (pm.max_children) of child processes;
;   dynamic - the number of child processes are set dynamically based on the
;             following directives. With this process management, there will be
;             always at least 1 children.
;             pm.max_children      - the maximum number of children that can
;                                    be alive at the same time.
;             pm.start_servers     - the number of children created on startup.
;             pm.min_spare_servers - the minimum number of children in 'idle'
;                                    state (waiting to process). If the number
;                                    of 'idle' processes is less than this
;                                    number then some children will be created.
;             pm.max_spare_servers - the maximum number of children in 'idle'
;                                    state (waiting to process). If the number
;                                    of 'idle' processes is greater than this
;                                    number then some children will be killed.
;  ondemand - no children are created at startup. Children will be forked when
;             new requests will connect. The following parameter are used:
;             pm.max_children           - the maximum number of children that
;                                         can be alive at the same time.
;             pm.process_idle_timeout   - The number of seconds after which
;                                         an idle process will be killed.
; Note: This value is mandatory.
pm = dynamic

; The number of child processes to be created when pm is set to 'static' and the
; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
; This value sets the limit on the number of simultaneous requests that will be
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
; CGI. The below defaults are based on a server without much resources. Don't
; forget to tweak pm.* to fit your needs.
; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
; Note: This value is mandatory.
pm.max_children = 50

; The number of child processes created on startup.
; Note: Used only when pm is set to 'dynamic'
; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
pm.start_servers = 30
; The desired minimum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.min_spare_servers = 20

; The desired maximum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.max_spare_servers = 40

; The number of seconds after which an idle process will be killed.
; Note: Used only when pm is set to 'ondemand'
; Default Value: 10s
;pm.process_idle_timeout = 10s;

; The number of requests each child process should execute before respawning.
; This can be useful to work around memory leaks in 3rd party libraries. For
; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
; Default Value: 0
pm.max_requests = 10000

… El resto del archivo se mantiene sin cambios (el mío estaba todo comentado), pero vale la pena decir que puedes tener un log de scripts lentos mostrando la traza inversa de los mismos y podemos cambiar las opciones php.ini desde este archivo. Ahora, todo lo que tienes que hacer es reiniciar el demonio php5-fpm:

$ sudo service php5-fpm restart

y Apache:

$ sudo service php5-fpm restart

Habilitar ping y status de FPM

Podemos solicitar el ping y el status del demonio FPM añadiendo unas pocas líneas de código. Primero, en /etc/php5/fpm/pool.d/www.conf, añadimos o quitamos el comentario de las siguientes líneas:

pm.status_path = /status
ping.path = /ping
;ping.response = pong

La última línea, puede ser una palabra como respuesta a una señal de ping.
Por lo tanto, en nuestro archivo VirtualHost, añadimos lo siguiente:

    <FilesMatch "^(ping|status)$">
                SetHandler application/x-httpd-php
                Action application/x-httpd-php /php5-fpm-cgi virtual
    </FilesMatch>

En este caso, FilesMatch solamente trabajará cuando la URL termine con la palabra ping o status (se puede usar también LocationMatch “/status” y LocationMatch “/ping” en su lugar. La línea de acción permitirá pasar al demonio CGI un archivo no existente (dado que tenemos que definir una palabra clave virtual).

Recuerda asegurarlo, o la gente tendrá acceso a estas dos URLs, podemos utilizar lo siguiente:

    <FilesMatch "^(ping|status)$">
                SetHandler application/x-httpd-php
                Action application/x-httpd-php /php5-fpm-cgi virtual
                Order deny,allow
                Deny from all
                Allow from [my-ip]
    </FilesMatch>

Donde [my-ip] es tu dirección IP pública (o quizás 127.0.0.1 para un ping local).

Crear pools para diferentes configuraciones php-fpm

Si quieres crear una nueva configuración FPM para unVirtualHost diferente para restringir parámetros, cambia el usuario o aíslalo, simplemente editando la configuración de apache php-fpm (/etc/apache2/conf.d/php-fpm.conf) (usando negrita para nuevos ajustes):

<IfModule mod_fastcgi.c>
        Alias /php5-fpm-cgi /usr/lib/cgi-bin/php5-fcgi
        FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -socket /var/run/php-fpm.sock -pass-header Authorization

        Alias /php5-fpm-heavy-cgi /usr/lib/cgi-bin/php5-hfcgi
        FastCgiExternalServer /usr/lib/cgi-bin/php5-hfcgi -socket /var/run/php-fpm2.sock -pass-header Authorization
</IfModule>

En este caso, estamos creando otro Alias llamado /php5-fpm-heavy-cgi donde pondremos la configuración para sitios de carga pesada (lanzar menos procesos hijos, por ejemplo), nótese que estoy usando /var/run/php-fpm2.sock, por lo que voy a crear una nueva configuración de pool, copiando primeramente la ya existente:

$ sudo cp /etc/php/fpm/pool.d/www.conf /etc/php/fpm/pool.d/heavy_www.conf

y entonces, editamos el archivo. Lo más importante es cambiar la línea con [www] a otra cosa, como una forma de nombrar nuestro nuevo pool, entonces cambia el valor bajo listen a /var/run/php-fpm2.sock, luego puedes cambiar lo que desees.
El último aspecto es usar la nueva configuración en un VirtualHost, simplemente cambiando

Action application/x-httpd-php /php5-fpm-cgi

por

Action application/x-httpd-php /php5-fpm-heavy-cgi

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *