Preparando un servidor Web con Debian 9 y php7.3-fpm


El gran error común cuando se prepara un servidor Web con PHP en distribuciones Debian/Ubuntu es mantener la configuración predeterminada en el entorno de producción. Es decir, se instala Apache2 y después el módulo de Apache2 para PHP libapache2.mod-php.

Pues puedes estar seguro que más temprano que tarde, el servidor no podrá con las peticiones Web y como intentes arreglarlo incrementando valores en /etc/apache2/mod_enabled/mpm_worker.conf tumbarás el servidor por completo.

En este post muestro la configuración que suelo usar en la mayoría de los servidores Debian:

  • Apache sin el módulo PHP y con MPM-Event
  • PHP-FPM: FastCGI Process Manager
  • Versión 7.3 de PHP, la versión 7.0 no tiene soporte desde diciembre de 2018.

Instalando PHP-FPM

Como ya indiqué la instalación Apache2 + PHP de las distribuciones usan el módulo MPM Prefork de Apache2. Esta combinación consume muchos recursos y no es más rápido que otras configuraciones. Hay mucha información en la red explicándolo:

Sin entrar en discusiones, la mejor configuración para el servidor Apache2 es Event-based. Se puede leer una explicación en MPM de Apache event. Para activar esta configuración en primer lugar instalar el paquete php-fpm con.

apt install php-fpm

Tras la instalación en /etc/apache2/conf-available aparece el fichero php7.0-fpm.conf. Esta configuración se puede activar globalmente o incluir localmente en los virtualhosts deseados. La activación de esta configuración es manual, e incompatible con la configuración por predeterminada. Así que hay que desactivar el módulo mpm-prefork, activar el módulo fastcgi y mpm_event:

a2dismod php7.0 mpm_prefork
a2enmod mpm_event proxy_fcgi
a2enconf php7.0-fpm

De paso, en el último comando he activado globalmente la configuración de php7.0-fpm de Apache2 aunque posteriormente mostraré como hacer convivir varias versiones de PHP en el mismo servidor.

Configurando PHP-FPM

PHP-FPM funciona ejecutando uno o varios procesos separados del intérprete de PHP siendo todo esto configurable. Una explicación rápida: éste crea un socket hacia el cual se redirigen las peticiones Web de Apache u otro servidor Web. Por ello, php-fpm se ejecuta como un servicio separado y está completamente integrado en systemd.

La configuración de este proceso está en /etc/php/7.0/fpm/ aquí se encuentra el fichero php.ini donde establecer los valores requeridos por las webs pero esto no es objeto de este post. Otro directorio importante es /etc/php/7.0/fpm/pool.d donde se ubican las configuraciones para los procesos/servidores del servicio php-fpm. Inicialmente se va a configurar un único servidor php-fpm, posteriormente indicaré las ventajas de tener alguno adicional y como se configura.

Editando el fichero predeterminado ubicado en /etc/php/7.0/fpm/pool.d/www.conf aparece una configuración que no me satisface, principalmente porque el servicio está en modo dinámico. Cuando opera en este modo se crear procesos según los necesita, personalmente prefiero cambiarlo a estático y así se tiene controlado en todo momento los máximos recursos que usará PHP, además, responde más rápido. La configuración para una máquina con 4 núcleos y 8 hilos es la mostrada a continuación y se cambia en el fichero /etc/php/7.0/fpm/pool.d/www.conf:

pm = static
pm.max_children = 8

Tras los cambios, se reinicia con systemctl restart php7.0-fpm y puedes ver los procesos con pstree -c, y, debe aparecer un proceso master y sus 8 subprocesos:

├─php-fpm7.0─┬─php-fpm7.0
│            ├─php-fpm7.0
│            ├─php-fpm7.0
│            ├─php-fpm7.0
│            ├─php-fpm7.0
│            ├─php-fpm7.0
│            └─php-fpm7.0

Esos 8 sub-procesos son procesos separados, no hilos, posteriormente cuando lleven tiempo en ejecución cada subproceso iniciará algunos hilos, según la carga del servidor. Si todo ha ido bien, sólo hay que reiniciar Apache2 para que comience a usar esta configuración. Además se puede hacer un poco de limpieza eliminando el módulo de apache no necesitado:

apt remove libapache2-mod-php7.0

Actualizando a PHP7.3

Debian 9 tiene ya algunos años y muchos paquetes tienen versiones obsoletas de software. No existe un sistema similar a PPA de Ubuntu para añadir repositorios extra de software. Así que el proceso consiste en añadir fuentes extra a APT. Se debe tener cuidado con integrar en un sistema estable software no estable, de todas formas, mi experiencia es positiva hasta el momento.

Para actualizar la versión de PHP la opción más usada es añadir el repositorio del empaquetador oficial de Debian de PHP. Su página Web está en https://deb.sury.org y contiene las instrucciones, de todas formas las indico aquí.

Primer paso, por si no está instalado, añadir soporte HTTPS a APT con:

apt install apt-transport-https

Después, para evitar problemas con fuentes de software no firmadas hay que importar la clave pública GPG del empaquetador de PHP con:

wget -q -O - https://packages.sury.org/php/apt.gpg | apt-key add -

y finalmente sólo hay que añadir la nueva fuente de software, por ejemplo, en un nuevo fichero en /etc/apt/sources.list.d/php.list con el contenido:

deb https://packages.sury.org/php/ stretch main

Al tener PHP funcionando con FPM, es posible ejecutar varias versiones simultáneamente y se puede migrar a PHP 7.3 sin interrumpir el servicio. Otro detalle para que la migración finalice bien, se pueden listar las extensiones instaladas con PHP 7.0 para instalar las mismas pero en versión 7.3 y así minimizar los fallos en las Web que tengamos operativos:

apt list --installed | grep php7.0

Una vez terminado este proceso se tienen instalado php7.3-fpm como servicio en systemd, el cual se ejecuta de manera independiente a la versión 7.0. Se deben repetir los pasos de configuración mostrados en la sección anterior de este post para configurarlo.

A partir de aquí tendrás 2 versiones de PHP ejecutándose y puedes decidir cuando usar cada una de ellas. Se puede comprobar listando el directorio /etc/apache2/conf-available donde aparece el fichero php7.3-fpm.conf, que se puede activar globalmente con a2enconf o incluir en los virtualhost deseados.

Separando servicios

Otra opción interesante es separar los procesos PHP que sirven cada una de las Webs. Con PHP-FPM es fácil ya que se pueden crear nuevos pools de servicio. En el ejemplo que muestro separo una Web encargada de servidor WebMail con RoundCube. Con esto, cuando fallan las páginas de los alojamientos, los usuarios que conectan al WebMail no se quedan sin servicio.

La configuración consiste en crear un pool extra copiando copiando el fichero /etc/php/7.3/fpm/pool.d/www.conf como mail.conf y realizar los siguientes cambios:

  1. Cambiar el nombre del pool.
  2. Cambiar el socket para usarlo desde Apache.

Muestro sólo los cambios necesarios en el nuevo fichero mail.conf:

[mail]
...
listen = /run/php/php7.3-fpm-mail.sock
...
pm = static
pm.max_children = 1

Con las pruebas qye he hecho, para servir peticiones de unos 1000 usuarios de correo con RoundCube me basta con un pool de un único proceso.

systemctl restart php7.3-fpm

Tras el reinicio anterior se puede comprobar con htop (mejor que con pstree) que aparece un pool extra llamado mail.

Finalmente hay que configurar Apache2 para usar este pool indicando en el Virtualhost correspondiente que se debe usar el socket /run/php/php7.3-fpm-mail.sock para PHP. Hay dos opciones:

  1. Incluir la configuración manualmente en el Virtualhost.
  2. Crear un archivo .conf para incluirlo en cada Web que queramos servir con este pool.

Personalmente me decanto por la segunda, así en /etc/apache2/config-available creo el fichero php7.3-fpm-mail.conf con el contenido:

# Enable http authorization headers
<IfModule setenvif_module>
    SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1
</IfModule>
<FilesMatch ".+\.ph(p[3457]?|t|tml)$">
    SetHandler "proxy:unix:/run/php/php7.3-fpm-mail.sock|fcgi://localhost"
</FilesMatch>
<FilesMatch ".+\.phps$">
    \# Deny access to raw php sources by default
    \# To re-enable it's recommended to enable access to the files
    \# only in specific virtual host or directory
    Require all denied
</FilesMatch>

\# Deny access to files without filename (e.g. '.php')
<FilesMatch "^\.ph(p[3457]?|t|tml|ps)$">
    Require all denied
</FilesMatch>

En realidad este fichero es una copia de la configuración por defecto cambiando el socket. Después basta con incluirlo en el Virtualhost que sirve el WebMail

<VirtualHost>
...
    Include /etc/apache2/conf-available/php7.3-fpm-mail.conf
...
</Vitualhost>
#Apache #Debian #PHP