Mejorando el rendimiento de sendmail

Imagen de Epe

Tema: 

En este articulo hablaremos un poco sobre como realizar ciertos ajustes para acelerar el procesamiento de mensajes con sendmail.

Ultimamente hemos notado de que muchos administradores presentan dificultades en manejar una mediana cantidad de conexiones smtp (digamos, 50 conexiones por segundo) y optan por la solucion mas facil, que es usar algun otro MTA. En este articulo expondremos algunas soluciones para mitigra y realmente hacer efectivo el uso de sendmail como MTA.

Este articulo lo publico basado en la experiencia que tengo en trabajo con medianos (sobre las 50 conexiones por segundo) y pequeños (menos de 50 conexiones por segundo) sistemas de mensajeria y las dificultades crecientes que muchos administradores presentan para mantener un sistema estable.

El articulo aunque esta diseñado para ser probado en sendmail tambien aplica (atencion bellsouth!) para MTA en cualquier sistema operativo serio o pseudo ya sean estos variantes un*x o windows.

Sendmail tiene varios parametros para reconfigurarlo, y aunque parezca un engendro, es muy facil, siguiendo una serie de tips, mejorar el desempeño de este. Comenzaremos por ver un problema muy comun, y es que las conexiones a un servidor sendmail se demoran muchisimo en abrir, esto hace que ambas partes pierdan tiempo en enviar/recibir un pequenno mail, y sobre todo, en servidores medianamente congestionados podemos hacer que este explote de tantas conexiones abiertas. Imaginemos que tenemos un servidor con 50 conexiones por segundo entrando y el sistema demorando 30 segundos en abrir una conexion, que pasa al cabo de los 30 segundos? Pues que muy seguramente tendremos cerca de 1500 conexiones abiertas, esto es, 1500 procesos abiertos, 1500 sockets abiertos y todavia no ha comenzado a pasar el primer mail, a esto sumenle que muchos mensajes demoran algunos minutos en terminar de ser enviados lo que incrementa en algo la carga (claro esta, algunos mensajes salen mas rapido y asi por el estilo).

Que soluciones tenemos a este problema? Las comunes, las provenientes de una falta total de investigacion se centran en el problema en si, pero no miran la causa. Recuerden que los administradores no se deben dejar guiar por falsas pistas, sino por causas de los problemas. Lo mas normal es que los administradores tomen las siguientes medidas:

1- Bajar el demonio del MTA hasta ver que hacer: En efecto, existe la costumbre microsofica de apagar o reiniciar el servicio a ver si se mejora. En efecto, muy posiblemente mejore de forma temporal, porque al bajar el servicio se cierran todos los sockets, se eliminan todos esos procesos de la tabla de procesos y se libera la memoria. Y listo, se soluciono el problema. Que tal de los usuarios? Ah, que esperen que estamos en problemas!

2- instalar otro servidor de desbordamiento: Es una buena solucion para cuando el servidor principal nos deja de funcionar, ponemos otro servidor con un MX mas bajo, para que reciba los mensajes. cuando no podamos. Buena idea, pero que tal de los usuarios internos que solo ven nuestro servidor principal (lo configuran directo en su outlook). Bueno, es algo, y es un paso de avance. Mis respetos al que al menos logro hacer esto.

3- Instalar uno o varios servidores de frontera: Otra solucion para bajarle carga al servidor principal de mensjeria es instalar uno o varios servidores que reciban el trafico entrante a nuestro sistema y que se ocupen de enviar el trafico saliente. Que tiene de bueno esto? El servidor principal solo se ocupara de recibir los mails de nuestros clientes, pero no los envia directamente, sino que los entrega a un servidor que se ocupara unicamente de reenviar estos mensajes al exterior, reintentar una o varias veces, etc, etc.. el servidor principalk nuestro solo tomara centesimas de segundo para reenviar los mails de nuestros clientes a este servidor de frontera. Lo mismo ocurre con un servidor de frontera para recibir mails.. este servidor aceptara y procesara todos los mails entrantes y en fracciones de segundo se ocupara de hacerle reenvio de los mails al principal, ahorrandole el tener varias conexiones abiertas por servidores externos. esta solucion mitiga grandemente el problema, pero aun asi, hemos comprobado que una gran cantidad de conexiones se realizan por clientes internos enviando mails.. y eso no lo podemos evitar con esta solucion.

4- Instalar un cluster de servidores MTA para procesar la mensajeria siguiendo parametros definidos. Esta es una buenisima solucion, muy buena para la empresa interesada y excelente para la empresa que realiza la venta, porque una solucion de este tipo pasa de los 5 digitos y puede facilmente alcanzar los 6. No es una solucion para el comun de las empresas, pero en realidad seria una maravilla el que muchas empresas la pudieran tener.

Bien, que tenemos hasta ahora. Tenemos que cualquiera de las soluciones anteriores solo mitigan el problema, por que? Porque en realidad no se ha llegado a la causa del problema, sino que se ha tratado de mitigar sus consecuencias.

Cuales son las posibles causas y como se pueden eliminar para obtener un mejor performance?

1- La cola de mensajes es muy grande: A veces la cola de mensajes se llena de mensajes parcialmente recibidos, etc, que sendmail cada cierto tiempo elimina, pero hay veces que estos mensajes quedan ahi por dias o meses, si se dan cuenta, recibir 50 mensajes por segundo es mas o menos tener un trafico de 30mil mensajes por dia (contando que el grueso de los mensajes se manejan en unas 10 horas de trabajo), por lo tanto. Si solo el 1% de estos mensajes se me quedaran almacenados por diversas causas, tendriamos unos 300 mensajes populando nuestra cola. Esto, con el transcurso de los dias puede convertirse en un problema (pensando en un tamanno de 10k por mensajes en 30 dias tendriamos 300*10*30=90M de mails basicamente inutiles).

En el codigo fuente de sendmail (www.sendmail.org podemos encontrar varias utilerias para mover la cola de mensajes viejos y darle una solucion a esto.

Realmente no es un tremendazo problema ni una grandisima solucion, pero ayuda mucho a eliminar un poco de mails espurios.

2- La cola de mensajes tiene que procesar muchos mails una y otra vez pues la conexion remota no se puede realizar. Este es el tipico warning, could not deliver the mail past 4 hours.... bien, se esta adoptando como estandar que mas que ayudar al usuario, lo confunde este mensaje y ademas, genera una sobrecarga en el sistema al tenerse que enviar cientos de estos warnings al dia. Normalmente ya no se estan enviando warnings a los usuarios. Ademas que de experiencia propia se que toma muchisimo trabajo convencer a un usuario enojado que el mail si llego.. a pesar de ese warning.. no, ellos piensan que es un error y envian el mail una y otra vez aumentando la carga del sistema!!

Solucion a este problema? Podemos evitar la sobrecarga tomando dos medidas:
- editar /etc/mail/sendmail.cf ( o /etc/sendmail.cf segun el sistema) y buscar estos parametros:

O Timeout.queuereturn=5d
O Timeout.queuewarn=4h

Estos parametros estaban bien para hace unos annos, cuando la mensajeria de un sitio remoto podia pasarse 4 y 5 dias sin funcionar. Ya no es la regla, sino la excepcion, ya nadie admite estar sin mensajeria mas de un dia a lo sumo 2 dias.. por lo tanto, eliminemos el warning (poniendole un tiempo mayor al de retorno) y bajemos un poco el tiempo de retorno:

O Timeout.queuereturn=3d
O Timeout.queuewarn=8d

Que hicimos aqui? Le indicamos al sendmail que solamente espere 3 dias para retornar un mail de un sitio no alcanzable (asi eliminamos de la cola estos mensajes que ya seguro no llegaran y tendremos una cola mas limpia) y ademas que emita el warning o advertencia a los 8 dias (si el mensaje se retorna como unreacheable a los 3 dias, no emitira warning al 8vo dia por razones obvias).

Hasta aqui hemos logrado que menos mensajes esten en cola esperando a ser enviados y no enviamos warnings. Hemos logrado un paso mas en pro de mantener nuestro sistema estable y funcional.

3- El algoritmo por defecto para el procesamiento de mensajes no es el mas adecuado: Por defecto, sendmail usa el algoritmo (buscar en el sendmail.cf):

O QueueSortOrder=priority

Para enviar los mensajes. Esto era porque hace annos, los mensajes tardaban mucho en enviarse y por lo tanto, si un usuario marcaba un mensaje como urgente, era de mas prioridad y el sistema hacia un esfuerzo mayor por enviarlo primero. Esto ya no es una realidad, un mail con cualquier prioridad, toma segundos en salir, por lo tanto ya no es una necesidad. Que trae como problema esto? Que los mensajes, antes de ser procesados, tengan que ser revisados uno a uno y ordenados por prioridad. Como tenemos un filesystem sincrono (ext2 o ext3) y los inodos no estan ordenados por hash, esta operacion puede durar hasta algunos minutos. Es por eso que en colas de mensajes grandes, vemos que el sendmail demora algunos segundos y hasta minutos en comenzar a enviar mails, por que? Porque se esta ordenando por prioridad.

La opcion mas usada antiguamente era ordenar, pero no ya por prioridad, sino por el host a conectarse, el sendmail cachea las conexiones, de forma tal que si acaba de mandar un mail a hotmail, mantiene abierta esta conexion por si acaso alguna de las proximas conexiones son para hotmail, solamente enviar el mail sin llegar al proceso de reconexion, etc. El host, te ordena los mensajes por host de destino, por lo que se aprovecha al maximo las conexiones cacheadas pues casi seguramente despues de enviar un mail a hotmail, seguira otro mas para hotmail, asi nos ahorramos el tiempo que se toma el sistema en abrir una conexion y cerrarla.

Pero esto no elimina el problema de tener que reindexar todo el directorio de archivos, es por esto que , para servidores muy cargados, en los que no importe tanto el usar una conexion previa (cacheada) y mas importe el no recorrer el directorio realizando ordenamientos por prioridad, tiempo, host, etc.. entonces podemos usar estas opciones:

QueueSortOrder=Filename
o QueueSortOrder=random

La primera no ordena los archivos, no los abre para leer informacio, sino sencillamente los va tomando a medida que van llegando. Asi se disminuye la carga en el sistema por tener que realizar ordenamientos. Sin embargo, introduce otro problema, y es que si un archivo esta bloqueado por estar leyendose, se tiene que ir a leer el siguiente y asi el mismo proceso hasta encontrar un mensaje disponible. Estas colisiones se disminuyen si se toma la lista de archivos y sencillamente se randomiza, asi es menos probable toparse con un mensaje bloqeuado por otro proceso. Por lo tanto sugerimos que, para evitar al maximo lo que son accesos a disco, se use:

QueueSortOrder=random

4- Sendmail usa una sola cola. Al usar una sola cola, los queuerunner (procesos revisando la cola) recibiran constantemente bloqueos de los otros, que tal si ponemos mas colas de procesamiento de mensajes? Si ponemos por ejemplo 5 colas, tendremos que se recibiran 1/5 veces menos bloqueos pues los queuerunners se distribuiran entre ellas. Claro, depende del tmanno del sistema el poner una cantidad de colas. He tenido sistemas que he puesto hasta 16 colas y realmente funciona un poco mejor. Pero atencion, no vale la pena si usted esta manejando menos de 20 mensajes por segundo, realmente no vale, es una perdida de tiempo.

Para poner multiples colas, buscamos en el sendmail.cf a:

O QueueDirectory=/var/spool/mqueue

y le agregamos un wildcard, por ejemplo:

O QueueDirectory=/var/spool/mqueue/q*

esto significa que el sendmail debe buscar directorios que comienzan con q y asumir que son colas. Esto si, debemos manualmente crear estos directorios:

service sendmail stop
cd /var/spool/mqueue
mkdir q1
#movamos todo lo que hay actualmente hacia la primera cola
mv * q1

# creemos el resto de colas
mkdir q2 q3 q4 q5 q6 qmil

arranquemos el sendmail:
service sendmail start

hago notar, aparte de las colas q1-q7 y qmil no debemos tener ningun otro archivo.. por eso muevo primero todo hacia q1.. sino, el sendmail fallara en arrancar.

un simple mailq a los pocos minutos, nos indicara que mensajes hay que cada cola corriendo.

Ah, si no les gusta la idea.. entonces paren sendmail, copien todo lo que hay en q*/ hacia /var/spool/mqueue para volver a tener una sola cola, y quiten /q* del O QueueDirectory=/var/spool/mqueue/q* en el sendmail.cf

5- La ultima y la mas fuerte de las recomendaciones, es disminuir el tiempo de conexion inicial. Lamentablemente, la enorme cantidad de firewalls y ruteadores presentes en el mercado, bloquean no solo el icmp echo (ping) sino en general todo tipo de paquete icmp, entre ellos, uno usado para hacer identificacion de las conexiones y manejar el tamanno de paquete a ser usado. Muchos ruteadores no realizan fragmentacion/defragmentacion de paquetes y envian un paquete icmp indicando que no se puede fragmentar/defragmentar un paquete, al ser bloqueados los paquetes icmp por los firewalls, este paquete nunca llega y los servidores demoran mucho mas tiempo en darse cuenta del problema (a veces ni cuenta se dan).

Por eso, es recomendado, indicarle al sendmail que no realice identificacion de las conexiones, con el objetivo de no perder tiempo contactando a un servidor ident remoto (que ya nadie tiene activo).. sino que acepte las conexiones inmediatamente. Para esto, editamos el sendmail.cf y buscamos:

O Timeout.ident=5

Hay servidores antiguos que esperaban 30s, en todo caso, descomentemos esa linea y pongamosla en 0s

O Timeout.ident=0s>

cero segundos para esperar por ident, es decir, no esperes por ident. Recuerden que si el servidor esta procesando 50 mensajes por segundo, en 5 segundos tendra ya 250 coenxiones abiertas y todavia sin procesarse. Estamos eliminando este tiempo.

Ahora, eliminemos el paquete PMTU que es el bloqueado por firewalls mal configurados.

Agreguemos esta linea al final de /etc/rc.d/rc.local

iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

Quieren comprobar que ahora trabaja mas rapido? Andinanet tiene eliminado el ident, es decir, lo tiene en 0. Hagamos un telnet al puerto 25 y verifiquemos cuanto tiempo demora entre Escape character is '^]' (el momento en que se abrio la conexion realmente) y recibir el 220 del servidor:

telnet mail.andinanet.net 25

En mi caso no demoro nada, sencillamente abrio de una (despues de hacer el lookup de la URL claro esta)

Veamos ahora un servidor que no tiene este parametro ajustado, satnet y etapaonline:

telnet cue.satnet.net 25
Escape character is '^]'.
220 cue.satnet.net ESMTP Exim 3.33 #1 Tue, 28 Oct 2003 18:04:51 +0500

22 segundos demoro en sacar el mensaje 220!!!!!!!! 22 segundos, si hacemos un simple calculo, 22 segundos a 10 conexiones por segundo (solamente 10 por poner un numero), estamos teniendo en ese servidor unas 220 conexiones en espera... y Uy que horror, esos sennores andan por alla por moscu.. tienen la zona horaria +5 !!!! cuando debia ser -5, parece que no se preocupan mucho por tracear problemas en los que las horas intervienen.

veamos mail.ecualinux.com, lo tengo con ident en 0:

telnet mail.ecualinux.com 25
Trying 216.40.227.32...
Connected to mail.ecualinux.com.
Escape character is '^]'.
220 www.ecualinux.com ESMTP Sendmail 8.10.2-SOL3/8.10.2; Tue, 28 Oct 2003 18:22:37 -0500

Sencillamente abrio, sin problemas.. de una, sin esperar. A proposito, ecualinux parece que tiene sendmail 8.10.2 pero en realidad es un backporting de todas las correcciones ultimas de sendmail.

veamos etapaonline que seguro no tiene el ident (ninguna empresa se ha tomado el trabajo de poner ident 0):

telnet mail01.etapaonline.net.ec 25
Trying 200.55.224.71...
Connected to mail01.etapaonline.net.ec.
Escape character is '^]'.
220 megatron.etapaonline.net.ec ESMTP Sendmail 8.12.8/8.12.8; Tue, 28 Oct 2003 18:13:57 -0500

5 segundos... a 10 conexiones por segundo, tienen al menos 50 conexiones siempre en espera.

Claro esta, esto son promedios, pero de todas formas, por que hacer esperar a una conexion entrante, si podemos hacerla pasar mas rapido.

A proposito, etapa usa sendmail pero una version que parece no esta parcheada, pero ya eso sera tema de otro articulo. Espero este les guste.

Hay muchas otras cosas que se pueden usar para ajustar el sistema, pero suficiente por hoy.

Saludos
Ernesto
http://www.ecualinux.com