Optimizar y reducir el uso de memoria de PHP-FPM

La configuración por defecto que trae PHP-FPM (FastCGI Process Manager) no es funcional en todo tipo de entornos, sobre todo en aquellos sistemas que cuentan con una cantidad limitada o reducida de memoria RAM. Así pues, vamos a ver como optimizar la configuración para entornos con poca RAM y que siga siendo un sistema óptimo para manejar PHP.

Por defecto, la configuración de PHP-FPM levanta un número mínimo de servidores spare, que se mantienen en estado idle a la espera de recibir peticiones, digamos que cara al servicio es más óptimo tener procesos en activos en espera que levantarlos bajo demanda (más lento). El problema de esto es que esos procesos, incluso estando en espera sin hacer nada consumen RAM, y esto provoca que esa RAM no esté disponible para otros servicios o para la caché/buffers del propio sistema.

En una instalación estándar bajo RHEL o CentOS, el fichero de configuración global en el que se encuentran todas las configuraciones es www.conf, la ruta puede variar en función de la instalación:

# pwd
/etc/php-fpm.d
# ls -l
total 40
-rw-r--r-- 1 root root 19578 Apr 23 22:51 www.conf

El punto más importante es ver cómo está configurada la creación de procesos spare/child:

; 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

Como podéis observar, por defecto se generan de forma dinámica, en base a un número inicial, mínimo y máximo de procesos: pm.start_servers, pm.min_spare_servers y pm.max_spare_servers. Esto, como hemos dicho antes, es perfecto para entornos con mucha RAM, porque nos aseguramos de tener siempre un pool de procesos a la espera de procesar peticiones.

Para entornos como poca memoria, es mucho mejor definir el parámetro como «bajo demanda», ondemand, de modo que no habrá un pool mínimo de procesos, sino que se crearán conforme se necesite. Los parámetros importantes a configurar bajo ondemand son:

  • pm.max_children: número máximo de procesos concurrentes de php-fpm (se puede calcular en función de la RAM). Recordad que no tendremos un pool de procesos en espera para procesar peticiones
  • pm.process_idle_timeout: el tiempo que cada uno de esos procesos puede estar en espera para procesar nuevas peticiones antes de ser parado.
  • pm.max_requests: el número de peticiones que ese proceso puede gestionar antes de hacer un respawn (revivir). Este parámetro es útil para evitar memory leaks de aplicación.

Sólo con estos tres parámetros, veréis que cambia por completo la gestión de memoria en vuestro sistema, los valores a configurar depende del entorno, pero por ejemplo podrían ser:

pm = ondemand
pm.max_children = 15
pm.process_idle_timeout = 10s;
pm.max_requests = 500

Una vez realizados los cambios, recordad verificar que la configuración es correcta y después reiniciar php-fpm para aplicarlos:

# php-fpm -t
[01-May-2019 17:17:53] NOTICE: configuration file /etc/php-fpm.conf test is successful
# systemctl restart  php-fpm

A partir de aquí es jugar con los valores de los parámetros en función de vuestras necesidades. Si combináis php-fpm con esta configuración con un sistema de caché de PHP como OPcache seguro que notáis una mejora brutal en el consumo de recursos. ¡Suerte!