Apache. MaxClients y más…

Conocer la versión

Hay muchas maneras pero dejamos dos simples. Es necesario por motivos obvios, conocer la versión de apache instalada en nuestro sistema y el modo en el que ha sido compilado.

# httpd -v

Output:

-bash-3.1# Server version: Apache/2.2.3
-bash-3.1# Server built: Jan 15 2008 20:33:30

# httpd -l

Compiled in modules:

core.c
prefork.c
http_core.c
mod_so.c

De esta manera sabemos que estamos ante un servidor Apache / 2.2.3 y en modo prefork:

Prefork MPM: el modo prefork utiliza múltiples procesos hijo, cada proceso hijo se ocupa de una conexión a la vez. Prefork es muy adecuado para sistemas con doble CPU, la velocidad es comparable al del Worker MPM y es altamente tolerante con los fallos en los módulos y los procesos hijos colgados. Por contra, el uso de memoria es alto y cuanto más tráfico tenemos más memoria consume

Worker MPM: utiliza múltiples procesos hijo. Es multi-thread dentro de cada proceso hijo y cada thread se encarga de una conexión. Worker es más rápido y escalable y el uso de memoria es comparativamente bajo. Es también adecuado para múltiples procesadores. Worker es menos tolerante ante fallos de módulos y un fallo en un thread puede afectar a todos los threads de un proceso hijo.

Este Módulo de MultiProcesamiento (MPM) implementa un servidor híbrido multiproceso-multihebra. Usando hebras para atender peticiones, el servidor puede servir un mayor número de peticiones con menos recursos de sistema que un servidor basado únicamente en procesos. No obtante, se mantiene casi por completo la estabilidad de un servidor basado en procesos manteniendo la capacidad multiproceso, pudiendo cada proceso tener muchas hebras.

Directivas de Apache

  • MaxClients: es el número total de procesos hijo httpd que pueden ser procesados simultáneamente. El valor por defecto es bastante elevado para la mayoría de servidores (256). Cada proceso hijo es capaz de mapear (allocate) en memoria un número de información X. En Drupal, por ejemplo, cada proceso puede utilizar unos 10-15 Mb de memoria. 20 procesos, por 20MB de RAM cada uno, consume cerca de 500MB de memoria física. Cuando nos acercamos o llegamos al límite marcado, apache no devuelve un TimeOut inmediato, sino que coloca la petición en cola. Una aproximación racional al número adecuado para esta directiva podría ser la división de la cantidad total de RAM disponible (dejando una generosa cantidad para otros procesos) entre el máximo tamaño del proceso apache. Podemos saber el consumo de memoria de apache por proceso con el comando ps.

Lo fundamental para entender como gestionar la directiva MaxClients, es comprender que puede tener un valor “alto” sino nuestro servidor está sirviendo contenido estático, pero con aplicaciones modernas tipo PHP de contenido dinámico, podemos tener graves problemas de estabilidad si no calculamos bien el número total de MaxClients. Una fórmula para calcular el valor apropiado del MaxClients sería (hay muchas) la siguiente:

MaxClients = Total RAM dedicated to the web server / Max child process size

En un ejemplo práctico, tenemos una máquina virtual (OpenVZ) con 1Gb de RAM asignado. Después de averiguar el tamaño total del proceso apache, dividimos:

1 GB (1024 MB) / 2,3 MB = 445 MaxClients

Siempre pensando en un servidor que “sólo” ejecutara apache, obviamente, este valor es sólo un ejemplo. Nunca se debería asumir esa cantidad total de MaxClients. Lo mejor sería aumentar un poco el valor y comprobar el funcionamiento del servidor. Pero sería un buen punto de referencia.

  • StartServers: número de procesos iniciales.
  • MinSpareServers: número de procesos iniciales en iddle que esperan una petición.
  • MaxSpareServers: número máximo de procesos en iddle esperando.
  • MaxRequestsPerChild: cuando un proceso excede este valor, el proceso es destruido y, si es necesario, un nuevo proceso lo reemplaza. Esto puede reducir la memoria total usada en muchas situaciones, con los archivos dinámicos incrementando constantemente su uso de RAM y reiniciando los procesos para reducir su uso.
  • ServerLimit: si nosotros queremos aumentar el número total de MaxClients de 256 a 300, debemos en principio aumentar el ServerLimit a la misma cantidad. Sino, el apache se quejará mostrándolo por la salida estándar.
  • KeepAlive: por defecto está en off. Al estar on, permitimos a una misma conexión TCP hacer múltiples peticiones sin descartar la conexión (conexiones persistentes).
  • MaxKeepAliveRequests: el número total de peticiones permitidas por una misma conexión TCP.
  • KeepAliveTimeOut: el número máximo de segundos que permanecerá el proceso esperando a ser respondido.

Ejemplo configuración por defecto de Apache


# IfModule prefork.c
StartServers 8
MinSpareServers 5
MaxSpareServers 20
ServerLimit 256
MaxClients 256
MaxRequestsPerChild 4000
# IfModule
# IfModule worker.c
StartServers 2
MaxClients 150
MinSpareThreads 25
MaxSpareThreads 75
ThreadsPerChild 25
MaxRequestsPerChild 0
# ifModule

Calcular memoria consumida por Apache

# ps -ylC httpd --sort:rss

–sort rss (lista ordenando por RSS(Resident Set Size), kb del proceso en memoria)

Output:

# S 48 5674 17426 0 75 0 2904 2637 277588 ? 00:00:00 httpd

2904 / 1024 = 2,8 MB ocupados por proceso de Apache. Ahora, debemos saber el número total de procesos:

# lsof -i | grep httpd | grep ESTABLISHED | wc -l

Output:

# 15

De este modo, tenemos que por proceso utilizamos 2,8 MB de memoria (no swap). Y sabemos que hay 15 procesos de apache en memoria, con lo cual 2,8 MB x 15 = 42 MB usados. Otra forma, quizá más fácil de calcular como está rindiendo nuestro apache, es utilizar este script escrito en python (gracias al admin de Devside 😉 ) Os dejo un output de lo que muestra el script:

Private + Shared = RAM used Program

84.0 KiB + 312.0 KiB = 396.0 KiB klogd
128.0 KiB + 452.0 KiB = 580.0 KiB syslogd
128.0 KiB + 524.0 KiB = 652.0 KiB init
824.0 KiB + 600.0 KiB = 1.4 MiB bash
568.0 KiB + 1.1 MiB = 1.6 MiB sftp-server
1.6 MiB + 1.9 MiB = 3.5 MiB sshd (3)
37.4 MiB + 2.1 MiB = 39.5 MiB httpd (180)

Optimización

No hablaremos de optimización totalmente pero, si comentaremos que una de las cosas que se recomiendan por toda la comunidad es deshabilitar los módulos de apache que no necesitemos. En el ejemplo que me ocupa, una máquina virtual de 1GB de RAM de OpenVZ, deshabilitamos de 52 procesos 35, dejando sólo activos 17. Volvimos a hacer el calculo de la memoria utilizada por apache entonces:

2,3 MB por proceso x 12 procesos = 33,6 MB! (22 % menos en memoria). No está mal.

Otro consejo que se da en todos los manuales de tunning de Apache, es el deshabilitar la resolución de DNS, HostnameLookups. Esta directiva intenta resolver cada IP conectada a tu servidor y eso genera un consumo de recursos innecesario.

Conexiones Persistentes

En sus inicios, el protocolo HTTP no permitía las conexiones persistentes, lo que significaba que era necesaria una conexión al servidor por cada archivo que tuviese que ser descargado. Esto era una manera ineficiente de hacer las cosas, especialmente desde que Internet comenzó a tener sitios webs con gran cantidad de archivos. ¿Por qué?:

  1. Cada conexión requiere la carga de al menos, 3 paquetes para ser iniciada (SYN,SYN-ACK,ACK). Esto significa que al menos 3 viajes de ida y vuelta para abrir una conexión.
  2. Dada la naturaleza de TCP, bajo protocolo HTTP, una conexión funciona más rápido cuanto más tiempo está “abierta”. El cerrar y abrir continuamente conexiones, no permite a HTTP utilizar su ancho de banda total.

Directivas de KeepAlive

  • KeepAlive: habilita o deshabilita las conexiones persistentes [ On – Off]
  • KeepAliveTimeout: cuanto tardará Apache, en segundos, después de que una petición haya sido respondida para pasar a otra antes de cerrar la conexión.
  • MaxKeepAliveRequests: el número total de peticiones que se permite a una conexión abierta.

Cuando un cliente se conecta al servidor web, se le permite realizar múltiples peticiones en la misma conexión TCP, lo cual reduce la latencia asociada a las múltiples conexiones. Esto es útil cuando, por ejemplo, una conexión a una página web requiere varias imágenes, y todas esas imágenes son recibidas por el cliente en una misma conexión. El lado malo es que cada proceso o worker en el servidor debe esperar a que se cierre la sesión por el cliente antes de poder resolver la siguiente conexión. Es difícil decir si es adecuado o no, activar las conexiones persistentes. En caso de que lo activemos, debemos dejar un valor muy bajo, 2, en la directiva KeepAliveTimeout. De esta manera, nos aseguramos de que cualquier cliente puede hacer las peticiones con bastante tiempo, y que el proceso no estará esperando eternamente a que el cliente cierre la conexión y se moverá a la siguiente conexión TCP. Resumiendo, sólo podemos probar su uso y ver si responde a nuestras necesidades.

Una curiosidad. Después de activar el KeepAlive, lo pude ver reflejado al instante en las gráficas del Cacti.

Un poco después de las 16.00 hice el cambio y se puede ver perfectamente. Es lo bueno de tener herramientas como esta. Era viernes y como no es bueno hacer cambios en fin de semana, lo desactivé.

Por último, comentar que la compresión de Apache logra muy buenos resultados. Revisar mod_deflate.

Fuentes:

http://drupal.org/node/215516

http://www.mysqlperformanceblog.com

http://www.mysqlperformanceblog.com/

http://www.devside.net/articles/apache-performance-tuning

http://publib.boulder.ibm.com

http://www.onlamp.com/pub/a/onlamp/2004/02/05/lamp_tuning.html

http://www.pacosanchez.com/2007/11/21/optimizando-apache

http://httpd.apache.org/docs/2.0/mod/worker.html

http://httpd.apache.org/docs/2.0/mod/prefork.html

http://www.ibm.com/developerworks/web/library/l-tune-lamp-2.html#listing1

http://www.apache-es.org/?p=20

http://www.pixelbeat.org/scripts/ps_mem.py – Reporta la memoria consumida por el sistema

9 thoughts on “Apache. MaxClients y más…

  1. Oscar says:

    Hola tengo una aplicacion con ajax sobre apache, me sucede que cuando tengo continuas desconexiones, al encontrar medio la aplicación se recupera pero en determinado momento mi aplicacion se bloquea, como si apache rompiera el hilo de peticiones de ese cliente, en realidad no se que tan cerca estoy de lo que realmente sucede, me gustaria tu opinion, y siendo asi que parametro requiero mover en apache para corregirlo, gracias.

  2. Rubén Ortiz says:

    Hola Oscar

    imposible decirte nada útil sobre tu problema, pueden ser mil cosas. De momento no soy tan bueno como para hacer un diágnostico de ese tipo : /

    Mira los logs de apache y los logs que genere tu aplicación, sea PHP, Ruby, etc para buscar más información sobre tu problema.

    Saludos

  3. Pingback: Mi última pesadilla: Optimizar el VPS para Wordpress

  4. Gustavo says:

    Muchas gracias por el art Rubén. La verdad es que tengo muy poca idea sobre como trabajar con Apache como web server, ya que siempre trabajé sobre plataformas Microsoft. Nuestra plataforma de monitoreo es con soft PRTG de Paessler y estoy teniendo problemas para importar los MIBs per, independientemente de esto ¿cuál es la mejor manera de tener un monitoreo proactivo de distintos sitios sobre el mismo Apache? Muchas gracias

  5. Alfonso Lopez says:

    Saludos estoy tratando de que mi sitio web en producción aguante conexiones por lo menos de 200 clientes en determinado momento pero no se que tengo que hacer ni come saber cuanto pesa un proceso de cada cliente…No quiero que se caiga el servidor para que no exista inconformidad de los clientes.

  6. Thiago says:

    Hola! Tengo una duda. El comando abajo me dio una lista.
    ps -ylC httpd –sort:rss

    Hay una lista de proceso httpd…
    # S 48 5674 17426 0 75 0 2904 2637 277588 ? 00:00:00 httpd
    # S 48 5474 17426 0 75 0 2904 2637 277588 ? 00:00:00 httpd
    # S 48 5673 17426 0 75 0 2904 2637 277588 ? 00:00:00 httpd

    ¿Cómo puedo proceder?
    Tengo 740 procesos y 494 megabytes . (Suma de toda mi RSS entregado a apache ).
    Lamento lo de mi terrible español.

Leave a Reply

Your email address will not be published. Required fields are marked *