Arquitectura de microservicios


Introducción

Imagina que lanzas una aplicación que conecta personas que van a preparar la cena con personas que no tienen ganas de cocinar hoy, pero que pueden pagar una cantidad justa por una comida casera recién preparada. Al principio, utilizas tu computadora personal y un DNS dinámico para direccionar tráfico hacia ella para resolver las solicitudes que se le hacen a la aplicación. Todo funciona bien y se convierte en un éxito en unos cuantos meses. De repente, el tiempo de respuesta empieza a disminuir y los usuarios comienzan a quejarse del desempeño de la aplicación. Es hora de crecer. ¿Qué hacer para resolver esto? Aun si compras una computadora con mejor procesador y más memoria, esta situación se puede repetir cuando el negocio crezca más. Felicidades, te acabas de topar con el problema feliz de la escalabilidad. Aquí es donde conocer acerca de la nube y la arquitectura de microservicios te puede ayudar.

Los microservicios, también conocidos como arquitectura de microservicios, son un estilo arquitectónico que estructura una aplicación como una colección de servicios, tales como:

  1.  Altamente sostenibles y comprobables.
  2.  Débilmente acoplados.
  3.  Desplegable independientemente.
  4.  Organizado en torno a las capacidades comerciales.
  5.  Propiedad de un pequeño equipo.

La arquitectura de microservicio permite la entrega rápida, frecuente y confiable de aplicaciones grandes y complejas.


Explicación

Microservicios  

Una arquitectura de microservicios es una colección de servicios autónomos, pequeños y de propósito definido. Cada servicio es independiente y debe implementar una funcionalidad de negocio específica dentro de un contexto delimitado. Es decir, los límites, actividades y datos afectados por el servicio son fácilmente identificables y de alcance discernible.



Cada servicio en una arquitectura de microservicios se puede desarrollar, implementar, operar y escalar de forma independiente de los demás servicios utilizados en la organización.
Los servicios no necesitan compartir ninguno de sus códigos, frameworks, lenguajes, implementaciones o repositorios de datos con otros servicios.

Los servicios son los responsables de conservar sus propios datos. Esto difiere del modelo tradicional, donde una capa de datos independiente controla la persistencia de los mismos.
Cualquier comunicación entre componentes individuales ocurre a través de application program interfaz (API) bien definidas.

Los beneficios de los microservicios son los siguientes:

  1. Agilidad. Dado que los microservicios se implementan de forma independiente, resulta más fácil administrar las correcciones de errores y las versiones de características. 
  2. Equipos pequeños y centrados. Un microservicio debe ser lo suficientemente pequeño como para que un solo equipo de características lo pueda compilar, probar e implementar.
  3. Base de código pequeña. Al no compartir el código ni los almacenes de datos, la arquitectura de microservicios minimiza las dependencias y resulta más fácil agregar nuevas características.
  4.  Mezcla de tecnologías. Los equipos pueden elegir la tecnología que mejor se adapte al servicio de una combinación de pilas de tecnología, según corresponda.
  5.  Aislamiento de errores. Si un microservicio individual no está disponible, no interrumpe toda la aplicación.
  6.  Escalabilidad. Los servicios se pueden escalar de forma independiente, lo que permite escalar horizontalmente los subsistemas que requieren más recursos, sin tener que escalar horizontalmente toda la aplicación. 
  7. Aislamiento de los datos. Al verse afectado solo un microservicio es mucho más fácil realizar actualizaciones del esquema. En una aplicación monolítica, las actualizaciones del esquema pueden ser muy complicadas, ya que las distintas partes de la aplicación pueden tocar los mismos datos, por lo que realizar modificaciones en el esquema resulta peligroso.

Las complicaciones de los microservicios son los siguientes:

  1.  Complejidad. Una aplicación de microservicios tiene más componentes interactuando que la aplicación monolítica que tiene el mismo propósito. Cada servicio es más sencillo, pero el sistema como un todo es más complejo.
  1. Desarrollo y pruebas. La escritura de un servicio pequeño que utilice otros servicios interdependientes requiere un enfoque que no sea escribir una aplicación tradicional monolítica o en capas. Las herramientas existentes no siempre están diseñadas para trabajar con dependencias de servicios. La definición y adecuación en los límites del servicio pueden resultar difíciles. También es retador el probar las dependencias de los servicios, especialmente cuando la aplicación cambia con frecuencia por mejoras incrementales.
  2. Falta de estandarización. La descentralización puede permitir el uso de tantos lenguajes y frameworks diferentes que la aplicación puede ser difícil de mantener. 
  3. Congestión y latencia de red. El uso de muchos servicios pequeños y detallados puede dar lugar a más comunicación entre ellos. Si muchos servicios se hacen llamadas entre sí, la latencia adicional puede constituir un problema por la saturación del canal de comunicación entre los mismos. 
  4.  Integridad de datos. Cada microservicio es responsable de almacenar y garantizar la integridad de sus propios datos. Como consecuencia, la coherencia de los datos puede suponer un problema. 
  5. Administración. El registro consolidado entre servicios puede resultar un desafío. Normalmente, el registro suele englobar varias llamadas de servicio para una sola operación de usuario.
  6. Control de versiones. Las actualizaciones de un servicio no deben interrumpir servicios que dependen de él. Es posible que varios servicios se actualicen en distintos tiempos; por ende, sin un meticuloso diseño, podrían surgir problemas con la compatibilidad con diferentes versiones.

Los microservicios son una forma de trabajar con aplicaciones grandes de manera modular y con esto atender grandes demandas que son exigentes en cuestiones de cambios, capacidades y disponibilidad. Se puede decir que los microservicios son un conjunto de tareas de la arquitectura orientada a servicios.

La evolución de las arquitecturas de servicios 

La característica principal de las aplicaciones monolíticas es que hacen uso de una base de código única para sus servicios y funcionalidades. Es decir, la aplicación tiene control absoluto sobre todas las tareas necesarias para realizar una determinada función.

Este tipo de aplicaciones sobresalen por combinar la interfaz del usuario y la capa de acceso a los datos en el mismo programa y alojarlos en el mismo lugar.
Su principal cometido de la aplicación distribuida es tener la aplicación disgregada en múltiples nodos, dando la posibilidad de que en cada nodo exista un componente de la aplicación o un mismo componente replicado en varios nodos.

Esta arquitectura es bastante más compleja y difícil de gestionar y administrar que una aplicación monolítica.

Uno de los grandes conceptos que introduce este tipo de aplicaciones es el escalado horizontal. Es decir, cuando por cuestiones de demanda los recursos actuales ya no son suficientes, en vez de aumentar los recursos de un nodo (escalado vertical), lo que se hace es incrementar el número de nodos dedicados al procesamiento de ese componente demandado.

Otro gran concepto que surge, aunque todavía no de forma automática y madura, es la llamada elasticidad. Es decir, al igual que se aumentan el número de nodos dedicados para soportar un aumento de demanda, se pueden reducir si la demanda disminuye y dejarlos disponibles para otros procesos.
La seguridad y la robustez mejoran notablemente, ya que es más probable que los problemas de seguridad de un nodo sean cercados en ese nodo.

La arquitectura SOA fue la que insistió en la utilización de aplicaciones distribuidas y orientadas a servicios.
La idea fue hacer que esos servicios fueran independientes y las interacciones entre ellos se hicieran bajo ciertos protocolos estandarizados de comunicación, como WSDL y SOAP.

Esta independencia hacía posible que servicios desarrollados por diferentes tecnologías pudiesen comunicarse entre sí, sin ningún problema.

La arquitectura SOA está más enfocada a la arquitectura de la aplicación. Cloud Native se enfoca más en la arquitectura del sistema que alberga, distribuye y ofrece las aplicaciones.
Este punto de vista ofrece básicamente el uso de recursos en la nube bajo demanda automatizada. Es decir, va a existir una elasticidad gestionada de forma automática por el sistema en la infraestructura dedicada a cada una de las aplicaciones que distribuyen.

No existe una definición formal, pero básicamente es una evolución de SOA (service oriented architecture) y está orientada al trabajo con servicios muy pequeños e independientes.
El objetivo es aislar los distintos componentes de una aplicación con el fin de que cada uno sea una aplicación por sí misma.
Otra de las diferencias con SOA es la comunicación entre los servicios; ya no sería con servicios web WSDL o SOAP, sino vía HTTP con API-REST.

Cómo modelar servicios

Puede resultar útil establecer algunos estándares para todo el proyecto sin restringir excesivamente la flexibilidad de los equipos. Esto se aplica especialmente a las funcionalidades transversales, como el registro.
El diseño de las API requiere atención al detalle. Debemos evitar que las API se comuniquen demasiado, pensando en formatos de serialización y buscando lugares para utilizar patrones de comunicación asincrónica como la nivelación de carga basada en buffers.


Una práctica usual en el diseño de microservicios es utilizar la metodología DDD (domain driven design), la cual consta de cuatro pasos: 

  1. Analizar el dominio de la empresa para conocer los requisitos funcionales de la aplicación. En este paso obtenemos una descripción informal del dominio, que puede detallarse en un conjunto más formal de modelos de dominio.
  2. Definir los contextos delimitados del dominio. Cada contexto delimitado contiene su propio modelo de dominio que representa un subdominio específico de la aplicación mayor.
  3. Para cada contexto delimitado, se aplican los modelos tácticos de diseño basado en dominios para definir las entidades, los agregados y los servicios de dominio. Los agregados típicamente son colecciones de objetos de valor o entidades que sirven a una entidad raíz.
  4. Utilizar los resultados del paso anterior para identificar los microservicios de la aplicación.

Entidades. Una entidad es un objeto con una identidad única que persiste en el tiempo. 
Una entidad tiene un identificador único en el sistema, que se puede usar para buscar la entidad o para recuperarla. Una identidad puede abarcar varios contextos delimitados y puede durar más que la aplicación. Los atributos de una entidad pueden cambiar con el tiempo. Una entidad puede contener referencias a otras entidades.
Objetos de valor. Un objeto de valor no tiene identidad. Se define únicamente mediante los valores de sus atributos. Los objetos de valor también son inmutables. Para actualizar un objeto de valor, siempre hay que crear una nueva instancia que reemplace a la anterior.
Agregados. Un agregado define un límite de coherencia alrededor de una o varias entidades. Una entidad exacta en un agregado es la raíz. La búsqueda se realiza con el identificador de la entidad raíz. Cualquier otra entidad en el agregado es secundaria de la raíz y hace referencia a ella siguiendo apuntadores desde esta. Si la aplicación modifica varios objetos relacionados, ¿cómo podemos garantizar la coherencia? ¿Cómo se realiza el seguimiento de los elementos invariables y cómo se aplican?

Partiendo el monolito

Al tratar de transformar una arquitectura monolítica a una arquitectura de microservicios es importante revisar detenidamente el entorno que se tiene adelante ya que, al momento de partir el monolito en partes pequeñas, que son los microservicios, se tendrá el desafío de establecer los límites de cada uno sin olvidar las responsabilidades que esto conlleva.

Aunque el proceso para determinar entidades es muy similar al que se seguiría para una aplicación monolítica, hay que buscar formas de que los datos de cada servicio sean auto-contenidos y auto-gestionados por cada servicio. Si otro servicio requiere hacer consultar y/o modificar atributos de una entidad, esto debe de hacerse a través de métodos provistos por el API que gestiona el dominio del servicio y no acceder a los datos directamente. La intención es mantener los servicios lo menos interrelacionados posible para que de esa forma se vuelvan fácilmente escalables y que no requieran validaciones por otros equipos para ser modificados, siempre y cuando mantengan una interfaz consistente a través del API. 

La arquitectura de monolito es considerada como el método tradicional de construir las cosas. Una aplicación monolítica se construye como una programa único e indivisible. Típicamente una aplicación de este tipo está compuesta por las siguientes capas:

  • Interfaz de usuario.
  • Aplicación del lado del servidor que contiene la lógica de negocios.
  • Una serie de rutinas que administran la interfaz de la lógica de negocios con los repositorios de datos.
  • Una base de datos que almacena los catálogos y transacciones operadas en la capa anterior.

Las fortalezas de la arquitectura monolítica son las siguientes:

  1. Menos puntos de quiebre por simplicidad. Tareas administrativas como: bitácoras, manejadores, creación de cachés y monitoreo de desempeño se simplifican puesto que todas las transacciones están bajo el dominio del dominio.
  2. Facilidad para depurar código (debugging) y hacer pruebas, en contraste con la arquitectura de microservicios, ya que, la ejecución de principio a fin ocurre en un mismo ambiente controlado.
  3. Desplegado (deployment) sencillo. La cantidad de despliegues en las aplicaciones monolíticas son mínimas. Usualmente solo se despliega un solo archivo o directorio.
  4. Facilidad para desarrollar. La mayoría de los equipos de desarrollo tiene el conocimiento adecuado y la capacidad para desarrollar una aplicación monolítica.

Las debilidades de la arquitectura monolítica son las siguientes:

  1. Entendimiento. Cuando una aplicación monolítica escala, se vuelve muy complicado para entenderla.
  2. Hacer cambios. Como los componentes de las aplicaciones monolíticas están interrelacionados de manera muy próxima (tight coupling), los cambios afectan la estabilidad de todo el sistema con facilidad y, por lo tanto, debe ser coordinado con extrema cautela.
  3. Susceptibilidad a fallas. Las aplicaciones monolíticas son menos robustas que las aplicaciones distribuidas. Cuando el nodo que las contiene falla usualmente tira todo el sistema y tardan más en recuperar su estado óptimo.

La metodología de aplicación (app) con 12 factores 

En la era moderna, el software se entrega comúnmente como un servicio: llamadas aplicaciones web o software como servicio. La aplicación de 12 factores es una metodología para crear aplicaciones de software como servicio que:

    1. Minimizan la divergencia entre el desarrollo y la producción, permitiendo la implementación continua para una máxima agilidad.
    2. Utilizan formatos declarativos para la automatización de la configuración, para minimizar el tiempo y el costo de los nuevos desarrolladores que se unen al proyecto.
  1. Se adecuan para su implementación en plataformas modernas en la nube, lo que evita la necesidad de administración de servidores y sistemas.
  2. Escalan sin cambios significativos en las herramientas, la arquitectura o las prácticas de desarrollo.
  3. Tienen un contrato claro con el sistema operativo subyacente, ofreciendo máxima portabilidad entre entornos de ejecución.

Hoy en día el software se está distribuyendo como software as a service (SaaS) o, como se le conoce comúnmente, aplicaciones web o web “apps”. La metodología de los 12 factores se puede aplicar a aplicaciones escritas en cualquier lenguaje de programación y que utilizan cualquier combinación de servicios de respaldo (base de datos, Queue, caché de memoria, etc.).
A continuación, se presentan las características de cada uno de ellos.

  1. Repositorio central (Codebase)

    Se usa para dar seguimiento de una base de código en el control de revisiones, en donde parten una gran cantidad de despliegues.
    Una aplicación de 12 factores siempre se rastrea en un sistema de control de versiones como Git, Mercurial o Subversion. Una copia de la base de datos de seguimiento de revisiones se conoce como repositorio de código, a menudo abreviado como repositorio de código o simplemente repositorio.

    Una base de código es cualquier repositorio único (por ejemplo, Subversion) o cualquier conjunto de repositorios que comparten una confirmación de root (por ejemplo, Git).
    Siempre hay una correlación uno a uno entre el código base y la aplicación como se explican en los siguientes puntos:
    • Si hay varias bases de código, no es una aplicación, es un sistema distribuido. Cada componente de un sistema distribuido es una aplicación y cada uno puede cumplir individualmente con doce factores.
    •  Varias aplicaciones que comparten el mismo código es una violación de los 12 factores. La solución aquí es factorizar el código compartido en bibliotecas que se pueden incluir a través del administrador de dependencias.
    • Solo hay una base de código por aplicación, pero habrá muchas implementaciones de la aplicación. Una implementación es una instancia en ejecución de la aplicación. Este suele ser un sitio de producción y uno o más sitios de preparación. Además, cada desarrollador tiene una copia de la aplicación ejecutándose en su entorno de desarrollo local, cada uno de los cuales también califica como implementación.

    El código base es el mismo en todas las implementaciones, aunque pueden estar activas diferentes versiones en cada implementación. Por ejemplo, un desarrollador tiene algunas commits que aún no se han implementado en el servidor de staging. La versión liberada tiene algunos commits que aún no se han implementado en producción, pero todos comparten la misma base de código, lo que los hace identificables como diferentes implementaciones de la misma aplicación.

  2. Dependencias

    Una aplicación de 12 factores nunca se basa en la existencia implícita de paquetes en todo el sistema. Declara todas las dependencias, completa y exactamente, a través de un manifiesto de declaración de dependencia. Además, utiliza una herramienta de aislamiento de dependencias durante la ejecución para garantizar que no se "filtren" dependencias implícitas del sistema circundante. La especificación de dependencia completa y explícita se aplica de manera uniforme, tanto a la producción como al desarrollo.

    Un beneficio de la declaración de dependencia explícita es que simplifica la configuración para los desarrolladores nuevos en la aplicación. El nuevo desarrollador puede verificar el código base de la aplicación en su máquina de desarrollo, requiriendo solo el tiempo de ejecución del lenguaje y el administrador de dependencias instalado como requisitos previos. Podrán configurar todo lo necesario para ejecutar el código de la aplicación con un comando de compilación determinista.

    Las aplicaciones de 12 factores tampoco dependen de la existencia implícita de ninguna herramienta del sistema. Si bien estas herramientas pueden existir en muchos o incluso en la mayoría de los sistemas, no hay garantía de que existan en todos los sistemas donde la aplicación pueda ejecutarse en el futuro, o si la versión encontrada en un sistema futuro será compatible con la aplicación. Si la aplicación necesita destinarse a una herramienta del sistema, esa herramienta debe venderse en la aplicación.

  3. Configuración

    La configuración de una aplicación es todo lo que puede variar entre implementaciones (staging, producción, entornos de desarrollo, etcétera). Esto incluye:
    • Controladores de recursos para la base de datos, Memcached y otros servicios de respaldo.
    • Credenciales para servicios externos como Amazon S3 o Twitter.
    • Valores por implementación, como el nombre de host canónico para la implementación.

    Las aplicaciones a veces almacenan la configuración como constantes en el código. Esta es una violación de los 12 factores que requiere una separación estricta de la configuración del código. La configuración varía sustancialmente entre las implementaciones; el código, no.

    La aplicación de 12 factores almacena la configuración en variables de entorno. Las variables de entorno son fáciles de cambiar entre implementaciones sin cambiar ningún código; a diferencia de los archivos de configuración, hay pocas posibilidades de que se registren accidentalmente en el repositorio de código. Y a diferencia de los archivos de configuración personalizados u otros mecanismos de configuración como las propiedades del sistema Java, son un estándar independiente del lenguaje y del sistema operativo.

  4. Servicios de soporte 

    Un servicio de respaldo es cualquier servicio que la aplicación consume a través de la red como parte de su funcionamiento normal. Los ejemplos incluyen almacenes de datos, sistemas de mensajería/colas, servicios SMTP para correo electrónico saliente y sistemas de almacenamiento en caché.

    Los servicios de respaldo, como la base de datos, son administrados tradicionalmente por los mismos administradores de sistemas que implementan el tiempo de ejecución de la aplicación. Además de estos servicios administrados localmente, la aplicación también puede tener servicios proporcionados y administrados por terceros. Los ejemplos incluyen servicios SMTP, servicios de recopilación de métricas, servicios de activos binarios e incluso servicios para consumidores accesibles a API.

    El código para una aplicación de 12 factores no distingue entre servicios locales y de terceros. Para la aplicación, ambos son recursos adjuntos, a los que se accede a través de una URL u otro localizador/credenciales almacenados en la configuración. Una implementación de la aplicación de 12 factores debería poder intercambiar una base de datos MySQL local por una administrada por un tercero (como Amazon RDS), sin ningún cambio en el código de la aplicación. Del mismo modo, un servidor SMTP local podría intercambiarse con un servicio SMTP de terceros (como Postmark), sin cambios de código. En ambos casos, solo es necesario cambiar el identificador de recursos en la configuración.

    Cada servicio de respaldo distinto es un recurso. Los recursos se pueden adjuntar y separar de las implementaciones a voluntad. Por ejemplo, si la base de datos de la aplicación se está comportando mal debido a un problema de hardware, el administrador de la aplicación podría activar un nuevo servidor de base de datos restaurado a partir de una copia de seguridad reciente. La base de datos de producción actual podría separarse y adjuntarse la nueva base de datos, todo sin ningún cambio de código.

  5. Construir, desplegar, ejecutar 

    Una base de código se transforma en una implementación (sin desarrollo) a través de tres etapas:
    1. La etapa de compilación es una transformación que convierte un repositorio de código en un paquete ejecutable conocido como compilación. Usando una versión del código en una confirmación especificada por el proceso de implementación, la etapa de compilación busca las dependencias de los proveedores y compila binarios y activos.
    2. La etapa de lanzamiento toma la compilación producida por la etapa de compilación y la combina con la configuración actual de la implementación. La versión resultante contiene tanto la compilación como la configuración y está lista para su ejecución inmediata en el entorno de ejecución.
    3. La etapa de ejecución (también conocida como "tiempo de ejecución") ejecuta la aplicación en el entorno de ejecución, mediante el lanzamiento de un conjunto de procesos de la aplicación en una versión seleccionada.

    La aplicación de 12 factores utiliza una separación estricta entre las etapas de compilación, lanzamiento y ejecución. Por ejemplo, es imposible realizar cambios en el código en tiempo de ejecución, ya que no hay forma de propagar esos cambios a la etapa de compilación.

    Cada lanzamiento debe tener siempre un ID de lanzamiento único, como una marca de tiempo del lanzamiento o un número creciente. Las entregas son un libro mayor de solo anexo y una entrega no se puede modificar una vez que se crea. Cualquier cambio debe crear una nueva versión.

    Los desarrolladores de la aplicación inician las compilaciones cada vez que se implementa un nuevo código. La ejecución en tiempo de ejecución, por el contrario, puede suceder automáticamente en casos como el reinicio del servidor o el reinicio de un proceso bloqueado por el administrador de procesos.

  6. Procesos 

  7. La aplicación se ejecuta en el entorno de ejecución como uno o más procesos.
    Los procesos de 12 factores son huérfanos y no comparten nada. Cualquier dato que deba persistir debe almacenarse en un servicio 
    de respaldo con estado, generalmente una base de datos.

    El espacio de memoria o el sistema de archivos del proceso se puede utilizar como un breve caché de transacción única. Por ejemplo, descargar un archivo grande, operar en él y almacenar los resultados de la operación en la base de datos. La aplicación de 12 factores nunca asume que cualquier cosa almacenada en la memoria caché o en el disco estará disponible en una solicitud o trabajo futuro; con muchos procesos de cada tipo en ejecución, es muy probable que una solicitud futura sea atendida por un proceso diferente. Incluso cuando se ejecuta solo un proceso, un reinicio generalmente eliminará todo el estado local.

  8. Asignación de puertos 

  9. En este factor se realiza la exportación de servicios a través de la vinculación de puertos en la red.

    Las aplicaciones web a veces se ejecutan dentro de un contenedor de servidor web. Por ejemplo, las aplicaciones Perl pueden ejecutarse como un módulo dentro de Apache HTTPD o las aplicaciones Java pueden ejecutarse dentro de Tomcat.

    La aplicación de 12 factores es completamente autónoma y no depende de la inyección en tiempo de ejecución de un servidor web en el entorno de ejecución para crear un servicio orientado a la web. La aplicación web exporta HTTP como servicio al vincularse a un puerto y escuchar las solicitudes que ingresan en ese puerto.

    Ten en cuenta también que el enfoque de enlace de puertos significa que una aplicación puede convertirse en el servicio de respaldo para otra aplicación, proporcionando la URL a la aplicación de respaldo como un identificador de recursos en la configuración de la aplicación consumidora.

  10. Concurrencia 

  11. Cualquier programa de computadora, una vez ejecutado, está representado por uno o más procesos. Las aplicaciones web han adoptado una variedad de formas de ejecución de procesos. Por ejemplo, los procesos Perl se ejecutan como procesos secundarios de Apache iniciados bajo demanda según sea necesario por volumen de solicitudes. Los procesos de Java adoptan el enfoque opuesto, con la JVM que proporciona un súper proceso masivo que reserva un gran bloque de recursos del sistema (CPU y memoria RAM) al inicio, con simultaneidad administrada internamente a través de subprocesos. En ambos casos, los procesos en ejecución solo son mínimamente visibles para los desarrolladores de la aplicación.

    En la aplicación de 12 factores, los procesos son ciudadanos de primera clase. Los procesos en la aplicación de 12 factores toman fuertes señales del modelo de proceso Unix para ejecutar demonios de servicio. Con este modelo, el desarrollador puede diseñar su aplicación para manejar diversas cargas de trabajo asignando cada tipo de trabajo a un tipo de proceso. Por ejemplo, las solicitudes HTTP pueden ser manejadas por un proceso web y las tareas en segundo plano de larga ejecución manejadas por un proceso de trabajo.

    Esto no excluye que los procesos individuales manejen su propia multiplexación interna, a través de subprocesos dentro de la máquina virtual en tiempo de ejecución, o el modelo asíncrono / evento que se encuentra en herramientas como Node.js, pero una máquina virtual individual solo puede crecer hasta cierto punto, por lo que la aplicación también debe poder abarcar varios procesos que se ejecutan en varias máquinas físicas.

  12. Prescindibilidad (disposability) 

  13. Los procesos de la aplicación de 12 factores son prescindibles, lo que significa que se pueden iniciar o detener en cualquier momento. Esto facilita el escalado elástico rápido, la implementación rápida de cambios de código o configuración y la solidez de las implementaciones de producción.

    Los procesos deben esforzarse por minimizar el tiempo de inicio. Idealmente, un proceso toma unos segundos desde el momento en que se ejecuta el comando de inicio hasta que el proceso está activo y listo para recibir solicitudes o trabajos. El tiempo de inicio corto proporciona más agilidad para el proceso de lanzamiento y la ampliación; y ayuda a la robustez, porque el administrador de procesos puede mover más fácilmente los procesos a nuevas máquinas físicas cuando esté justificado.

    Los procesos se apagan correctamente cuando reciben una señal SIGTERM (señal de terminación) del administrador de procesos. Para un proceso web, el cierre ordenado se logra al dejar de escuchar en el puerto de servicio, permitiendo que finalice cualquier solicitud actual y luego saliendo. Implícito en este modelo es que las solicitudes HTTP son breves (no más de unos pocos segundos) o, en el caso de una consulta larga, el cliente debe intentar reconectarse sin problemas cuando se pierde la conexión.

    Los procesos también deben ser robustos contra la muerte súbita, en el caso de una falla en el hardware subyacente. Si bien esto es una ocurrencia mucho menos común que un cierre ordenado con SIGTERM, aún puede suceder. Un enfoque recomendado es el uso de un backend de cola robusto, como Beanstalkd, que devuelve trabajos a la cola cuando los clientes se desconectan o agotan el tiempo de espera. De cualquier manera, una aplicación de 12 factores está diseñada para manejar terminaciones inesperadas y no agradables. El diseño solo para colisiones lleva este concepto a su conclusión lógica.

  14. Paridad (Dev/Prod)

  15. Tanto el desarrollo, la puesta en escena y la producción tienen que ser lo más similar posible; esto es para disminuir los errores en la        codificación que afecten los tiempos de despliegue de las aplicaciones.

    Históricamente, ha habido brechas sustanciales entre el desarrollo (un desarrollador que realiza ediciones en vivo en una implementación local de la aplicación) y la producción (una implementación en ejecución de la aplicación a la que acceden los usuarios finales). Estas brechas se manifiestan en tres áreas:

    1. El intervalo de tiempo: un desarrollador puede trabajar en un código que tarda días, semanas o incluso meses en entrar en producción.
    2. La brecha de personal: los desarrolladores escriben el código, los ingenieros de operaciones lo implementan.
    3. La brecha de herramientas: los desarrolladores pueden estar usando una pila como Nginx, SQLite y OS X, mientras que la implementación de producción usa Apache, MySQL y Linux.

    La aplicación de 12 factores está diseñada para una implementación continua al mantener pequeña la brecha entre el desarrollo y la producción. Observando las tres brechas descritas anteriormente:

    1. Reduce la brecha de tiempo: un desarrollador puede escribir código e implementarlo horas o incluso minutos después.
    2. Reduce la brecha de personal: los desarrolladores que escribieron código están estrechamente involucrados en su implementación y en observar su comportamiento en producción.
    3. Reduce la brecha de herramientas: mantiene el desarrollo y la producción lo más similares posible.

    Los servicios de respaldo, como la base de datos de la aplicación, el sistema de cola o la caché, es un área donde la paridad dev/prod es importante. Muchos idiomas ofrecen bibliotecas que simplifican el acceso al servicio de respaldo, incluidos adaptadores para diferentes tipos de servicios.

  16. Bitácoras (logs

  17. Las bitácoras son un historial de eventos para llevar un control más estricto, así como una supervisión en los comportamientos de las aplicaciones.

    Las bitácoras proporcionan visibilidad del comportamiento de una aplicación en ejecución. En entornos basados ​​en servidor, normalmente se escriben en un archivo en el disco (un "archivo de bitácora” o “logfile”), pero este es solo un formato de salida.

    Las bitácoras son el flujo de eventos agregados ordenados por tiempo recopilados de los flujos de salida de todos los procesos en ejecución y servicios de respaldo. Las bitácoras en su forma sin procesar suelen ser un formato de texto con un evento por línea (aunque en el manejo de algunas excepciones se pueden abarcar varias líneas). Este tipo de archivos no tienen un principio ni un final fijos, sino que fluyen continuamente mientras la aplicación esté en funcionamiento.

    Una aplicación de 12 factores nunca se preocupa por el enrutamiento o el almacenamiento de su flujo de salida. No debe intentar escribir o administrar archivos de bitácora. En cambio, cada proceso en ejecución escribe su flujo de eventos, sin buffer, en stdout. Durante el desarrollo local, el desarrollador verá esta transmisión en primer plano de su terminal para observar el comportamiento de la aplicación.

    En las implementaciones de producción o staging, el entorno de ejecución captura la secuencia de cada proceso, la recopila junto con todas las demás secuencias de la aplicación y la enruta a uno o más destinos finales para su visualización y archivo a largo plazo. Estos destinos de archivo no son visibles ni configurables por la aplicación, sino que están completamente administrados por el entorno de ejecución.

  18. Procesos de administración 

  19. Ejecutar tareas de administración/gestión como procesos únicos para automatizar las tareas que son repetitivas o manuales que impactan directamente a los tiempos de ejecución de las aplicaciones.

    La formación del proceso es el conjunto de procesos que se utilizan para realizar el negocio habitual de la aplicación (como el manejo de solicitudes web) mientras se ejecuta. Por separado, los desarrolladores a menudo desearán realizar tareas administrativas o de mantenimiento únicas para la aplicación, como:

    1.  Ejecutar migraciones de bases de datos.
    2.  Ejecutar una consola para ejecutar código arbitrario o inspeccionar los modelos de la aplicación con la base de datos en vivo. La mayoría de los lenguajes proporcionan un REPL ejecutando el intérprete sin ningún argumento o, en algunos casos, tienen un comando separado.
    3.  Ejecutar scripts únicos comprometidos en el repositorio de la aplicación.

    Los procesos de administración únicos deben ejecutarse en un entorno idéntico al de los procesos regulares de larga ejecución de la aplicación. Se ejecutan en una versión, utilizando la misma base de código y configuración que cualquier proceso que se ejecute en esa versión. El código de administración debe empaquetarse junto con el código de la aplicación para evitar problemas de sincronización.


Cierre

Es importante que sepas distinguir cuándo una arquitectura de microservicios es aplicable para resolver los problemas de escalamiento que pudiera tener tu aplicación. Si tu equipo es pequeño o tu aplicación aún no alcanza un volumen de usuarios o transaccional de gran escala quizás aún haya tiempo de planear y refinar el diseño antes de migrar a una estructura que implica mayor disciplina y procesos de administración de clase mundial. Los puntos relevantes para considerar son los siguientes:

¿Cuándo será un buen momento para migrar a esta arquitectura?
¿Qué reglas de dedo / lineamientos se pueden seguir para identificar las fronteras de microservicios utilizando técnicas como DDD (domain driven design)?
¿Cómo se puede diseñar una buena manera para que los servicios colaboren entre sí, sin incrementar el acoplamiento entre ellos, usando colaboración basada en eventos?


Checkpoint

Asegúrate de:

  • Detectar la diferencia entre una aplicación monolítica y los diferentes tipos de arquitecturas distribuidas, en particular la de microservicios para alinear los factores particulares del proyecto a desarrollar.
  • Practicar los 12 factores para el diseño de aplicaciones con la metodología del mismo nombre.
  • Aplicar la metodología DDD para delimitar los alcances de microservicios en arquitecturas distribuidas.

Bibliografía

  • Ilimit. (2020). Arquitecturas monolíticas o arquitectura de microservicios: ventajas e inconvenientes. Recuperado de https://www.ilimit.com/blog/arquitecturas-monoliticas-o-arquitectura-de-microservicios-ventajas-e-inconvenientes/
  • Microsoft. (2021). Design a microservice Domain Model. Recuperado de https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/microservice-domain-model
  • Reselman, Bob. (2021). An illustrated guide to 12 Factor Apps. Recuperado de https://www.redhat.com/architect/12-factor-app

La obra presentada es propiedad de ENSEÑANZA E INVESTIGACIÓN SUPERIOR A.C. (UNIVERSIDAD TECMILENIO), protegida por la Ley Federal de Derecho de Autor; la alteración o deformación de una obra, así como su reproducción, exhibición o ejecución pública sin el consentimiento de su autor y titular de los derechos correspondientes es constitutivo de un delito tipificado en la Ley Federal de Derechos de Autor, así como en las Leyes Internacionales de Derecho de Autor.

El uso de imágenes, fragmentos de videos, fragmentos de eventos culturales, programas y demás material que sea objeto de protección de los derechos de autor, es exclusivamente para fines educativos e informativos, y cualquier uso distinto como el lucro, reproducción, edición o modificación, será perseguido y sancionado por UNIVERSIDAD TECMILENIO.

Queda prohibido copiar, reproducir, distribuir, publicar, transmitir, difundir, o en cualquier modo explotar cualquier parte de esta obra sin la autorización previa por escrito de UNIVERSIDAD TECMILENIO. Sin embargo, usted podrá bajar material a su computadora personal para uso exclusivamente personal o educacional y no comercial limitado a una copia por página. No se podrá remover o alterar de la copia ninguna leyenda de Derechos de Autor o la que manifieste la autoría del material.