General

Carga rápida y masiva de Memcache para Nginx

Hace años escribí sobre como uso Nginx como proxy de Apache en algunas instalaciones. En esa arquitectura contemplo Memcache. La configuración es muy sencilla, basta agregar a la sección location que queramos cachear lo siguiente:

set $memcached_key $uri;
memcached_pass 127.0.0.1:11211;
error_page 404 @fallback;

Y agregar el location @fallback correspondiente:

location @fallback {
proxy_pass http://localhost:8000;
}

El único problema, como algunas personas que han usado Nginx con Memcache, es que alguien tiene que llenar Memcache con objetos para que Nginx pueda leerlos.

Usualmente, los desarrolladores de la aplicación usarán las librerías del lenguaje de programación para acceder a Memcache y cargar allí algunos objetos. Esto funciona, y es como la mayoría de la gente implementa este escenario. Sin embargo, si uno quiere cargar varios archivos de forma rápida a Memcache, no hay muchas herramientas sencillas y fácilmente disponibles.

Por ejemplo, hace dos meses en la wiki de Nginx alguien publicó un método para precargar memcache con Python. Es un enfoque interesante, pero complicado de mantener y decididamente experimental.

Sin embargo, memcache ya incluye un cliente llamado memccp que permite cargar archivos en Memcache. El problema es que este cliente no permite definir la llave con la que el objeto se almacena en Memcache. Esa llave es $uri, por ejemplo algo como /wp-content/plugins/akismet/akismet.gif.

Cuando Nginx encuentra un cliente que hace GET a este archivo, lo sirve desde Memcache, lo que en este escenario nos ahorra abrir una conexión TCP a localhost, que Apache atienda y responda una petición, y potencialmente I/O de disco.

Este parche a libmemcached permite que se defina una clave con –key, lo que facilita precargar archivos como imágenes o CSS en Memcache. Su uso es sencillo y se puede invocar desde un shell script (probado en dash)

#!/bin/sh
BASE=”/var/www/mysite”
for file in `\
find $BASE -type f \
-name ‘*.jpg’ -or \
-name ‘*.gif’ -or \
-name ‘*.png’ \
| sed “s#$BASE##”`
do
echo “Adding $file to memcached…”
sudo memccp –key=$file –servers=localhost $BASE$file
done

Entre otros escenarios que puedes activar en este caso, está el poder almacenar archivos para distintos hosts virtuales. En este caso sugiero que configures $memcached_key para usar $http_host y $uri, y añadas una variable de prefijo a tu script. También puedes correr otro memcache, si en realidad lo necesitas. memccp tiene otros problemas, por ejemplo no maneja la codificación de caracteres muy bien. Pero para archivos binarios, usualmente estáticos, ahorra bastante trabajo.

El repositorio en GitHub es un paquete fuente de Debian. Si tienes las dependencias (sudo apt-get build-dep libmemcached-tools) puedes construir el paquete (dpkg-buildpackage -b) e instalar libmemcached-tools que contiene memccp.

Este escenario es uno de los que describo en mi próximo libro rápido sobre Debian para aplicaciones Web, que está actualmente en fase de edición.

Standard
General

Reviviendo el Neo Freerunner en Campus Party Quito 2011

Hace más de 3 años puse mis manos en un Neo Freerunner, la segunda versión al público del teléfono celular del proyecto OpenMoko. Luego me pasé a Android (un HTC Magic) durante bastante tiempo y lo dejé por otros smartphones. Pero el Freerunner siguió ahí, desplazado por tecnología más nueva y por software más completo.

Hasta hace una semana.

Tuve la oportunidad de ser invitado a dar dos presentaciones en el escenario de Innovación de la Campus Party Quito 2011, y una de ellas era sobre clusters. Para lxs que me conocen, y lxs asiduxs al blog, la clusterización de servicios (y no la clusterización de cómputo científico) es un tema específico de expertise que me apasiona desde hace 5 años.

En mi paso por la Electrificación del Caroní, primero Corporación Venezolana de Guayana y ahora Corporación Eléctrica Nacional, una de las hidroeléctricas más importantes del mundo, le asignaron a mi equipo la responsabilidad de construir un cluster para correo electrónico con almacenamiento compartido.

El cluster, que se encuentra en producción, consta de servidores Itanium geográficamente distribuidos (EDELCA tiene una red que va desde Brasil hasta Colombia) así como balanceadores de carga en alta disponibilidad, un servicio de directorio propio, etc.

También he hecho clusters con máquinas virtuales anteriormente. Por ejemplo, hace algunos meses en el TechDay Quito, o en algunos screencasts de mi trabajo.

Pero para Campus Party Quito pensé que la gente necesitaba ver algo más cool, así que preparé un cluster sencillo, para un servicio sobre TCP (HTTP) usando HTML estático y los siguientes miembros del cluster:

1. Dos (2) máquinas virtuales Debian GNU/Linux 6.0 corriendo sobre Hyper-V en Windows Server 2008 R2, a su vez booteado desde un disco duro USB exteo (sí, se puede correr Windows en una memoria USB) con nginx como servidor Web

2. Una (1) máquina virtual Windows Server 2008 R2 con IIS corriendo PHP (específicamente Drupal) también sobre Hyper-V

3. Una (1) máquina física con Debian GNU/Linux Sid para las pruebas del cluster

4. Un (1) Openmoko Neo Freerunner corriendo Hackable:1 (distribución basada en Debian) y a su vez con lighttpd como servidor Web y conectado al router del cluster vía Wi-Fi

Con respecto a Hackable::1, la instalación fue straightforward. No usé microSD, sino la flash del teléfono. Tan solo necesité dfu-util (disponible en Debian) y las dos imagenes (keel y root filesystem) para flashear el dispositivo. Luego configuré USB networking para entrar por SSH al teléfono e instalar lighttpd y configurar la IP. Todos los detalles en el link de arriba.

El cluster está inspirado en la metodología Ultramonkey, específicamente en el escenario de HA+LB streamlined, donde se utiliza una sola capa de equipos Linux entregando HA, LB y potencialmente también el servicio. Esto reduce costos, pero requiere una configuración un poco cumbersome de ARP en las máquinas.

La verdad, la prueba salió bastante bien. El cluster respondió muy bien a los casos de prueba, que fueron:

1. Remoción en caliente de uno de los balanceadores de carga en alta disponibilidad

2. Remoción en caliente de uno de los equipos prestadores del servicio

3. Inclusión en caliente del balanceador de carga activo inicial (del caso 1) y restitución de la alta disponibilidad

Incluso, al final, el Freerunner se quedó sin batería (recuerden que era un miembro del cluster conectado por Wi-Fi) y el cluster lo manejó sin problemas.

También tuvimos la oportunidad de revisar diversas ventajas y retos de hacer un cluster, e identificar los SPOF (puntos únicos de falla) del cluster de demostración. Las láminas están publicadas aquí, y el video aquí. Gracias a todos/as los que participaron y se interesaron por el tema. Siempre estaré a la orden para cualquier inquietud.

Standard
General

Haciendo que nginx y Apache coexistan

nginx es un producto que me ha apasionado desde hace varios años. Su enfoque orientado a eventos lo hace inherentemente distinto a otros servidores Web, y en particular Apache y Cherokee que eran los dos que tenían mi atención allá en el 2006, cuando usaba Gentoo y pasaba la noche compilando cosas.

El rendimiento de nginx es increíble, y su funcionalidad como proxy reverso HTTP es incuestionable. Lo que la gente normalmente dice es que nginx tiene muy buen performance, pero pocas funcionalidades, especialmente cuando se compara con Apache, que tiene módulos para casi cualquier cosa.

A pesar de que mi primer proyecto grande con nginx fue justamente un caso de funcionalidades (hacer un proxy IMAP) estoy consciente de esa limitante, por lo que siempre he pensado que Apache es un buen servidor Web de backend, y nginx funciona muy bien en el frontend, como proxy reverso y caché. De hecho eso se aplica para otros servidores Web, incluso no solamente de código abierto sino propietarios como IIS o WAS.

Desde hace ya varios años utilizo una arquitectura sencilla pero con muchos beneficios, donde nginx se para delante de mis servidores Apache (que sirven aplicaciones con maravillosos módulos) y hace caché con el popular motor memcached.

Básicamente, en la instancia server de nginx correspondiente, y en el location que queramos servir, colocamos algo como:

location / {                set  $memcached_key  $uri;                memcached_pass 127.0.0.1:10101;                proxy_pass http://127.0.0.1:8080/;                ...        }

Lo que hacemos es pasar a un server memcached corriendo en 10101, y utilizar un proxy reverso en 8080, que en este caso sería nuestro Apache. La URI que el cliente desea es lo que se busca en memcache como una clave, y para eso sirve la primera línea. El resto de la configuración involucra instalar y correr memcached (en Debian y derivados, aptitude install memcached) e instalar, configurar y correr Apache en el puerto que definamos; y opcionalmente protegerlo con Netfilter (iptables).

Hay dos caveats de esta solución. El primero es que si delegamos los registros en Apache, solo veremos conexiones locales. Para eso hay soluciones, como pasar algunos headers con el host apropiado y usar un módulo de Apache (RPAF) en el backend. El segundo caveat es que no queremos que todas las aplicaciones pasen por el memcached, para ello podemos abrir el puerto del Apache, o bien definir otro server en nginx y solo usar proxy_pass.

En mi escenario, memcached ocupa alrededor de 2 MB. de memoria RAM cuando no hay carga y puede crecer hasta 64 MB. cuando hay carga (es el default) lo cual es razonable en un VPS.

¿Hay algo mejor que nginx? Sí. nginx y tu servidor Web preferido.

Standard
General

nginx como proxy balanceador de carga de otros servidores Web

Hace 4 años y medio que escribí por primera vez sobre nginx, un servidor Web orientado a eventos, muy robusto y elegante. En ese momento, Aníbal Rojas, uno de los programadores, profesionales y personas más creativas que conozco, me habló de nginx y de como se estaba haciendo popular en el underground de Ruby. Decidí dar un paso y empaquetarlo para Debian, era la versión 0.4.2

Mi primera experiencia con nginx en un proyecto grande fue utilizando sus capacidades de proxy IMAP para escribir un módulo de autenticación (en Perl) que consultaba en OpenLDAP el destino de una conexión entrante. Esto se convirtió en el IMAP de entrada de lo que es ahora la CORPOELEC.

Luego, me pregunté si podía usar nginx como proxy para Puppet, en vez del terrible servidor Web integrado (WEBrick) que traía en su momento.

Y desde hace algunos años, lo uso para hacer caché junto a memcached, escenario que publicaré en los próximos días. Pero nunca lo había puesto como un proxy balanceador de carga ante otros servidores Web, así que aquí explico como, y en realidad es bastante sencillo. Para hacerlo más interesante usé IIS en Windows Server 2003R2, tratando de emular lo que una organización con aplicaciones en .NET quisiera hacer para ahorrar costos de appliances de balanceo de carga usando software libre. Como nginx.

En mi escenario, nginx 0.7.67 corre en Debian, dirección IP 192.168.56.104, y IIS corre en dos máquinas Windows Server 2003, direcciones IP 192.168.56.101 y 192.168.56.102. En ambas he instalado Acquia Drupal, tal y como se instala con Web Platform Installer, y he nombrado a los sitios WS1 y WS2. He comprobado que puedo ver el sitio por las direcciones IP de los servidores.

En nginx, la configuración en el site (por ejemplo, en /etc/nginx/sites-available/default en Debian y derivados) se remite a:

upstream mycluster { # en el contexto http, que es el default de este archivo  server 192.168.56.101;  server 192.168.56.102;}...server {  ...  location / {    proxy_pass http://mycluster;  }}

Puedo hacer algunas variaciones, por ejemplo, dentro de upstream puedo definir un puerto para cada server, puedo usar IPv6, o puedo definir un peso (weight) para hacer un balanceo con preferencias, por ejemplo si uno de los servidores con Windows tiene más recursos de cómputo.

Un service nginx restart es suficiente para que la nueva configuración está funcionando y pueda llamar directamente a la IP (o host name) de nginx y utilizar los recursos del cluster. En Firefox, con http://192.168.56.104/acquia-drupal y recargando puedo ver como cambia el título WS1 y WS2 con cada request.

Si pongo un weight=2 en el primer server, cada 3 requests veré 2 con WS1 y 1 con WS2, y así sucesivamente.

¿Por qué es útil? Nuevamente, si tengo una granja de servidores de aplicación, potencialmente propietarios (como los de IBM o Microsoft) y administrados por una unidad dedicada que no necesariamente conoce de infraestructura, y quiero una solución rápida a un problema común (balanceo de carga) entonces puedo ahorrarme el overhead de tener appliances y hacer una solución en software con nginx sobre Linux. Y con nginx en el borde puedo hacer aplicaciones interesantes como el proxy IMAP del que les hablé o incluso colocar memcache antes de los servidores Web de backend.

Standard
General

Puntero Nulo – Episodio 5

Escúchalo:: PunteroNulo-Episodio5

La semana pasada me dijeron que el podcast era demasiado técnico. Gracias, aunque siempre intento digerir las noticias de tecnología algunos días después de que estén de moda (para no caer en la agenda) y de una forma que sea comprensible para la mayoría de la audiencia técnica.

Como siempre, sus comentarios y sugerencias son siempre bienvenidos en el correo j en bureado.com y en Twitter, @bureado.

De facto, el podcast ha estado saliendo quincenal así que mejor no engañaos. A continuación las fuentes de este episodio:

Standard
General

Apache/Mongrel como proxy para Puppet

Puppet es un gestor de configuraciones compuesto por un lenguaje declarativo para expresar configuraciones de sistema, un distribuidor de esa configuración y una librería para implementarla en distintos sistemas operativos. Ya había hablado sobre Puppet hace tiempo, en particular por tratarse del sistema de gestión de configuraciones propuesto para su uso en la DSLv2 de EDELCA.Asuma el lector como premisa que WEBrick no sirve (¡hola Aníbal Rojas!) y por lo tanto debe usar un proxy enfrente de puppetmasterd para poder hacer algo serio con Puppet.Puede usar varios servidores Web decentes, pero básicamente hay tres formas documentadas y probadas de hacerlo con éxito: Apache, pound y nginx.Apache y nginx son muy buenas elecciones. Si ya tiene algo operando en Apache, necesita algo específico de un módulo de Apache y no sufre de esa enfermedad que le obliga a querer poner un servidor Web distinto a Apache just for the sake of it, use Apache; el mantenedor de nginx en Debian sufre de severos problemas mentales.El problema es que, al menos en las versiones de Puppet disponibles en Lenny en su estado actual, Apache patea al puppetd de la máquina del puppetmasterd si el CRL está vacío y activa esta función, como está documentado en el reporte 1525.Como bonus, posteé ahí un snippet de configuración de Apache que funciona muy bien en un caso de referencia donde implementé Puppet con Apache como proxy, además de describir el problema de la CRL y como se soluciona comentando una línea.

Standard
General

Proxy IMAP/POP n-vías con consultas LDAP utilizando Perl y nginx

En un sistema de correo corporativo geográficamente distribuido, los buzones de correo se suelen servir desde el MDA más cercano al usuario. Como resultado, suelen haber dos o más servidores IMAP (o POP3) por región sirviendo correos a usuarios finales. Si el usuario se desplaza a otra región, debe configurar el cliente de correo electrónico de forma apropiada para revisar su buzón, o utilizar un cliente de correo Web (como IMP de Horde) que permita hacer login en todos los servidores.Sin embargo, cuando se requiere que el usuario acceda a su buzón remotamente (sin considerar VPN en este momento) con un dispositivo lector de correos (como un Blackberry) se presentan dos problemas; el primero es presentar el servicio en la capa de aplicación (para evitar usar NAT, por ejemplo) y el segundo es el de dirigir la consulta al MDA que corresponda. Lo primero es fácil de resolver con cosas como imapproxy o Courier. Lo segundo es el tema en cuestión de este post.nginx, un ligero servidor Web diseñado originalmente para ser un proxy HTTP, tiene la capacidad de servir como proxy IMAP (POP, SMTP también). nginx sabe IMAP y puede transformar una solicitud de inicio de sesión (0 login USER PASS) en una consulta HTTP para un servicio de autenticación:

GET ... HTTP/1.0Host: localhostAuth-Method: plainAuth-User: USERAuth-Pass: PASSAuth-Protocol: imapAuth-Login-Attempt: 1Client-IP: 127.0.0.1

Por lo tanto, es posible hacer un script (en cualquier lenguaje, FWIW) servido por HTTP que lea las cabeceras de esta petición y responda con información útil para continuar con la sesión IMAP utilizando una respuesta HTTP de este tipo (por supuesto, Auth-Status también puede ser Invalid):

HTTP/1.0 200 OKAuth-Status: OKAuth-Server: 10.10.10.10Auth-Port: 143

La parte divertida es cuando se usa Perl para el asunto, porque nginx embebe Perl bajo la clase nginx y es posible escribir un mecanismo de autenticación directamente como una subclase de nginx. La clase tiene acceso a todas las cabeceras de la petición y puede inyectar cabeceras y contenido a la respuesta, por lo que podemos cambiar la cabecera Auth-Server, por ejemplo, dependiendo de la lógica de nuestro script. En el caso que nos ocupa, escribiríamos una cabecera correspondiente al MDA donde está el buzón del usuario consultando un campo en LDAP.Algo muy básico podría ser esto:

package Auth;use nginx;use Net::LDAP;our $ip={};$ip->{'serverA'} = "10.10.10.10";$ip->{'serverB'} = "10.20.10.10";sub handler {  my $r = shift;  my $ldap = Net::LDAP->new( 'ldap-server' );  my $mesg = $ldap->bind;  $mesg = $ldap->search ( base => 'dc=tu,dc=base', filter => '(&(uid=' . $r->header_in("Auth-User") . '))' );  my $goto = $mesg->entry(0)->get_value('atributoLDAPconDireccion');  $r->header_out( "Auth-Status", "OK" ) ;  $r->header_out( "Auth-Server", $mail_server_ip->{$goto} );  $r->header_out( "Auth-Port", "143");  $r->send_http_header("text/html");  retu OK;}1;

nginx también debe ser configurado apropiadamente para utilizar la clase de autenticación y activar el proxy IMAP. El servidor web debe ser configurado con las opciones --with-mail y --with-http_perl_module. Aun estoy buscando la mejor forma de hacer distribuible esta configuración en el paquete que mantengo para Debian y Ubuntu (ya que nginx es muy pwd centered)Con un servidor nginx correctamente compilado, y con la clase de autenticación lista, basta agregar en /etc/nginx/nginx.conf algo como:

http {  ...    perl_modules  /etc/nginx/perl/lib;    perl_require  Auth.pm;    server {        location /auth {            perl  Auth::handler;        }    }}mail {    auth_http  127.0.0.1:80/auth;    imap_capabilities  "IMAP4rev1"  "UIDPLUS";    server {        listen     143;        protocol   imap;        proxy      on;    }}
Standard