Tal vez recordarán que hace algunos días publiqué mi artículo explicando detalladamente cómo instalar y configurar Nginx con PHP-FPM. También supongo recordarán que había prometido hacer una comparación con Apache MPM prefork con PHP como módulo, en lo que a consumo de memoria respecta. Cumplo entonces mi promesa con este breve artículo, donde además comparto mi configuración final de PHP-FPM.

Cuando monté el VPS utilicé un clásico esquema LAMP (Linux Apache PHP MySQL), por estar familiarizado con Apache. Pero como el VPS posee poca memoria RAM (256 MB), estaba muy al límite en cuanto a consumo, utilizando incluso algo de swap (malo para el rendimiento). Por esta razón llegué a sufrir un par de caídas de MySQL al quedarse sin memoria disponible:

Aug 18 20:38:03 linuxito kernel: [25158481.064581] Out of memory in UB 41810: OOM killed process 14134 (mysqld) score 0 vm:467748kB, rss:21732kB, swap:59472kB

Por ello tuve la necesidad de migrar a Nginx con PHP en modo CGI, un esquema mucho más eficiente en lo que a consumo de memoria respecta (al menos eso prometía).

Un snapshot de los procesos cuando utilizaba Apache, con PHP como módulo, reportaba que consumía algo más de 53 MB residentes en memoria física:

root@linuxito:~# date
Thu Aug 20 11:46:19 EDT 2015
root@linuxito:~# ps axl | grep apache
1    0  1857     1  20   0 158864   980 poll_s Ss   ?       0:05 /usr/sbin/apache2 -k start
5   33  8112  1857  20   0 162228 11144 semtim S    ?       0:01 /usr/sbin/apache2 -k start
5   33  8114  1857  20   0 168116 17248 semtim S    ?       0:00 /usr/sbin/apache2 -k start
5   33  8123  1857  20   0 167876 16488 semtim S    ?       0:00 /usr/sbin/apache2 -k start
5   33  8125  1857  20   0 159216  2436 poll_s S    ?       0:00 /usr/sbin/apache2 -k start
5   33  8129  1857  20   0 159184  2396 semtim S    ?       0:00 /usr/sbin/apache2 -k start
5   33  8146  1857  20   0 159184  2192 semtim S    ?       0:00 /usr/sbin/apache2 -k start
5   33  8147  1857  20   0 158896   880 semtim S    ?       0:00 /usr/sbin/apache2 -k start
5   33  8148  1857  20   0 158896   900 inet_c S    ?       0:00 /usr/sbin/apache2 -k start
0    0  8151  8137  20   0   7724   936 pipe_w S+   pts/0   0:00 grep apache

Luego de reemplazar Apache por Nginx con PHP-FPM debo decir que no logré mejores resultados en cuanto a consumo de memoria. Esto se debió a que había configurado al gestor de procesos de FPM en modo dinámico (la cantidad de procesos hijos varía dinámicamente, pero siempre existe un mínimo en ejecución).

Respecto a Nginx, su consumo de memoria es realmente muy bajo, la mayor parte se la lleva el motor FPM de PHP. Por ende decidí configurar el gestor de procesos de FPM en modo "ondemand". En este modo, sólo se crean procesos hijos cuando se requiera, y hay un tope máximo para la cantidad de hijos residiendo al mismo tiempo. Lógicamente, al limitar la cantidad máxima de hijos en ejecución, se pone efectivamente un tope al consumo de memoria, al costo de que se pueda retrasar el procesamiento de requerimientos si los hijos están saturados y no se permite crear más. Es el precio a pagar cuando se negocia bajo consumo de recursos versus rapidez.

La nueva configuración "ondemand" para mi servidor PHP FPM es la siguiente (/etc/php5/fpm/pool.d/www.conf):

;pm = dynamic
pm = ondemand
pm.max_children = 50
pm.process_idle_timeout = 10s;
pm.max_requests = 500

En este caso utilicé como máximo un tope de 50 hijos, pero es recomendable modificar la variable max_children de acuerdo a las necesidades particulares de cada caso (por ejemplo es posible bajarla hasta sólo 5 hijos simultáneamente, para evitar problemas).

Esta configuración reduce notablemente el consumo de memoria RAM, pues no se crean hijos si no se requiere:

root@linuxito:~# date
Tue Sep  1 09:34:54 EDT 2015
root@linuxito:~# ps axl | grep "php\|nginx"
5   0  2557     1  20  0 108120  540 ep_pol Ss  ?      0:57 php-fpm: master process (/etc...
1   0 23448     1  20  0  22980  408 rt_sig Ss  ?      0:00 nginx: master process /sbin/nginx
5  33 28079 23448  20  0  23244 1592 ep_pol S   ?      4:02 nginx: worker process
5  33 30678  2557  20  0 110392 9072 skb_re S   ?      0:00 php-fpm: pool www
0   0 30680 30013  20  0   7724  924 pipe_w S+  pts/0  0:00 grep php\|nginx

Se observa en este snapshot de procesos un consumo residente de aproximadamente 11 MB contra los 53 de Apache con PHP como módulo.

Al mismo tiempo, verificar el log de errores de Nginx para asegurarse que no se produzcan errores de procesamiento (500) en el motor FastCGI por falta de recursos:

WARNING: [pool www] server reached pm.max_children setting (50), consider raising it

Obviamente no se puede hacer un análisis exacto comparando sólo dos snapshots, pero es una muestra clara de que el piso de consumo de memoria es mucho menor. Para un análisis más preciso, monitorear el sistema utilizando atop. Además, monitorear posibles entradas de "OOM" (out of memory) en el log de sistema (grep "OOM" /var/log/syslog*) para comprobar que en ningún momento se queda el servidor sin memoria (RAM y swap).

Es un proceso de prueba y error hasta encontrar el balance perfecto en el cual no se produzcan errores en el motor FastCGI ni eventos OOM en el kernel. Si no fuese posible llegar a este equilibrio, evidentemente será necesario contar con mayor cantidad de memoria para procesar adecuadamente el volumen de requerimientos sobre el servidor.

Referencias

PHP: FastCGI Process Manager (FPM)


Tal vez pueda interesarte


Compartí este artículo