Depurando problemas con nginx

  • english
  • spanish

La semana pasada he estado pegándome con un problemilla que tenía con el nginx de mi blog: de vez en cuando se quedaba frito, no respondía a ningún request y tenía que reinciarlo. Hace tiempo que monté un script que se ejcutaba cada X minutos, comprobaba si se había quedado tostado y lo reiniciaba, pero esto no es una solución, sólo una ñapa termporal.

Como decía he estado unos días mirándolo con más detalle, y preguntando en la lista de correo de nginx. Un detalle importante que me dijeron allí es que el log de error se puede poner en modo “debug”, que saca trazas bastante completas de lo que está haciendo cada proceso (con su PID) en cada paso del procesado de las requests: cabeceras, rewrites, proxies, cgis … todo.

error_log /var/log/nginx/$host-error.log debug;

Con esto, sabiendo el PID del proceso que está chungo, es muy fácil ver qué estaba haciendo en el momento que se ha quedado colgado. Y para juntar una cosa con la otra, el script que usaba para detectar los cuelgues, en el que añadí varias métricas como un netstat -nap (que saca el PID), un ps, vmstat, etc.:

#!/bin/sh

TIMEOUT=20
CHECK=http://localhost/wp-admin/
LOG=/var/log/checkWeb/checkWeb-$(date +%Y%m%d).log
LOGR=/var/log/checkWeb/restart-$(date +%Y%m%d).log
TMP=/tmp/checkWeb-$RANDOM

if ! wget -t 1 -o /dev/null -O /dev/nul -T $TIMEOUT $CHECK
then
echo "ERROR, reiniciando nginx"
echo "** REINICIANDO **" >> $TMP
date >> $TMP
echo "- CLOSE_WAIT:" >> $TMP
netstat -nap | grep -c CLOSE_WAIT >> $TMP
echo "- vmstat" >> $TMP
vmstat 1 5 >> $TMP
echo "- free" >> $TMP
free >> $TMP
echo "- ps" >> $TMP
ps aux >> $TMP
echo "- netstat" >> $TMP
netstat -nap >> $TMP
echo "" >> $TMP
echo "" >> $TMP

#       pkill -9 -f php-cgi
pkill -9 -f nginx
sleep 1s
/etc/init.d/nginx start

cat $TMP
cat $TMP >> $LOG
date >> $LOGR
fi

rm -rf $TMP

De esta forma, cada vez que localhost/wp-admin (estaba debuggeando un WordPress) no respondía, aparte de reiniciar nginx, me guardaba en un log bastante información sobre el sistema. Con el tiempo vi que siempre que se quedaba colgado, había varios procesos nginx con sockets en el estado CLOSE_WAIT. Con ese PID y el error.log de nginx en modo debug, vi que siempre que un proceso se quedaba colgado con sockets en CLOSE_WAIT, lo último que había estado sirviendo era lo mismo: en el blog tengo varios ejemplos de cómo ejecutar servidores con daemontools; daemontools utiliza “named pipes” (FIFO) en disco, que básicamente si no tienen un proceso alimentándolos, para el que los lee son un agujero negro; cuando nginx se ponía a servir uno de estos FIFO es cuando se quedaba frito.

Lo curioso es que no había tenido problemas ni con Apache ni con lighttpd. Aunque desde luego el problema es que esos FIFO no deberían estar ahí. Los quité y llevo más de cinco días sin cuelgues, cuando antes tenía 3-4 al día mínimo.

Probando nginx

Estoy cacharreando con nginx. El nuevo servidor virtual donde tengo el dominio está un poco justo, con la migración lo dejé con Apache en vez de volver a lighttpd y ahora estoy probando alternativas.

Como aparte de configurar el servidor y el PHP con fast-cgi hay que convertir todos los rewrites del Apache (wpmu y super-cache) al formato de nginx, es posible que el blog haga cosas raras, no se actualice debidamente, algunas páginas no vayan bien, pete, no acepte comentarios, o cobre consciencia de sí mismo y decida matar a todos los humanos. YMMV.

cut | sort | uniq: Apache logs

  • english
  • spanish

Muchas veces cuando un servidor web va lento es posible que esté ante un “ataque”, intencionado o no, por parte de algún bot. He visto casos en los que el propio Google Bot o bots de universidades u otros indexadores generaban más de la mitad del tráfico de un sitio web. Estos casos no son un ataque DoS como tal, sería tráfico legítimo, pero el resultado es que nos “tumban” el servicio. En algunos casos como con Google se puede configurar con las Webmaster Tools y los ficheros sitemaps y/o robots.txt la frecuencia de las visitas y a qué páginas acceder, en otros no y tendremos que valorar filtrar ese tráfico a nivel de firewall. Pero en cualquier caso el primer paso es detectar que realmente hay una IP (o un grupo de IPs) que nos está “machacando” y con whois averiguar quién es.

Para ver las cinco IPs con más accesos en el Apache podemos ejecutar algo así:

cut -d" " -f 1 access.log | sort | uniq -c | sort -nr | head -n 5

Scripts daemontools para lighttpd y PHP

  • english
  • spanish

He preparado unos scripts para controlar con daemontools el lighttpd y los procesos PHP con spawn-fcgi. Aquí está el README, el tar con los scripts, y aquí se puede navegar por los directorios.

PD: si, me gusta daemontools. Me ayuda a que un montón de servicios no caigan aunque un servidor pueda tener un fallo puntual y algún proceso muera, evitando que me despierten a las tantas de la noche. Es un gran invento. :)

PHP con lighttpd 1.5.0

  • english
  • spanish

La forma de configurar PHP en lighttpd 1.5.0 ha cambiado: ya no se usa mod_fastcgi si no mod_proxy_backend_fastcgi, y lighty ya no se encarga de lanzar los procesos PHP si no que tendremos que hacerlo nosotros con ayuda del programa spawn-fcgi.

Para configurar mod_proxycore bastaría con ésto (en lighttpd.conf, o conf-enabled/php.conf p.ej.):

server.modules += ( "mod_proxy_core", "mod_proxy_backend_fastcgi" )

$PHYSICAL["existing-path"] =~ ".php$" {
proxy-core.allow-x-sendfile = "enable"
proxy-core.protocol = "fastcgi"
proxy-core.backends = ( "unix:/tmp/php-fastcgi.sock" )
proxy-core.max-pool-size = 16
}

Y para lanzar los procesos PHP, ejecutar a mano o mejor desde un script en init.d:

/usr/bin/spawn-fcgi -s /tmp/php-fastcgi.sock -f /usr/bin/php-cgi -u www-data -g www-data -C 5 -P /var/run/spawn-fcgi.pid

lighttpd 1.5.0-SVN r1992 para Debian Sarge

  • english
  • spanish

He preparado paquetes .deb de lighttpd 1.5.0-SVN r1992 para Debian Sarge. Están basados en el último paquete de testing, actualizado a la 1.5.0. Falta sólo el mod_mysql_vhost, no tengo ahora mismo instalado el mySQL 5.0 ni sus librerías. Ésta página ya se sirve desde lighttpd 1.5.0, así que es la mejor prueba de que funciona. ;)

Se puede descargar aquí.

(PD: si, ya se, lo que tendría que hacer es dejarme de historias y actualizar a Etch…)

ACTUALIZACIÓN (20070921): Nueva versión con soporte para linux-aio-sendfile. Hace falta también el port de libaio.

CakePHP y lighttpd

  • english
  • spanish

Llevo unos días enfrascado en un proyecto web con CakePHP, y quería usar lighttpd como servidor web. Para que funcionen las “URLs limpias” CakePHP trae el típico .htaccess con las reglas mod_rewrite para Apache, que hay que convertir al formato de lighttpd. He encontrado la solución aquí, 3er comentario:

Lighttpd and CakePHP setup in subdirectories

url.rewrite-once = (
"/(.*)\.(.*)" => "$0",
"/(css|files|img|js)/" => "$0",
"^/([^.]+)$" => "/index.php?url=$1"
)

Arquitecturas escalables en Google

  • english
  • spanish

Un compañero de trabajo me manda tres interesantes artículos de la web High Scalability, que no conocía aún y que ya he añadido a mi lista de RSSs en Google Reader. :) Los artículos tratan sobre las decisiones de diseño y arquitectura de computadores/redes tomadas en YouTube, Google y GTalk, para hacer frente a la altísima cantidad de visitas y datos que deben servir por segundo, comentando también más o menos la estructura actual y la evolución de la infraestructura de cada uno de ellos:

Algunas conclusiones a extraer de éstos artículos:

  • No intentar arreglarlo todo con una única arquitectura o una única herramienta. Dividir el problema en subproblemas, ver en cada caso si el límite es CPU, ancho de banda, IO y optimizarlo. Especializar servidores para cada caso y coordinar el trabajo de cada parte.
  • Cachear contenido siempre que sea posible. Pregenerar contenido siempre que sea posible. Utilizar las directivas HTTP para el control de caché. Usar squid como proxy inverso para aliviar a los servidores de aplicaciones.
  • Plantearse externalizar algún servicio, el hosting de imágenes o videos que puedan suponer más ancho de banda del que actualmente tenemos. Aunque sea como solución de compromiso mientras se contrata más ancho de banda. Lo importante es ofrecer el servicio en condiciones.
  • Simplicidad, dentro de lo que cabe. Permitirá hacer cambios y evoluciones sin cagarla demasiado.
  • Clusters basados en PCs normales. Maximizan la relación potencia/precio. Tener montado un sistema de redundancia para que una caída no afecte al servicio, y un sistema de instalación sencillo para poder añadir/sustituir equipos sin calentarse mucho la cabeza. Y empezar a planificar los problemas de alimentación y aire acondicionado. ;)
  • La programación hoy en día se basa en librerías y frameworks. No reinventar la rueda. Usar un framework común, propio o no, de forma que los programadores no tengan que hacer una y otra vez las mismas cosas y una actualización beneficie a todos nuestros productos de golpe.
  • Pensar en la arquitectura desde el principio. Estoy tristemente acostumbrado a que los desarrolladores no se preocupen de lo que hay por debajo de su código, ni si éste va a provocar problemas de red, disco, CPU, etc. Google enfoca todos los problemas desde la arquitectura necesaria para dar el servicio, y luego programa alrededor de ésa arquitectura. Es lo que diferencia a Google del resto.

Cabalgando los gusanos

“Debes cabalgar por la arena a la luz del día,
para que Shai-hulud vea y sepa que no tienes miedo.”

Dune, de Frank Herbert

“Si no está en Google, no existe”. Esta frase tan categórica es cierta tanto para comercios on-line o webs corporativas, como para nuestro blog personal. Cuando necesitamos localizar información en Internet, vamos a Google. Y quien dice Google, dice Yahoo, MSN, o cualquier otro buscador. Tenemos que estar ahí.

Éstos buscadores usan “bots” o “spiders” para indexar el contenido de nuestras páginas, programas que periódicamente recorren todos los sitios que ya conocen en busca de actualizaciones y nuevos enlaces a través de los cuales descubrir, procesar e indexar más y más páginas

A nadie se le escapa que el trabajo de éstos programas es beneficioso, pero normalmente no tenemos en cuenta que generan tráfico extra a nuestra web. Aunque parezca mentira, conozco sitios en los que el tráfico de GoogleBot y compañía (ojo, hablo del propio bot, no de visitas dirigidas desde el buscador) consumía hasta un tercio del ancho de banda total de los accesos. Estamos hablando de GIGAS de tráfico al día.

Además los buscadores penalizan la información repetida: si tenemos varias páginas con contenido igual o muy similar, o aún peor, si podemos cargar una misma página con varias URLs distintas, podemos llevarnos sorpresas desagradables como páginas que no aparecen en los resultados de una búsqueda en favor de un feed o un resumen (índice de sección, categoría, etc.) con contenido similar, o páginas con un pagerank bajo porque éste se “diluye” entre varias URLs.

Por ello es importante aprender cómo funcionan éstos bots para saber cómo optimizar su paso por nuestro sitio web, cómo “llevarlos de la mano” hasta la información que queremos priorizar para así mejorar nuestro posicionamiento en los resultados, minimizando a su vez cuando sea posible la cantidad de información transmitida para no saturar nuestra conexión y servidores.
Sigue leyendo