Existen muchas arquitecturas, cada cuál más diversa y compleja. Adaptadas específicamente para que las aplicaciones que corren en ella, lo hagan de la forma más adecuada.
En un solo post sería imposible abarcar todas ellas. Sin embargo, tras años trabajando en diferentes proyectos con infraestructuras muy diversas. He podido comprobar que existen ciertas pautas sobre todo en cuanto a escalabilidad y alta disponibilidad que son comunes en casi todas las infraestructuras.
Arquitecturas básicas, no quiere decir que sean “sencillas” sino más bien que son conceptos escenciales para prácticamente cualquier aplicación.
Empecemos con el post!
Monolitos
En muchas ocasiones, queremos empezar por lo que más “tendencia” tiene en el sector, varios clusters de Kubernetes, arquitecturas complejas con varias replicas de bases de datos, etc. Cuando en realidad, la aplicación aún ni ha nacido o simplemente ni se ha validado el concepto en el mercado.
Esto siempre lo asemejo a intentar comprarse un ferrari y ni siquiera tener carnet de conducir ni el dinero para comprarlo.
La realidad es que si estas empezando de 0 o incluso si ya tienes tu aplicación pero aún no tienes un tráfico realmente considerable. Lo más recomendable es empezar con un monolito.
¿No me crees? Te voy a poner un ejemplo de una aplicación monolítica real de una empresa (por temas de confidencialidad no puedo poner su nombre) que empezó de la siguiente manera:
Como ves, su stack se componía:
- Servidor Web.
- Aplicación de Frontend.
- Aplicación de Backend (API).
- Una base de datos.
Todo conviviendo dentro de un mismo servidor.
Tras un par de meses empezaron a recibir e incrementar las visitas y dado que en este caso, la base de datos era la que más recursos consumía, decidimos externalizarla. Quedando este resultado:
Este cambio fue suficiente para mantener la aplicación casi un año a bajo coste y con buen rendimiento. Tras casi un año y con la app creciendo a un buen ritmo y sobre todo, asumiendo que la facturación de la empresa nos permitía meter más músculo.
Decidimos incrementar la capacidad añadiendo alta disponibilidad a nuestra aplicación, al menos en la parte de aplicación. Verás también que en esta etapa se generó una tercera aplicación. Este fue el resultado:
Aunque se añadieron algunos componentes más, la arquitectura de esta empresa a día en que escribo este post sigue siendo la misma. ¿Ha seguido creciendo en tráfico esta app? Por supuesto, con esta misma arquitectura actualmente están atendiendo miles de peticiones diarias.
Ventajas:
- Bajo coste.
- Baja complejidad.
- Reduce casi por completo la necesidad de aprender tecnologías adicionales a las usadas en la propia aplicación.
- Alta disponibilidad en la capa de aplicación.
- Escalabilidad, dado que todo estaba automatizado. Si se recibía más tráfico, “solamente” había que añadir un servidor más al balanceador.
Desventajas:
- No hay alta disponibilidad a nivel de base de datos.
- Escalabilidad, aunque se puede añadir un servidor más. Esto no se realiza de forma automática sino que requiere la participación de una persona.
Monolitos con crecimiento automático + microservicios
Dadas las desventajas indicadas anteriormente, te voy a poner el ejemplo de una empresa que actualmente recibe millones de peticiones mensuales. Como verás aquí aparte de resolver las desventajas anteriores empezamos a añadir un componente que seguro has oído hablar, los famosos microservicios.
¿Qué son microservicios?
Bueno, podríamos hacer un post aparte para hablar de este tema pero por resumir muy mucho.
Los microservicios son pequeños componentes que cumplen o realizan una funcionalidad específica. El conjunto de estos componentes, forman normalmente una o n aplicaciones que aportan a su vez al usuario un conjunto de funcionalidades.
En este caso concreto, los microservicios que utiliza esta empresa estan basados en Lambdas. Esta es su arquitectura:
Vamos por partes. ¿Cómo estamos resolviendo las deventajas que citamos anteriormente?
- Hemos dividido la base de datos en un cluster donde hay un master y varias réplicas de lectura. Dado que la mayor parte de peticiones a base de datos siempre son de lectura, suele ser lo que más “sobre carga” el servidor.
- Escalabilidad. En este caso utilizamos un servicio de AWS que se llama autoscaling, que nos permite definir diferentes reglas que hace que nuestra aplicación añada o retire servidores de forma totalmente automática.
Esto nos permite escalar fácilmente y además nos agrega un grado de alta disponibilidad.
Veamos ahora los componentes nuevos:
- CDN: Una CDN, entre sus muchas funcionalidades, nos permite distribuir ficheros estáticos a lo largo de todo el mundo y que además nos agrega un componente fundamental. Caché.
- Microservicios: como te indicaba en este caso sus microservicios se basan en eventos por tanto una solución que se adaptaba perfectamente a este caso de uso y sin necesidad de añadir complejidad a la infraestructura, era el uso de Lambdas.
Un buen sistema de caché nos reduce mucho la carga en nuestra aplicación y además nos reduce bastante el tiempo de respuesta y la lantencia. Esto último muy importante sobre todo cuando tus clientes/usuarios están en distintas partes del mundo.
Ventajas:
- El gasto a pesar de que ya empieza a ser alto. Sigue siendo bastante comedido y bien aprovechado.
- La complejidad para los desarrolladores sigue siendo baja.
- Alta disponibilidad. Tanto aplicaciones como base de datos están replicadas.
- Escalabilidad inmediata y prácticamente automática.
- Sistema de caché y CDN para distribución de ficheros estáticos a nivel global.
- Microservicios en Lambdas. Su escalabilidad es automática y apenas requieren manteniemiento.
Desventajas:
- El grupo de autoscaling. Si bien este servicio funciona muy bien, tiene varias desventajas:
- Hacer un sistema de deploy con una estrategia blue-green a pesar de que es posible, resulta lenta y aveces no demasiado efectiva.
- Para que los cambios se vean reflejados en todos los servidores que lo componen, se ha de hacer cambios sobre una imagen base (AMI) y para ver reflejados estos cambios habrá que “re-crear” los servidores del grupo de autoscaling. Existen tecnologías como Packer que simplifican esto pero no son demasiado amigables.
- Lambdas. Este servicio si bien para el caso de uso de esta empresa es casi perfecto, puede que no se adapte a otros casos, por ejemplo:
- Si el microservicio requiere una ejecución por proceso de más de 15 minutos, esto será incompatible con esta tecnología.
- Si el microservicio requiere un procesamiento de memoria o CPU muy alto, esto puede quedar limitado al usar Lambdas.
- Si el microservicio requiere de hacer uso de alguna librería o paquete específico a nivel de sistema operativo, esto hará incompatible el uso de Lambda.
Microservicios basados en contenedores
Hemos visto varias arquitecturas hasta ahora. Aunque entre apartados hemos usado diferentes empresas de ejemplo. Quiero que veas que la infraestructura debe crecer al ritmo que crece la aplicación y con ello la empresa.
Muchas veces queremos empezar desde el principio por esta parte asumiendo que vamos a tener nada mas empezar millones de visitas y usuarios. Y está muy bien ser optimista pero lo cierto es que no es para nada lo habitual.
He visto, por desgracia, alguna empresa que tras asumir mucho coste en su infraestructura, equipo, herramientas, etc. Debe empezar a recortar gastos por todos lados porque su facturación no da para más.
Por ello, siempre recomiendo crecer tu infraestructura de forma paulatina y a medida que también lo haga la empresa.
¡Ojo! Empezar directamente por aquí no te digo que este mal, sin embargo no es lo más recomendable principalmente por temas de costo y sobre todo por complejidad.
Ya que desarrollar microservicios para Kubernetes requiere un mínimo de conocimiento sobre esta tecnología, sino se terminará haciendo un mal uso de los contenedores (PODs) al querer asemejarlos y utlizarlos como si de un servidor completo se tratase.
Dicho esto veamos una arquitectura de un cliente que al igual que antes, está actualmente en producción y recibiendo cientos de peticiones POR SEGUNDO!
He omitido varios componentes y comunicaciones, sin embargo la esencia para entender la infraestructura se mantiene.
Vamos a repasar los nuevos componentes:
- Kubernetes: como sabrás esta tecnología se ha convertido en el estándar en cuanto a orquestación de contedores se refiere.
- Cluster de base de datos: si bien habíamos visto ya un cluster de master-réplicas. En esta ocasión tenemos un clúster multi-master con cuatro servidores. La diferencia principal es que mientras las replicas son de solo lectura. En un cluster multi-master, todos los nodos son de lectura y escritura.
- Caché de base de datos: al igual que comentamos en la capa de aplicación, un buen sistema de caché nos reduce mucho la carga, en este caso a nuestro cluster de base de datos. Además, nos reduce bastante el tiempo de respuesta y la lantencia.
Ventajas:
- Alta disponibilidad en todas las capas (aplicación y datos).
- Escalabilidad en todas las capas.
- Sistema de caché en todas las capas.
- Mayor facilidad de implantar sistemas de deploy más avanzados (blue-green o canary)
Desventajas:
- Aumentamos la complejidad en todas las capas y para todos los equipos (desarrollo, infraestructura, etc).
- Necesidad de empezar a aprender nuevas tecnologías y sistemas (caché en la capa de datos, Kubernetes, etc) en todos los equipos.
- Alto coste en la infraestructura.
Espero que este post te haya sido de utilidad, si tienes cualquier consulta o quieres darme feedback puedes enviarme un mensaje a través Telegram o sino siempre puedes mandarme un Tweet.
Hasta la próxima!
By Helmcode