Analizando el arranque de tu Linux con bootchart

herramientas ¿Qué es lo que hace tu Linux durante el arranque?¿Hay alguna forma de ver exactamente que se ejecuta, cuales son los procesos dependientes que genera y cuanto tiempo se invierte en cada uno de ellos de forma que podamos, si es necesario, saber donde tenemos que tocar para conseguir un arranque más rápido?

Bootchart es una herramienta que obtiene y procesa fácilmente esta información y genera como resultado un gráfico fácilmente interpretable. Como muestra, aquí tenéis la gráfica resultado en un servidor VPS con Debian 7 (ya sabes, pulsa sobre la gráfica para verla con mejor resolución):

Gráfica de bootchart 0.9 en un servidor VPS con Debian 7

La instalación básica en una Debian estable requiere dos paquetes (y sus dependencias): bootchart y pybootchartgui. El primero es el que recopila los datos y el segundo el que genera el gráfico a partir de ellos. bootchard se instalará por defecto en el directorio /sbin y su archivo de configuración se llama bootchard.conf y se encuentra en el directorio /etc. pybootchartgui se encuentra en el directorio /usr/bin/. Aunque tenemos muchas opciones interesantes para explorar, vamos a limitarnos a ver el funcionamiento tal y como queda recién instalado y sin tocar nada.

Una vez instalados ambos, tenemos que hacer que el kernel de nuestro Linux ejecute el proceso que realiza la recogida de datos lo antes posible y esto lo hacemos introduciendo dicha llamada directamente en GRUB. Si estamos en un equipo desde el que tenemos acceso directo a teclado y monitor lo más fácil es introducir a mano la opción necesaria en el momento del arranque. Para ello, una vez que nos aparece el menú de GRUB pulsamos la letra “e” (de editar) e introducimos la opción init=/sbin/bootchartd. De esta forma el proceso sólo se llamará en este arranque puesto que la opción no queda salvada:

Editando la entrada de Grub para invocar a bootchar

Si no tenemos acceso al teclado de la máquina en el momento del arranque (en un Hosting VPS, por ejemplo) el método pasa por introducir esta opción en los archivos de configuración de GRUB. El método es diferente según que usemos GRUB o GRUB2. En GRUB basta con editar el archivo /boot/grub/menu.lst, buscar el bloque correspondiente al kernel con el que qeremos realizar el arranque e introducir la misma opción que hemos visto antes. Por ejemplo, así:

title           Debian GNU/Linux, kernel 3.2.0-4-amd64
root            (hd0,0)
kernel          /boot/vmlinuz-3.2.0-4-amd64 root=/dev/vda1 ro quiet no-kvmclock init=/sbin/bootchartd
initrd          /boot/initrd.img-3.2.0-4-amd64

En GRUB2 tenemos que editar el fichero /etc/default/grub, buscar la línea etiquetada como GRUB_CMDLINE_LINUX_DEFAULT e incluir al final de lo que allí aparezca la opción de carga de bootchartd. Así, por ejemplo:

GRUB_CMDLINE_LINUX_DEFAULT="resume=/dev/disk/by-uuid/35a07870-ac1a-489f-a19b-629
6dca46903 init=/sbin/bootchartd"

En este segundo caso, una vez editado el fichero tendremos que ejecutar el programa update-grub para que los cambios tomen efecto.

Un cuarto método, disponible si estamos usando un equipo de escritorio con un gestor de ventanas instalado, es usar uno de los muchos programas existentes para configurar grub de forma gráfica. Por ejemplo, grub-customizer:

Configurando el arranque de bootchartd con grub-customizer

Sea cual sea el método que hayamos usado, en el próximo arranque se ejecutará bootchartd, pero no olvides que, salvo en el primer caso, cuando ya no quieras usarlo tendrás que deshacer los cambios efectuados.

Bien. Tras reiniciar la máquina se invocará al proceso llamado bootchartd que recogerá la información necesaria y la guardará en un archivo llamado bootchart.tgz en el directorio /var/log. Para crear el gráfico basta con invocar a pybootchartgui:

Generando el gráfico de bootchart con pybootchartgui

Nota que se toma por defecto la ubicación y nombre del archivo original y que, por tratarse este de un nombre genérico, cada vez que reiniciemos y/o generemos un nuevo gráfico machararemos los datos del anterior a menos que los renombremos manualmente o le echemos un vistazo a la forma de modificarlos mediante los parámetros en línea y/o opciones de configuración adecuadas. Y listo. A continuación tenéis otro ejemplo correspondiente a un servidor más modesto que el anterior montado sobre VirtualBox:

Gráfica de bootchart 0.9 en una máquina de virtualbox con Debian 7

En la paquetería de Manjaro Linux (e, imagino, que ocurre igual con Arch) se usa otro programa similar también llamado bootchart pero que, en realidad, integra en un sólo servicio la recolección de datos y la generación del gráfico final. El gráfico resultante presenta, además, algunos aspectos diferenciadores. Podéis ver una muestra en la siguiente imagen que corresponde con el arranque de un PC de escritorio:

Gráfica de bootchart 1,2 en un PC de escritorio con Manjaro Linux

Para usarlo basta instalar el paquete llamado bootchart. El programa que tendremos que invocar se llamará igualmente bootchartd pero en este caso reside en el directorio /usr/bin. Podemos usar cualquiera de los métodos anteriormente vistos para invocarlo pero el parámetro a incluir en las opciones de carga del kernel será init=/usr/bin/bootchard. El archivo de configuración también es sensiblemente diferente y está en el directorio /etc/systemd. Cuando reiniciamos la máquina, el archivo que bootchart genera es ya directamente un gráfico en formato svg. El directorio destino del mismo es, igualmente, /var/log pero en este caso el nombre incluye una marca de tiempo (por ejemplo bootchart-20130916-0800.svg) de forma que si realizamos sucesivos reinicios tendremos disponible una colección de gráficos para su posterior estudio sin necesidad de ninguna intervención manual.

Existe aún, al menos, otro tercer sistema similar a estos denominado bootchart2 y que, por el momento, no he tenido ocasión de probar. Ahí queda el enlace por si alguien se anima…

NOTA: Desde hace ya unos meses estoy usando Manjaro Linux (con Openbox como window manager) como distribución de escritorio, tanto en mi ordenador de sobremesa como en mi portatil. Manjaro es una distribución rolling release, muy, muy ligera basada en Arch Linux que me está gustanado bastante, así que previsiblemente hablaremos bastante de ellas en lo sucesivo.

Apache Tunning (y II). Ajustando el parámetro MaxClients

apache Como ya habíamos hablado por aquí, uno de los parámetros críticos en un servidor Apache es MaxClients, el número máximo de conexiones que es capaz de manejar simultaneamente. Es crítico porque cada tarea de Apache consume una determinada cantidad de memoria y si la suma de la memoria que consumen todos de forma simultanea es mayor que la memoria RAM que la máquina tiene libre comenzará a hacer swapping a disco y, si esta situación se mantiene, corremos un riesgo importante de que se nos quede virtualmente congelada. Una instalación de Apache por defecto toma un valor de 150 para este parámetro y esto es una verdadera burrada para casi cualquier servidor web con menos de 1 Gbytes (o incluso con bastante mas) que use contenidos dinámicos medianamente pesados.

Hace bien poco, buscando por ahí algún enlace para profundizar un poco más en estos temas, me encontré con este artículo de una empresa de hosting donde te proponen un script para ajustar el valor más adecuado a tus necesidades. Había dos cosas que no me gustaban en él script propuesto. Por un lado, tomaban el valor más conservador, es decir, el resultante de dividir la memoria libre entre lo que ocupa el proceso de Apache mayor. En segundo lugar, hacen el cálculo de la memoria libre sin tener en cuenta la que está ocupada por cache o buffers y que podría liberarse en caso de necesidad. Yo le he hecho algunos cambios para salvar ambos puntos y dejarlo un poco más bonito e informativo y me ha salido esto:

#!/bin/bash
clear

# Primero analizamos los procesos de apache funcionando actualmente
LISTA=`ps -aylC apache2 |grep apache2 |awk '{print $8'} |sort -n`

echo "Lista ordenada de la memoria, en bytes, ocupada por los procesos de Apache corriendo en este momento:"
COUNT=0
SUMA=0
for ITEM in $LISTA
do
        COUNT=`expr $COUNT + 1`
        SUMA=`expr $SUMA + $ITEM`
        echo "$COUNT -> $ITEM"
        if [ $COUNT -eq 1 ]
        then
                MENOR=$ITEM
        fi
done

MAYOR=$ITEM
MEDIA=`expr $SUMA / $COUNT`

# Pasamos las cantidades a Kbytes
MENOR=`expr $MENOR / 1024`
MAYOR=`expr $MAYOR / 1024`
MEDIA=`expr $MEDIA / 1024`

echo "El proceso de Apache más grande ocupa" $MAYOR "Kbytes y el menor" $MENOR "Kbytes"
echo "La media de la memoria ocupada por un proceso es de $MEDIA Kbytes"

echo "Detenemos Apache..."
apache2ctl graceful-stop > /dev/null

# Limpiamos los caches y buffers del servidor
sync
echo 3 > /proc/sys/vm/drop_caches

# Calculamos la memoria libre del sistema
FREEMEM=`free -m |head -n 2 |tail -n 1 |awk '{free=($4); print free}'`
echo "La memoria libre del servidor, después de liberar cache y buffers, es de $FREEMEM Kbytes"

echo "Arrancamos Apache de nuevo..."
apache2ctl start > /dev/null

echo "MaxClients debería de estar entre" `expr $FREEMEM / $MAYOR` "y" `expr $FREEMEM / $MENOR`
echo "Usando el valor medio de la memoria ocupada, un valor recomendable de MaxClients sería de" `expr $FREEMEM / $MEDIA`

El script de aquí arriba imprime una lista de la memoria ocupada por todos procesos de Apache activos en el momento de ejecutarlo. Luego toma el mayor, el menor y la media de todos ellos y calcula la memoria libre del sistema. Para ello, antes detiene Apache (con un graceful-stop para no fastidiar a nadie que esté viendo nuestra web en ese momento) y libera los buffers y caches de memoria. Por último, arranca de nuevo Apache y nos da como salida dos valores: una horquilla entre el valor que debería de tomar MaxClient usando el menor y el mayor valor de los procesos tomados en la instantánea anterior y un valor recomendado que sale de usar el valor medio de todos ellos.

Evidentemente no se trata de algo matemático. Como es lógico, ejecutado en diferentes momentos obtendremos valores distintos ya que ni la RAM que usa nuestro servidor es inmutable ni el tamaño de los procesos de Apache es siempre el mismo ni siguen la misma distribución. Pero para darnos una primera idea del valor con el que tenemos que empezar a jugar creo que es bastante válido. Ya me contáis.

NOTA: En algunas instantáneas el valor de memoria ocupado por el proceso más pequeño de Apache puede ser cero, seguramente porque o está creándose o está destruyéndose en ese momento. En ese caso el valor de la horquilla pierde uno de sus extremos. Sería muy fácil modificar el script para que en ese caso tome el segundo valor y divida por un elemento menos, pero ocurre en tan pocas ocasiones que no creo que merezca la pena hacerlo. Al menos no hoy 😉

Apache Tunning. Ajustando recursos en un servidor web con poca memoria

apacheInstalar un servidor Apache en un entorno Linux debe de ser lo más fácil del mundo. Configurarlo para que funcione de forma aceptable sirviendo una o dos instancias web también es sencillo. Ajustarlo para que funcione de forma eficiente en entornos escasos de recursos o para que escale bien ante grandes cargas ya es algo en lo que muy pocos se meten. Si siempre has trabajado con servidores compartidos no te habrás dado cuenta de esto último porque alguien te lo ha dado ya hecho. Pero tanto si has tenido que gestionar servidores propios y/o dedicados o pequeños VPS posiblemente hayas tenido algún problema en este sentido. Y si no, creeme: los vas a tener…

La forma en que Apache gestiona la multitarea a la hora de atender a los clientes del servidor web se configura mediante los módulos MPM. Los dos más comúnmente usados son Prefork y Worker pero existe por ahí alguno más. Al instalar apache2 en Debian (y en todas las distribuciones, me atrevería a decir) se instala por defecto el paquete apache2-mpm-prefork y este es el módulo MPM que funcionará por defecto. Para probar con worker basta con instalarlo (apt-get install apache2-mpm-worker en una Debian) y automáticamente desinstalará el otro mpm (sólo puede haber uno en cada momento) y reiniciará el servicio de apache para que los cambios tomen efecto.

La primera pregunta que se nos plantea es, lógicamente, cual elegir. A grandes rasgos la principal diferencia entre ambos es que Prefork crea siempre nuevos procesos para gestionar las conexiones entrantes mientras que con Worker los procesos hijos, a su vez, crean múltiples hebras cada uno de ellos para esta tarea. Tienes información más precisa de como funcionan en los enlaces de hace unas líneas. Prefork tiene a gala ser más estable y funcionar mejor en máquinas escasas de recursos, mientras que Worker presume de ser más escalable y eficiente para servidores más ambiciosos. Para novatos o instalaciones sin necesidades especiales es mejor quedarse con la opción por defecto (Prefork) puesto que, además, incluso algo tan simple como instalar PHP es trivial y habitual con este módulo mientras que hay que “enredar” bastante más para hacerlo funcionar con Worker.

Si el servidor no lo hemos instalado nosotros y queremos saber que MPM está activo basta con recordar que sólo puede haber uno, así que podemos mirar que paquete está instalado (aptitude search apache2-mpm-* en Debian, de nuevo) o interrogar directamente a apache con la opción -l:

root@invernalia:/etc/apache2# apache2 -l
Compiled in modules:
  core.c
  mod_log_config.c
  mod_logio.c
  prefork.c
  http_core.c
  mod_so.c

Pero independientemente del módulo que escojamos, el comportamiento final del servidor web será muy parecido. Pensemos que se trata de un servicio diseñado para atender múltiples peticiones simultaneas de decenas (o centenares, si tenemos una web con éxito) de clientes y hacerlo de forma eficiente y en el menor tiempo posible. Nuestro servidor permanecerá a la espera de estas peticiones y, cuando vayan llegando, destinará a un proceso (o a una hebra de un proceso) a atenderlas. Cuantos más peticiones se cursen de forma simultanea, más procesos/hebras necesitaremos y consumiremos más recursos de la máquina. Sobre todo memoria. Un servidor web se comporta como un insaciable devorador de RAM. Este va a ser el recurso que debemos de aprender a gestionar: la RAM que no se usa cuando se necesita se desperdicia y nos hace perder visitantes o que estos sean atendidos más lentamente. Pero si no controlamos el consumo y el servidor la agota comenzará a hacer swapping a disco y evitar esto es la regla de oro: Si llegamos a este punto lo habremos perdido y, posiblemente, tendrás que reiniciar el servicio (o la máquina, si no tienes tiempo o paciencia) para volver a tomar el control.

Los principales ajustes que necesitamos hacer se encuentran en nueve líneas del archivo /etc/apache2/apache2.conf. A continuación tienes los valores por defecto en una instalación básica de Debian 6: Ten cuidado porque las cinco últimas se encontraran duplicadas en el interior de sentencias condicionales segun el módulo MPM que estemos usando. Las que nos interesan hoy son las que están tras la sentencia <IfModule mpm_prefork_module>.

Timeout 		300
KeepAlive 		On
MaxKeepAliveRequests	100
KeepAliveTimeout   	15
StartServers        	5
MinSpareServers     	5
MaxSpareServers     	10
MaxClients		150
MaxRequestsPerChild	0

Timeout, el primero de ellos, define el tiempo en segundos que Apache esperará a determinados eventos antes de cerrar o abortar una conexión. En servidores con pocos recursos 300 segundos puede ser una cantidad bastante elevada. Reducir ese tiempo sustancialmente ayudará a gestionar mejor la memoria del servidor. Valores de 30 o 40 son adecuados en servidores con pocos recursos. Un valor de 10, incluso, podría mejorar el rendimiento en determinados entornos.

Los tres parámetros siguientes (KeepAlive, MaxKeepAliveRequest y KeepAliveTimeout) definen la posibilidad de usar conexiones persistentes y la forma de tratarlas. En concreto, el número de peticiones que se permitirán sobre cada conexión (MaxKeepAliveRequest) y el tiempo de espera sin peticiones antes de abortarlas. Tener habilitadas este tipo de conexiones reduce sustancialmente la carga del servidor y los tiempos de respuesta y el número de peticiones se puede mantener en torno a 100 en casi cualquier entorno. Es, de nuevo, el Timeout por defecto de 15 segundos el que puede resultar demasiado elevado en servidores con poca RAM y podríamos reducirlo a un valor de 5 segundos o incluso menos.

Los cinco parámetros restantes son los que definen el comportamiento del módulo Prefork. StartServers, MinSpareServers y MaxSpareServers ajustan, respectivamente, el número de procesos apache que se crearan inicialmente, y el mínimo y máximo de estos que mantendremos inactivos a la espera de que lleguen peticiones de posibles clientes. Los procesos inactivos definidos por estos parámetros estarán consumiendo memoria pero nos permitirán dar una respuesta más rápida a los clientes (los procesos están ya listos para usarse cuando llegan peticiones y no hay que perder tiempo en crearlos), así que en un servidor con grandes recursos y que espera cargas elevadas querríamos tener valores más elevados de los que vienen por defecto mientras que en un servidor pequeño podríamos reducirlos todos a, por ejemplo, 3 o 4.

MaxClients es, posiblemente, el parámetro más importante. Define el máximo número de procesos simultaneos que nuestro servidor podrá crear para atender peticiones. Un número más pequeño del que podemos permitirnos desperdiciará memoria y ralentizará las peticiones de muchos clientes (que permanecerán a la espera de que uno de estos procesos se libere para atenderle) mientras que un número demasiado elevado agotará la memoria del servidor y lo obligará a hacer swapping a disco. Y aquí no hay fórmulas mágicas. Lo primero que se nos puede ocurrir es que dividiendo la memoria que tenemos disponible entre lo que ocupa cada proceso de Apache obtendríamos el número que necesitamos, pero el problema es que la memoria disponible es difícil de calcular de forma exacta (ya que, posiblemente, tendremos también un servidor mysql, servicios de correo, etc). Además, los procesos de apache no consumen todos la misma cantidad de memoria. Si tenemos distintas instancias de apache con características muy diferentes la diversidad será aún mayor. Podemos verlo en el siguiente pantallazo de htop tomado de un servidor apache funcionando con varias instancias web diferentes:
Vista de htop en un servidor apache que estamos monitorizando

La columna MEM% nos da el porcentaje de memoria que está ocupando el proceso de apache y, como vemos, tenemos cifras entre 0.5% y 4.2%, es decir, entre 4 y 32 Mbytes (la máquina es un VPS con 768Mbytes). Si aproximamos que disponemos de 600Mbytes disponibles para que apache los use nos saldrían cifras de entre 150 y 18 procesos simultaneos. Una horquilla demasiado grande. Si somos muy, muy conservadores podemos irnos al borde inferior de la horquilla y asegurar que nunca entraremos en zona de riesgo. Podemos también dedicarle tiempo a estudiar el comportamiento medio de nuestros clientes y hacer medias de la utilización de memoria por los procesos de Apache. Lo más rápido para empezar es tomar un valor medio, por ejemplo de un 2.5% o un 3% y ajustar por ahí el número máximo de procesos. Entre 25 y 40 en este caso. El hecho de que hayamos bajado mucho los Timeouts ayudará a que los procesos se liberen rápidamente y se turnen atendiendo a los clientes que están a la espera con lo que no deberíamos de notar demasiado la espera.

Tenemos aún otro parámetro más. MaxRequestsPerChild marca el número de peticiones que atenderá cada proceso antes de reciclarse. El valor por defecto es cero que indica un número indefinido. Poner un número elevado pero no ilimitado ayudará también a que nuestro servidor libere y sanee su memoria de forma regular. Un valor de entre 500 o 1000 podría ser adecuado para nuestra pequeña máquina. Nuestra configuración final podría quedar así:

Timeout 		10
KeepAlive 		On
MaxKeepAliveRequests	100
KeepAliveTimeout   	5
StartServers        	3
MinSpareServers     	3
MaxSpareServers     	4
MaxClients		30
MaxRequestsPerChild	600

No olvides que hay que reiniciar el servicio de apache (service apache2 restart) después de realizar cualquier modificación en este fichero de configuración para que los cambios tomen efecto.

No tires el dinero: no uses Windows Vista

icono de windowsLa gente de exo.blog le ha puesto cifras a lo que se ve de forma casi evidente a poco que hayas jugado con el nuevo sistema operativo de Microsoft: actualizar a Vista en un entorno empresarial es devaluar el hardware incluso en máquinas con requerimientos más que suficientes. Durante los últimos días han hecho pruebas muy significativas pero para mi la más reveladora es esta que compara el funcionamiento de Office 2003 en un Dell XPS (Pentium Core 2 Duo a 2 GHz y 2GBytes de RAM DDR2).

Test de rendimiento vista-xp

A mayor valor del eje vertical peor rendimiento, que no se equivoque nadie…

Visto en el blog de markbd.