Archivo de la etiqueta: servidor

Hello world! – mudanza

Welcome to WordPress. This is your first post. Edit or delete it, then start blogging!

O eso es lo que reza el post por defecto que te mete WordPress en cada nueva instalación.

Si, nueva instalación. Después de n años arrastrando un WordPress-mu 2.x y aprovechando un cambio de servidor, me he decidido a migrar a WordPress 3. Así que cambio de servidor y de software… va a petar todo. O más. Se va a cagar la perra.

Ya iré arreglando cosas poco a poco, instalando plugins, temas, acabando de configurar, migrando ficheros que no se hayan copiado bien y demás. Pero es que si no me tiro a la piscina a lo bestia, no lo hago. Y más con lo dejado que tengo esto últimamente, a ver si así lo retomo.

Además y aprovechando el nuevo servidor he montado un mirror de esto. :-)

Depurando problemas con nginx

[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.[/spanish]

[english]Last week I’ve been debugging a problem I had with this site’s nginx server: from time to time it hanged and I had to restart the process. Some time ago I wrote a little script that checked if it was running OK and restarted it otherwise, but anyway that wasn’t a real solution.

So I spent some days really looking into it and asking for support and reporting my findings to the nginx mailing list. One useful tip I got there was enabling the “debug” mode on the error log, which shows full traces of the processes (including their PID) as they’re processing the request, the rewrites, upstreams, etc.

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

With this extended log and the PID of the process malfunctioning, it’s quite easy finding out what that process was doing right before hanging. In order to find out the PID of the hanged processes, I extended my check-reboot script to log some generic system metrics right before restarting nginx: netstat -nap (which shows the PID), 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, restarting nginx"
echo "** RESTARTING **" >> $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

This way, each time localhost/wp-admin was unresponsive (I was debugging a WP site), besides restarting nginx I was getting a lot of system info. With time I got to realize that nginx processes were not actually hanging, but some of their sockets got on the CLOSE_WAIT state forever until the process was restarted. Looking for the PID of those processes according to netstat on the error log, the last request they were processing before getting to the CLOSE_WAIT state was always the same: on my blog I have some examples of how running servers with daemontools; daemontools uses named pipes (FIFOs), which can become kind of black holes if there’s no process feeding them; when nginx hit one of these FIFOs, it hanged.

Funny thing is that I never had this problem with either Apache nor lighttpd. But anyway the problem is not nginx but those FIFOs which shouldn’t really be there. I removed them and have had no hanged processes in five days, while before this nginx was restarting 3-4 times a day.[/english]

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.

Cluster de correo escalable con software libre

[spanish]

En mi anterior trabajo entre otras cosas era responsable del servidor de correo para un grupo de empresas, con unas 3000 cuentas repartidas entre unos 20 dominios y que recibía alrededor de 150.000 mails diarios, de los cuales más del 95% se desechaban por SPAM o virus (estos datos son de hace un año, no sé cómo habrá evolucionado la cantidad y tasa de SPAM desde entonces). Todo esto en un cluster que contaba por aquel entonces con siete servidores, que nos permitía escalar según las necesidades. Y basado en software libre.

Esto no es una guía de instalación con detalles técnicos y ficheros de configuración, si no más bien la historia de la evolución del servicio, los distintos problemas que fuimos encontrando, cómo se solucionaron, y las decisiones de diseño en cada caso.

Migración

La primera encarnación del servidor fue por el 2001, cuando tuvimos que migrar a un software y hardware más actual el antiguo servidor del grupo, que llevaba un tiempo dando problemas. Creo recordar que era un servidor de correo de Netscape (¡!) que almacenaba la información de las cuentas en un directorio LDAP. El servidor que elegimos para la migración fue qmail-ldap, por los buenos comentarios en cuanto a estabilidad y fiabilidad, facilidad de configuración (a mi personalmente me sigue pareciendo mucho más sencillo que p.ej. sendmail) y porque también usaba un LDAP. Puede que éste último motivo parezca una tontería, pero al final la migración se tuvo que hacer in extremis en un momento que el servidor original ni si quiera arrancaba la mayoría de las veces, y salimos airosos con un simple ldapsearch y un script que “traducía” los esquemas de un servidor a los del otro. Con el tiempo la elección de qmail-ldap demostró ser un gran acierto, ya que gracias a su diseño modular nos permitió ir evolucionando desde un único servidor hasta el cluster que comentaba en la introducción.

Este primer servidor era un enrackable con fuente redundante y RAID5 hardware, con lo que los datos estaban bien protegidos (o eso creíamos). También instalamos qmail-scanner y el anti-virus Kaspersky (no existía el ClamAV por aquel entonces, años más tarde migramos). En el mismo servidor teníamos SMTP, POP, IMAP y WebMail (SquirrelMail).

Backup en espera

La primera ampliación creo que la hicimos antes del año de haberlo montado, y vino propiciada por un susto con el RAID5 y una partición corrupta que nos costó Dios y ayuda recuperar. Se hizo evidente que el RAID y la fuente redundantes no eran suficientes para asegurar los datos y la disponibilidad del servicio, con lo que montamos otro servidor exactamente igual, y sincronizamos la configuración y los buzones mediante rsync y tareas programadas en el cron. El paso de un servidor a otro era manual por aquel entonces, a nivel de NAT en un router.

Con el tiempo se cambió de servidor varias veces pero mantuvimos la estructura con un backup en espera. La sincronización también se mejoró, con DRBD para los buzones y csync2 para la configuración, bases del anti-virus, etc. La monitorización y paso de backup a activo se automatizó con heartbeat.

Escalada de SPAM, especialización por recursos

En algún momento del 2003 los virus dejaron de ser el gran problema del correo electrónico que pasó a ser el SPAM, con lo que instalamos SpamAssassin en el servidor. Con el tiempo cada vez se recibía más y más SPAM, aumentando el consumo de CPU y memoria del servidor. Parecía que la única opción era migrar todos los años a un nuevo servidor más potente (¿y qué hacer con el anterior?), o montar varios servidores y distribuir los dominios entre ellos en un intento de distribuir la carga.

Finalmente nos dimos cuenta que teníamos necesidad de dos tipos de recursos distintos, con patrones de crecimiento también distintos:

  • espacio en disco para los buzones: el nº de cuentas era más o menos estable y la gran mayoría de usuarios descargaban el correo por POP (frente al acceso remoto por IMAP), con lo que la necesidad de disco aumentaba pero muy lentamente y realmente no suponía un gran problema, podíamos ampliar discos cada X años pivotando el servicio al servidor de backup.
  • CPU: las gráficas de correos recibidos (cada vez más SPAM) a lo largo de los años crecían de forma exponencial. Cada año prácticamente necesitábamos doblar la potencia del año anterior.

Así que, ¿por qué no especializar los servidores, unos para almacenamiento y otros para proceso de SPAM y virus? Quitamos el servicio SMTP del servidor principal y su backup, y los sacamos a una primera línea de servidores SMTP con las siguientes características:

  • eran PCs normales y corrientes “de la tienda de la esquina” y apenas variaba la configuración de uno a otro (hostname, IP y poco más), con lo que teníamos un servidor “tipo” que podíamos replicar en cuestión de minutos en otra máquina en caso de que uno fallara o por el aumento de SPAM recibido necesitáramos más recursos.
  • un router balanceaba la carga.
  • eran independientes del servidor central salvo para el paso final de entregarle el correo ya analizado: cada uno tenía su copia del LDAP (sincronizada con slurpd), su copia de la configuración y datos del anti-virus y anti-SPAM (sincronizados con csync2), y un caché/resolver de DNS (dnscache).
  • tenían logs en local pero también los enviaban a un servidor central de syslog.
  • no almacenaban los correos, dicho de otra forma no tenían cola: si un correo se analizaba y cumplía ciertos requisitos (lista negra, varias RBL, dirección incorrecta, etc.) se rechazaba directamente con un error SMTP y se cortaba la conexión; si se dejaba pasar (correo legítimo, o marcado como posible SPAM), se enviaba al servidor central y no se daba el OK al servidor origen hasta que el central no aceptaba el correo. Todo esto con qmail es muy simple sustituyendo el qmail-queue por qmail-qmqpc. Con esta medida conseguimos que aunque uno de los servidores se caiga no se pierda ningún correo, ya que al no haber recibido el OK el servidor remoto si se cortara la conexión volvería a intentar el envío pasados unos minutos.

Los buzones, POP e IMAP, maestro LDAP, webmail y cola de envío remoto permanecieron en el servidor central, aunque con el tiempo si hubiera sido necesario alguno de éstos servicios se podría haber sacado también a un servidor independiente.

Especialización por tipo de cliente

El siguiente problema que nos encontramos fue cuando hará cosa de aproximadamente 3 años empezó a aparecer el SPAM basado en imágenes y PDF: tuvimos que añadir al SpamAssassin un script que recomponía las imágenes en el caso de los GIF animados, y hacía OCR del texto. Este análisis aumentó mucho el consumo de CPU (tuvimos que pasar de 2 ó 3 servidores SMTP a 5) y aún así había ocasiones en las que un servidor se sobrecargaba y durante unos 5-10 minutos podía tardar del orden de 2 minutos en procesar cada correo. En el caso de conexiones desde otro MTA esto no es un problema, ya que aunque se llegara a dar un timeout, se reintentaría el envío; sin embargo si el remitente era un usuario final desde su MUA, un tiempo de entrega excesivo o incluso un timeout implicaban una llamada porque “el correo no va”. :-)

La solución fue dividir la granja de servidores SMTP y de análisis en dos: una para el correo externo y otra para el interno. A la primera es donde apuntaba el registro MX del DNS, y tenía todos los filtros anti-SPAM activados; la segunda es la que configuramos a los usuarios, tenía los filtros más pesados desactivados y requería autenticación SMTP para cualquier IP externa. Así conseguimos que cualquier correo externo a nosotros pasara por todos los filtros, mientras que nuestros propios usuarios no sufrían las consecuencias del consumo de recursos del anti-SPAM.

Esquema final

[/spanish][english]

At my previous job I was responsible for the MTA of a group of companies, handling around 3000 e-mail accounts spread over 20 domains. This MTA received around 150,000 mails daily, and over 95% of them was discarded/marked because it was identified as SPAM or viruses (as of last year, don’t know how this evolved since I left). We used a homegrown cluster of seven servers, which enabled us to scale as needed. And it was based on free software.

This is not an step-by-step installation guide with technical details and configuration files, but rather the story of the evolution of the service, the various problems that we faced, how we solved them, and the design decisions in each case.

Migration

The first incarnation of the server was in 2001 when we had to migrate the old server, which was starting to give lots of trouble, to more current software and hardware. I seem to remember it was a mail server from Netscape (!?) that stored the account information in an LDAP directory, but can’t recall the exact name or version of the product. The server we chosed for the migration was qmail-ldap, mainly because of the good reviews we read about its stability, reliability and security, ease of setup (personally I still think qmail is much simpler than eg sendmail) and because it also used an LDAP directory. The latter may seem a silly reason, but in the end the migration had to be done in extremis at a time that the original server wouldn’t even boot most of the times, and we got away with it with a simple ldapsearch and a little script that “translated” the LDAP scheme of one server into that of the other one. Over time the choice of qmail-ldap proved to be the right one, because thanks to its modular design it allowed us to progressively move from a one server deploy to the cluster that I refered about in the introduction.

This first server was a rack-mounted one, with redundant power supplies and hw RAID5, so that all the data was secure (or so we thought back then). We also rolled qmail-scanner and the Kaspersky anti-virus (there was no ClamAV yet, we moved to it some years later). The same server held the SMTP, POP, IMAP and WebMail (SquirrelMail) services.

Active/Passive backup

We had to do the first architectural upgrade a couple of months after the migration: a RAID5 hiccup lead to a corrupted filesystem which was quite difficult to fix. It became clear that the RAID discs and the redundant power supplies were not enough to ensure the data integrity and service availability, so we installed another server exactly like the first one, and synchronized the configuration and mailboxes using rsync and cron jobs. The switching from the primary to the backup server was manual back then, using NAT at the router.

Over time the server was upgraded to new models several times, but we kept the active/passive backup structure. The syncronization between both servers was also improved, with DRBD for the mailboxes and csync2 for the configuration, AV bases, and so on. Master-backup monitoring and service switch was automatized with heartbeat.

The SPAM flood, specialization by resources

Sometime around 2002-2003 viruses ceased beeing e-mail’s biggest problem: the increasing number of SPAM messages received every day was way worse. So we threw SpamAssassin into the mix. Over time this lead to an ever-increasing CPU and memory consumption, slowing the server to a crawl. At first it seemed that the only option was to migrate every year to a new, more powerful server (and what would we do with the old one then?), or have multiple servers and distribute all the domains among them in an attempt to distribute the load.

Finally we realized that we had two different kinds of resource needs, with different growth patterns:

  • HD space for the mailboxes: the number of mailboxes in our system was fairly stable and the vast majority of our users downloaded their e-mails using POP, so HD scalability wasn’t really that big of a problem for us. We could easily afford to upgrade disk every few years, moving the service to the backup server while we were upgrading the master one.
  • CPU for the filtering: SPAM was growing at an exponential rate, we basically needed to double the CPU power each year.

So, why not specialize our servers into storage servers and a filtering farm? We moved the SMTP service from the main servers to a front-line of SMTP servers with the follwing characteristics:

  • they were off-the-shelf PCs and their configuration was practically identical (no variations appart from hostnames and IP addressess). We prepared a system image we could easily dump in a matter of minutes to a new PC, in case one of the servers went down or we needed more raw CPU power because of an increase in SPAM.
  • we had a router load-balaincing port 25 among all these servers.
  • all these SMTP servers were independent from the central ones, except for the final step of delivering the already analized mail to its destination mailbox: each server had a local copy of the LDAP directory (synchronized with slurpd), a copy of all the configuration files and all the AV bases and the SpamAssassin bayesian database (synchronized with csync2), and a DNS resolver/cache (dnscache).
  • they did local logs, but also sent them to a centralized syslog server for easier analysis.
  • they didn’t store the mails locally for later delivery, in other words they had no delivery queue: e-mails were analyzed on the fly during the SMTP session and if one of them met certain anti-SPAM/AV criteria (blacklisted IP, a number of RBL hits, certain keywords, etc.) it was immediatelly rejected with an SMTP error and the connection was closed; on the other hand if the mail was let through (it was either legitimate, or marked as possible SPAM), it was sent to the central server on the spot, and the filtering server never gave the OK to the origin MTA until the mailboxes server acknowledged the delivery. This is done quite simply with qmail by means of replacing the qmail-queue binary with the qmail-qmqpc one. By doing this we were able to guarantee that no mail would be lost in the event that a filtering server crashed, as the origin MTA wouldn’t receive the OK from us and would re-try the delivery after a couple of minutes.

Mailboxes, the POP and IMAP services, the LDAP master, webmail, and the remote queue remained in the central server, although most of them could have been moved to independent servers if needed, but we never needed to.

Specialization by type of client

The next problem we faced came about 2-3 years ago when image- and PDF-based SPAM became popular: we added an SpamAssassin plugin which re-composed animated GIF images and did OCR to all image attachments. This extra analysis greatly increased our CPU needs (we had to go from 2 or 3 filtering servers to 5 in a couple of days) and even so there were times when a server got overloaded for some 5-10 minutes and an e-mail could take not less than 2 minutes to be processed, delivered and SMTP-OK’d. When this happened and the sending party was another MTA it represented no bigger issue, as in the event of a timeout or disconnection the remote server would re-try the delivery several times; however, if the sender was an end-user with his MUA, a longer-than-usual delivery time or (God forbid) an error message from Outlook because of an eventual dropped connection lead to a phone call to the IT team because “the mail wouldn’t work.” :-)

The solution was splitting the SMTP and analysis farm into two: one for external mail and another for internal ones, for our users. The first farm is the one the DNS’ MX records pointed to, and had all the SPAM filtering options activated; while the second one retained the domain name end users used as the SMTP server in their MUAs, had all the heavy-weight lifting filters disabled and required SMTP authentication (wouldn’t accept non-authenticated sesions even for local domains). This way all external e-mail coming from remote MTAs would go through all the filters, and our users went to the privileged servers with somewhat lesser filering capabilities (but enough for internal mail) and great response times.

The big picture

[/english]