martes, 19 de mayo de 2020

Capítulo 1: Introducción a los microservicios - Microservices Designing Deployment


El monolito

A la hora de desarrollar una nueva aplicación, independientemente de la arquitectura software que utilice, es hacerlo todo en un mismo paquete. Este paquete incluye todos los módulos que requiere la aplicación y su despliegue se hace como paquete lo que se denomina comúnmente monolito.

Los monolitos son extremadamente comunes dado que inicialmente son relativamente simples de desarrollar, testear y desplegar. Muchas herramientas de desarrollo están orientadas a desarrollar este tipo de aplicaciones monolíticas. A la hora de escalar sólo hay que ejecutar varias instancias detrás de un balanceador de carga.

En fases tempranas de un proyecto esta estrategia funciona bien.

El camino hacia el monolito infernal

Si la vida de la aplicación se empieza a alargar, el equipo de desarrollo irá añadiendo cada vez más código al monolito. Después de una tiempo el pequeño monolito se empezará a convertir en un pequeño monstruo el cual, sin las medidas oportunas, se convertirá en un monolito infernal.

Una vez se ha llegado al tener un monolito infernal los intentos de hacer un desarrollo o despliegue ágiles serán cada vez más difíciles. Otro de los mayores problemas es que la aplicación es demasiado grande para que un sólo desarrollador pueda entenderla completamente. Esto conlleva a que el desarrollo de nuevas funcionalidades sea cada vez más complejo dando lugar a una espiral de dolor dando lugar a una “big ball of mud”.

Otro problema del monolito infernal es que es un obstáculo al despliegue continuo. El estado del arte es que las software como servicio (SaaS) despliegue los cambios a producción varias veces al día y esto, es extremadamente difícil con un monolito complejo desde que hay que desplegar la aplicación entera para actualizar cualquier parte de ella.

Como el impacto de los cambios no es tarea fácil ya que no se llega a entender bien hay mucha probabilidad de tener que hacer las pruebas manualmente con lo será prácticamente imposible hacer el despliegue continuo.

La escalabilidad también puede verse comprometida en aquellos casos en los que diferentes módulos requieren de recursos específicos. Por ejemplo, un módulo puede requerir un uso intensivo de procesamiento mientras que otro requiere una gran cantidad de memoria.

Otro problema inherente a la naturaleza del monolito es la fiabilidad. Como todos los módulos se ejecutan en el mismo proceso, en el momento que haya un bug puede romper el proceso.

Otro aspecto importante es el acoplamiento que hay en las tecnología en uso dado que reescribir la aplicación puede llegar a ser extremadamente costoso.

¿Cómo abordar el problema?

El patrón de arquitectura de microservicios define una arquitectura de servicios donde estos servicios  colaboran entre sí y no están acoplados entre ellos.

Un servicio implementa un conjunto de funcionalidades que tienen relación como puede ser la administración de pedidos, la administración de clientes, etc. Cada servicio es una aplicación con su propia arquitectura hexagonal con su lógica de negocio con los adaptadores necesarios.

Como estos servicios contienen partes de la aplicación se denominan microservicios.

Cada servicio de backend expone una API REST con la que otros servicios se pueden comunicar. La comunicación entre los servicios también puede ser asíncrona como la comunicación basada en mensajes.

No obstante, las distintas aplicaciones que utilizan estos servicios de backend no deberían hacerlo directamente  si no a través de un intermediario conocido como API Gateway.

La Api Gateway se responsabiliza de tareas como el balanceo de carga, caché, control de acceso, métricas de API y monitorización.

Otro aspecto importante es que la arquitectura de microservicios impacta en la base de datos de manera que ya no hay una única base de datos si no que cada servicio tiene su propia base de datos. Esto es esencial aunque a veces resulte en duplicación de algunos datos para evitar el acoplamiento.

Esta estrategia tiene otro beneficio añadido y es que cada servicio puede utilizar el tipo de base de datos que más se ajuste a sus necesidades. 

La arquitectura de microservicios es similar a SOA en cuanto a que ambas consisten en un conjunto de servicios aunque SOA tienen los Web Service Especifications (WS-*) y los Enterprise Service Bus (ESB) mientras que los microservicios usan protocolos más simples como REST. Los microservicios también rechazan el concepto de esquemas canónicos para la base de datos.

Los beneficios de los microservicios

El primer beneficio es que ataca al problema de la complejidad descomponiendo una aplicación monolítica monstruosa en servicios siendo cada uno de estos mucho más manejables.

El segundo beneficio es que desarrollo independiente de cada servicio con lo que son mucho más rápidos de desarrollar. Un equipo de desarrollo puede enfocarse en el desarrollo de este servicio utilizando las tecnologías que se adecuen mejor a su propósito. 

El tercer beneficio es que el servicio se puede desplegar independientemente por lo que no se necesita una comunicación con otros equipos. Tan pronto como el desarrollo está probado se puede desplegar con lo que el continuous deployment es posible.

El cuarto beneficio es la escalabilidad de los distintos servicios de manera independiente. Se pueden utilizar servidores que se adecuen a sus los requisitos de recursos como pueden ser capacidad de procesamiento, almacenamiento o memoria.

Las desventajas de los microservicios

No es oro todo lo que reluce y, como bien apuntó Fred Brooks en su libro “The Mythical Man-Month”, no hay balas de plata.

La primera desventaja es el propio término “microservicio” el cual parece hacer excesivo énfasis en el tamaño de un servicio pero sin definir cuán de pequeño debe ser. Al ser interpretable cada desarrollador puede llevarlo al extremo que quiera creando servicios de 10 líneas, 100 líneas, 1000 líneas, etc. Así que el nombre es más una directriz a seguir que una reglas fijas. El propósito de los microservicios es descomponer la aplicación para facilitar el desarrollo, el despliegue y la escalabilidad.

Otro reto al que se enfrentan los microservicios es tener el esquema de la base de datos separado. Cuando se tiene una sóla base de datos las transacciones son algo trivial sin embargo, cuando se tienen microservicios estos tienen su propia base de datos. Las transacciones distribuidas no son una opción y no sólo por el teorema del CAP sino porque cada servicio puede tener bases de datos que no soporten una transacción. Para abordar este problema habrá que usar una estrategia de consistencia eventual lo cual será un reto para los programadores.

La pruebas en los microservicios también añaden un grado de complejidad dado que probar un servicio puede requerir lanzar ese servicio a la vez que cualquier servicio del cual dependa o al menos, lanzar “stubs” para estos servicios. No es que sea ciencia de cohetes pero hay que tener en cuenta esta complejidad extra.

Otro reto al que deben enfrentarse los desarrolladores es es implementar cambios que impacten en múltiples microservicios. Por ejemplo, imaginemos que hay que implementar una historia que requiera cambios en los servicios A, B y C donde A depende de B y B de C. En este caso hay que coordinar los cambios de cada servicio de manera los cambios se deberán abordar de manera inversa. Primero habría que actualizar el servicio C, después el servicio B y finalmente el servicio A.

El despliegue de los microservicios también es mucho más complejo. Una aplicación basada en  microservicios contiene un número importante de servicios que los cuales deben ser configurados, desplegados, escalados y monitorizados. 

Además, se debe implementar un “service discovery mechanism” que habilita un servicio para descubrir las localizaciones (host y puerto) de cualquier otro servicio que necesite comunicarse con él. Esto lleva a que se requiera de un gran control en el despliegue así como en la automatización de los despliegues.

Una estrategia para la automatización es usar “Platform as a service” (PaaS). Un típico punto de comienzo es usar una solución de clustering como Kubernetes en conjunción con una tecnología de contenedores como Dockers.

Resumen

La construcción de aplicaciones complejas es inherentemente difícil. El patrón de arquitectura de monolito tiene sentidos para aquellas aplicaciones simples. A medida que el monolito comienza a crecer se volverá más complejo hasta que casi sea impracticable acometer los cambios. 

La arquitectura de microservicios, a pesar de sus desventajas y los retos de implementación, es una mejor elección para aquellas aplicaciones que van a a ser complejas.

No hay comentarios:

Publicar un comentario