¿Qué infraestructura hay detrás de una web 2.0?

Hace muy poco, por serendipia, he descubierto un servidor vulnerable a un ataque DDoS debido a lo pesado que es. Reciente es el ataque que han sufrido Facebook, Google, LiveJournal y Twitter con dispares resultados: mientras que Facebook y Google aguantaban el tipo, aunque con lentitud, la web de Twitter (que no su api) caía unas horas y LiveJournal sucumbía ante el ataque. Y parece que todo se debe a un intento de silenciar a un usuario crítico con la guerra entre Rusia y Georgia aunque aún están investigándolo.

¿Qué infraestructura utilizan estas webs para soportar esta carga o minimizar daños frente a un ataque? Una investigación sobre el tema permite obtener técnicas y trucos para mejorar las webs y hacerlas más seguras frente a ataques DDoS. Evidentemente, no todas las opciones son aplicables en todos los casos y este tipo de sitios están preparados para soportar elevadas cargas: flickr sirve 1000 páginas por segundo que precisan unas 25000 transacciones SQL; la Wikipedia tiene 7 millones de usuarios registrados y soporta 50.000 peticiones HTTP por segundo y unas 80.000 consultas SQL (por segundo); Facebook por su parte tiene 250 millones de usuarios y ronda los 200 millones de visitantes únicos; WordPress.com ronda los 100 millones de visitantes…

Además, estas web 2.0, a diferencia de las inamovibles páginas HTML de la web 1.0, son dinámicas. En cualquier momento pueden recibir nuevo contenido o ser alteradas. Para ello necesitan algún tipo de lenguaje que se ejecuta en cada petición lo que implica un lenguaje de programación en servidor, BBDD…

Recomendaciones de Yahoo

Un posible punto de partida, y lectura imprescindible para planear un sitio web, son las Prácticas para acelerar sitios web de Yahoo. Se trata de un conjunto de recomendaciones y micro-optimizaciones que van desde reducir el número de ficheros a descargar empaquetándolos (CSS sprites, maps), servir de forma separada los elementos estáticos hasta configurar adecuadamente la cache o servir el código HTML, javascript o CSS optimizado y comprimido.

Muchas de estas técnicas, permiten ahorrar unos pocos ciclos de ejecución y algunos bytes de ancho de banda, aligerando una web y mejorando la experiencia del usuario. Aunque ayudan (evidentemente, suprimir una única petición en varios millones de visitas son varios millones de peticiones menos), no son suficientes para soportar miles de usuarios concurrentes o aguantar el tipo frente a un ataque. En estos casos es necesaria una planificación e infraestructura acorde a las necesidades.

La piedra de Rosetta: la cache

Dejando a un lado la infraestructura física, la clave de tolas optimizaciones es la cache. En la web existen muchas caches (la del navegador, los proxys que instalan los ISP…). Cada acierto en uno de los niveles supone una petición menos al servidor final con lo que su correcta configuración reduce en gran parte el tráfico que se debe soportar.

Esta cache «externa» reduce el número de peticiones al servidor. Sin embargo, ese concepto puede utilizarse para mejorar sensiblemente el rendimiento de la aplicación. Muchos de los típicos sitios web (por ejemplo, muchos de los CMS) realmente generan una y otra vez el mismo contenido estático, realizan las mismas consultas a la BBDD y ejecutan los mismos códigos… Si se almacenasen dichos resultados, se suprimiría enormemente el tiempo destinado a los mismos al reducir tiempo de proceso, conexión…

Una posible opción es el proxy-inverso. Se trata de un elemento que se sitúa entre el servidor web y el usuario y que almacena las respuestas. De esta forma, el primer usuario tiene que esperar a la ejecución del servidor web mientras que el segundo recibe la respuesta del proxy-inverso de forma «instantánea». Evidentemente, los datos almacenados no serán tan frescos pero con un sistema de invalidación (por ejemplo, anular las entradas una vez al día o cada hora) se puede obtener un resultado de compromiso suficientemente bueno.

El proxy-inverso, almacena toda la respuesta. Sin embargo en algunos casos nos interesa almacenar solamente una parte de la misma (un proceso muy lento, o una consulta de informes muy pesada). Para ello se disponen de unas BBDD un poco especiales: simplifican la estructura a un sencillo modelo clave-valor y suelen almacenarse en memoria. Su rendimiento es muy superior a las BBDD relacionales estándar y tienen el plus de poder consultar los datos desde prácticamente cualquier aplicación. Algunos ejemplos son memcached o redis

Utilizando estos sistemas, la optimización típica es almacenar el resultado de las operaciones pesadas en el sistema (consulta a la BBDD, petición a un servicio web) y utilizar dicha información en las subsiguientes peticiones. Como en el anterior caso, es necesario invalidar la copia del proxy-inverso para obtener datos suficientemente actualizados.

Aplicando correctamente esta simple solución software, se puede incrementar notablemente el rendimiento de la aplicación sin ningún coste adicional.

Hasta el infinito y más allá

Sin embargo, en algunos casos no es suficiente y es necesario plantearse ir más allá. En este caso es necesario escalar horizontalmente añadiendo nuevos equipos para distribuir la carga. Esta aproximación tiene multitud de opciones pero básicamente se puede realizar de dos formas.

Una solución sencilla es la replicación, es decir, utilizar dos servidores idénticos para servir el sitio. Bien aplicado, este esquema permite admitir el doble de carga y crecer en un futuro añadiendo nuevos clones. Sin embargo, este sistema presenta el problema de coherencia: todos los sistemas deberán replicar sus datos para proporcionarle al usuario la misma información independientemente del clon.

Otra opción es separar servicios. En este caso, se dejaría el servidor web (con su lenguajes de scripting) en una máquina, las BBDD en otra diferente… La carga queda distribuida y es posible afinar la configuración hardware de cada sistema para su cometido (mejores discos en la BBDD, mejor procesador en los servidores web, más memoria en servidores de caché…).

El siguiente paso es replicar algunos de los servicios. Por ejemplo, de un esquema compuesto por un servidor de BBDD, un servidor web y un proxy inverso; se podría añadir uno o dos proxy-inversos para admitir más clientes concurrentemente, o u segundo servidor de BBDD para mejorar su rendimiento.

El paso final es distribuir las réplicas geográficamente. En este caso, se tendría un servidor proxy-inverso por continente o duplicar toda la estructura en cada continente manteniendo una BBDD maestra centralizada… las posibilidades son infinitas.

La wikipedia a examen

La infraestructura de la Wikipedia ha sido presentada en sociedad. Se trata de un sitio mastodóntico y tiene una infraestructura del mismo nivel que engloba todas las posibles optimizaciones anteriores junto con algún detalle extra (geolocalización de las réplicas más adecuadas según la ubicación del usuario).

Fuentes:

Los comentarios están cerrados.