El tema del hosting suele ser un dolor de cabeza, y si además
necesitas una arquitectura sofisticada, o escalable debes sacar el
talonario y prepararte a gastar dinero que desde mi punto de vista no
tiene justificación. Como llevaba probando y “jugando” con Amazon EC2 desde hace tiempo
decidí hacer pruebas. Después de horas de cálculadora y viendo que nos
costaría sólo un poco más que Acens (pero menos que otras ofertas que
recibimos) pero nos daría mucha más flexibilidad y fiabilidad decidimos
hacer la migración.
La arquitectura básica es la que se muestra en la siguiente imagen
(prohido reirse de mi dibujo a mano con la mejor voluntad y dedicación):

Actualización: Britxardo me envió un par de parches con los esquemas:

Tenemos dos servidores (instancias) permanentes, aws0 y aws1, y un número variable de instancias
que sólo atienden a las peticiones web. Todos los servidores ejecutan
Ubuntu Karmic, de las imágenes (AMI) oficiales de Ubuntu para EC2.
El kernel que se ejecuta en cada instancia debe ser compatible con
el “anfitrión” de Amazon, Canonical tiene un convenio con Amazon para
validar su kernel, que son mucho más modernos que otras distribuciones
(estas imágenes –AMI– se encuentran en “Community AMIs” de EC2).
También nos interesaba Ubuntu Karmic porque en la migración
cambiaríamos el tipo de motor de la base de datos a MyISAM a InnoDB y
la versión de InnoDB del MySQL 5.1 está muy mejorada con respecto a la
5.0 de la última Ubuntu LTS (08.04).
Servidor principal: aws0
Es el “servidor central”, del tamaño large (m1.large) de 64 bits, cuesta $ 0.38 por hora, con 7.5GB de RAM y además la imagen está montada y arranca de un EBS (Elastic Block Service,
un “disco persistente” con mejor rendimiento). Además de las ventajas
obvias de persistencia de datos permite que en cualquier momento
detengamos la instancia y la volvamos a iniciar con otros parámetros,
por ejemplo más CPU y memoria RAM.
Los servicios que da este servidor son:
- Servidor MySQL: Aprovechando la migración hemos pasado a usar InnoDB y usar las transacciones/commit donde más hacían falta.
- Servidor NIS y NFS: Todo el software del Menéame y las cuentas de
usuario están exportadas por NFS que son usadas por aws1 y las
instancias web. Para centralizar la gestión de cuentas usamos el NIS.
- Servidor principal DNS: Es el primario de la zona meneame.net y notame.net.
- Servidor memcache: Usado por todas las instancias web.
- Servidor SVN para la gestión del software.
- Servidor SSH
- Servidor SMTP: Recibe todos los correos desde las otras instancias.
Debido a las restricciones de Amazon EC2 para el correo saliente, todos
los correos lo enviamos vía AuthSMTP (más adelante lo explicamos)
Teníamos muchas dudas de cómo se iba a comportar con carga real a la
base de datos, y estamos gratamente sorprendidos, durante el
funcionamiento normal no supera el 50% de uso de la CPU como se puede
ver en la imagen (los picos de madrugadas son por el cálculo del karma
y otras tareas como generación de “summaries”)

Mysql
Es fundamental la configuración mínimamente correcta del servidor
MySQL, en caso de InnoDB es muy crítico el tamaño del buffer, como
tenemos un total de 7.5 GB le asignamos 5 GB de RAM.
default-character-set=utf8 |
set-variable=max_connections=1000 |
set-variable=query_cache_wlock_invalidate=True |
innodb_buffer_pool_size = 5G |
innodb_flush_method = O_DIRECT |
innodb_flush_log_at_trx_commit = 0 |
innodb_additional_mem_pool_size = 16M |
innodb_log_buffer_size = 8M |
innodb_file_per_table = True |
read_rnd_buffer_size = 2M |
max_heap_table_size = 64M |
Servidor secundario y de backups: aws1
Esta es una instancia m1.small, de 32 bits, cuesta $0.095
por hora, con 1.7 GB de RAM, un sistema raiz de 10 GB y otro adicional
(en /mnt) de 150 GB (ninguno de los dos es persistente)
- Secundario DNS
- Réplica en tiempo real de la base de datos (slave)
- Sphinx para las búsquedas
Esta instancia ya la teníamos funcionando desde noviembre como
secundario DNS, ahora simplemente la reaprovechamos para hacer más
cosas. Las fundamental es la réplica en tiempo real de la base de
datos, que nos sirve para hacer backups a discreción sin afectar al
servicio web, también lo usamos para muchos scripts de control que son
“pesados” en consultas (en MyISAM era imprescindible hacerlo así porque
bloquea todos los updates, con el InnoDB ya no se nota, de todas formas
seguimo usando el slave para estas tareas).
También estamos usando (no acabamos de configurar todo) el S3Sync
para hacer backups diarios a S3. El S3Sync emula de alguna forma el
tradicional rsync, pero para trabajar sobre el sistema de S3.

Réplicas con balanceo de carga y escalado automático
Decidimos poner todo el servicio web en un sistema que se autoescale
automáticamente, no sólo por la cuestión de precios sino por la
tranquilidad que en momentos de mucha carga no habrá problemas de
saturación (como nos ocurrió durante la Eurocopa, que tuvimos que montar un servidor adicional en EC2, pero al hacer réplica en EEUU de un servidor en España no iba muy fino por las enormes latencias).
La decisión fue poner todos los servicios web en una misma imagen:
- Apache con PHP para HTTP y HTTPS (puerto 80 y 443)
- Lighttpd para los ficheros estáticos (puerto 81)
Desde hace tiempo usamos mnmstatic.net como dominio para los
ficheros estáticos, esto mejora la carga desde el navegador y evita que
el navegador envíe los cookies de meneame.net [*] cada vez que se baja
una imagen, en las pruebas que habíamos hecho se ahorra hasta 14KB de tráfico de subida en cada página.
[*] No son sólo los nuestros, que son sólo dos, sino también todos
los que definen vía javascript los medidores como Google Analytics,
podéis verificarlo en vuestros navegadores.
Cada instancia al arrancarse monta por NFS el directorio donde tiene
acceso a todo el software y las imágenes (estáticas y dinámicas). Esto
se hace el el fstab, pero hay que tener cuidado con las opciones,
fundamentalmente la opción bg para que que bloqueada al arranque, y otras para aumentar la eficiencia y tolerancia a errores.
# /etc/fstab: static file system information. |
proc /proc proc defaults 0 0 |
/dev/sda3 None swap defaults 0 0 |
/dev/sda1 / ext3 defaults 0 0 |
/dev/sda2 /mnt ext3 defaults 0 0 |
x.x.x.amazonaws.com:/home /home nfs rw,bg,noatime,soft,nolock,intr,async,nodev,nocto,ac,actimeo=15 0 0 |
Balanceo (Load Balancer)
Como tenemos que balancear del puerto 80 dos tráficos diferentes (al
Apache y al Lighttpd) tuvimos que definir dos baleanceadores
diferentes (web-balancer y static-balancer). Cada balanceador te da una
dirección IP y nombre DNS. Uno de los balanceadores –web-balancer–
redirecciona a los puertos 80 y 443 y el otro –static-balancer–
redirecciona del puerto 80 al 81 del lighttpd.

Para cada una de las réplicas elegimos primero el tamaño pequeño (m1.small),
pero el primer día de funcionamiento (el lunes) y a pesar que durante
las fiestas el tráfico es bastante menor que lo habitual vimos que el AutoScaler creaba cinco instancias para poder atender a la demanda, y durante las horas de menor tráfico no bajaba de dos. Me soprendió bastante, no esperaba que el consumo del Apache fuese tan elevado.
Me puse a estudiar y comparar con el vmstat, ví que las estadísticas del monitor de EC2 (CloudWatch)
no se correspondían con estas. Investigando más me di cuenta del
problema, el vmstat –al menos con el kernel de Ubuntu– no toma en
cuenta la “capacidad” real de CPU asignada sino la del procesador. La
asignada es un 40% de la total de la CPU (fácil de comprobar, se pone
un proceso que consuma el 100% de cpu –por ejemplo yes > /dev/null– y se mira la columna idle del vmstat, en este caso el idle indicaba 60%).
Entonces tenía que buscar una solución más barata, y encontré una solución muy buena, bonita, potente y barata, usar una instancia orientada a CPU para el Apache. EC2 ofrece este tipo de instancias, las High-CPU Instances. Con la c1.medium
y por sólo el doble de precio ($0.19/hora) podía tener una máquina con
la misma memoria y algo menos de prioridad de E/S (que es muy poca,
sólo los logs del Apache) pero con 5 veces la capacidad de CPU.
Así que reconfiguré el AutoScaler para que cree las instancias del tipo c1.medium y problema solucionado: desde que lo pusimos en marcha no ha creado ninguna instancia adicional, nunca superó al 80% de CPU:

AutoScaler
La configuración del escalador automático es casi lo último que se
hace, necesitas tener definido los balanceadores y la imagen (AMI) que
se usará para arrancar las réplicas. A diferencia de lo anterior, que
se puede crear y modificar con la consola web del Amazon AWS, todavía
no existe esa opción para el AutoScaler (aunque seguro que la agregan en las próximas semanas).
Por ahora toca hacer con los scripts, se necesitan definir tres cosas que están mostradas en el código de abajo:
as-create-launch-config web01cpu --image-id ami-ami-d3fad1a7 --instance-type m1.small --group default,www |
as-create-auto-scaling-group
web-group --launch-configuration web01cpu --availability-zones
eu-west-1a --min-size 1 --max-size 6 --load-balancers
web-balancer,static-balancer --cooldown 180 |
as-create-or-update-trigger web-trigger --auto-scaling-group web-group --namespace "AWS/EC2" --measure CPUUtilization --statistic Average --dimensions "AutoScalingGroupName=web-group" --period
60 --lower-threshold 30 --upper-threshold 80
--lower-breach-increment=-1 --upper-breach-increment 1
--breach-duration 120 |
Con todo lo comentado anteriormente, esta es la situación habitual de funcionamiento del Menéame:

Para mostrar el funcionamiento del escalador puse la CPU de la instancia web al 100% (ejecutando dos yes > /dev/null para poner al 100% cada una de las CPU) para obligar a crear otra instancia.
Aquí se puede ver como el trigger indica HighBreaching, es decir que se superó el límite superior
$ as-describe-triggers web-group --headers |
TRIGGER TRIGGER-NAME GROUP STATUS NAMESPACE MEASURE STATISTIC PERIOD |
TRIGGER web-trigger web-group HighBreaching AWS/EC2 CPUUtilization Average 60 |
Aquí se oberva como la “capacidad deseada” es de 2 y que inició la creación y arranque de una nueva instancia:
$ as-describe-auto-scaling-groups web-group --headers |
AUTO-SCALING-GROUP
GROUP-NAME LAUNCH-CONFIG AVAILABILITY-ZONES
LOAD-BALANCERS MIN-SIZE MAX-SIZE DESIRED-CAPACITY |
AUTO-SCALING-GROUP web-group web01cpu eu-west-1a static-balancer,web-balancer 1 6 2 |
INSTANCE INSTANCE-ID AUTO-SCALING-GROUP AVAILABILITY-ZONE STATE |
INSTANCE i-04ef2c73 web-group eu-west-1a InService |
INSTANCE i-4e7dbe39 web-group eu-west-1a Pending |
Finalmente las dos instancias ya están activas y accesibles:
$ as-describe-auto-scaling-groups web-group --headers |
AUTO-SCALING-GROUP
GROUP-NAME LAUNCH-CONFIG AVAILABILITY-ZONES
LOAD-BALANCERS MIN-SIZE MAX-SIZE DESIRED-CAPACITY |
AUTO-SCALING-GROUP web-group web01cpu eu-west-1a static-balancer,web-balancer 1 6 2 |
INSTANCE INSTANCE-ID AUTO-SCALING-GROUP AVAILABILITY-ZONE STATE |
INSTANCE i-04ef2c73 web-group eu-west-1a InService |
INSTANCE i-4e7dbe39 web-group eu-west-1a InService |
Así es como se ve en la consola:

Problemas encontrados
Tuvimos dos problemas fundamentales.
HTTP SSL detrás del balanceador
El primero es que las instancias sólo “ven” la IP privada del
balanceador y envía una cabecera HTTP para indicar la original, esto no
ocasionó gran problema porque el software del Menéame ya estaba
preparado. Pero con el SSL (o HTTPS) la cosa es muy distinta, en estos
caso la conexión entre el balanceador y la instancia no es vía HTTP,
sino TCP (por razones obvias, la comunicación va cifrada) por lo que no
se recibe nada de información de la IP del cliente.
Esto obligó a modificaciones y agregar campos de control en los formularios de registro y login vía el servidor seguro. No fue nada importante, pero me obligó a pensar bastante en una solución razonablemente segura (digo razonable porque seguro que se le pueden encontrar problemas si se le dedica tiempo).
Envío de correo por AuthSMTP
El segundo problema fue inesperado y el que nos retrasó la migración
del día sábado al domingo. El viernes recibimos un email de Amazon
indicándones que estábamos enviando correo saliente (nuestras pruebas
de envío de validación de cuenta y recuperación de contraseña) y que
nos iban a bloquear la salida. Además vimos que agregan automáticamente nuestras IPs al Spamhause.
Aunque te envían el URL de un formulario para pedir la autorización
decían que hay que esperar por lo menos dos días hábiles (hoy nos
enviaron un email diciendo que ya estudiaron el caso y nos autorizan,
pero las IPs siguen apareciendo en Spamhause). Fue un momento de depresión, además ví que es un problema en Amazon por las medidas que tomaron para evitar spammers en su servicio.
Pero encontré una solución buena, bonita y barata: AuthSMTP.
Por un precio muy razonable ellos se encargan de enviar el correo de
tus usuarios o todo el dominio. Funciona muy bien y muy rápido, te
olvidas del problema que las IPs de tus servidores de correo aparezcas
en esas demoníacas bases de datos de spammers.
En nuestro caso la configuración del Postfix de aws0 fue muy
sencilla, sólo tuvimos que agregar las siguientes líneas al main.cf:
smtp_sasl_auth_enable = yes |
smtp_sasl_password_maps = static:USUARIO:PASSWORD |
smtp_sasl_security_options = noanonymous |
default_destination_concurrency_limit = 4 |
Con esto se solucionó el problema más grave que encontramos y pudimos hacer la migración.
Costes mensuales
- 1 m1.large: $ 273
- 1 m1.small: $ 64.8
- 1, 2 instancias c1.medium web de media (máxima): $ 164.16
- 2 TB transferencia de datos: $ 340
- 1 EBS de 50GB almacenamiento + aproximadamente 50 Mops= $ 11
- 3 instancias medias medidas con Cloudwatch: $32.4
- 2 Balanceadores: $40.32 + $16 de 2 TB de tráfico, $56.32
Coste total estimado: $ 941.68, al cambio actual del euro queda en 649 € mensuales
Fin
Sí, se pueden encontrar ofertas más baratas, y es un poco más de lo
que pagamos a Acens (pero mucho menos que otras ofertas que recibimos),
pero te olvidas de estar atado por una arquitectura, de tener que hacer
engorrosas y largas negociaciones si necesitas un nuevo servidor, de
abrir averías por la red (en Amazon está todo monitorizado y es público, una caída afecta a muchos servidores, así que no tardan nada en resolver).
Y lo más importante, es el futuro, mucho más sofisticado, pero
también más divertido (y creo que además más ecológico, el hardware
está mucho mejor aprovechado) y como administrador de sistemas/programador/CTO tienes una libertad y flexibilidad impensable hace pocos años.
Como conclusión, por ahora muy positivo. Ya veremos como evoluciona,
conociendo la historia de EC2 no dudo que agregarán cada vez más
servicios y que bajarán los precios una vez al año.
PS: Son las cinco de la mañana, lo publico como está, disculpad los errores que haya, ya los corregiré luego.