Las directrices conocidas como la aplicación de los doce factores se publicaron por primera vez hace más de diez años. Desde entonces, casi todas sus prácticas obligatorias se han convertido en la forma estándar de facto de escribir e implementar aplicaciones web. Y si bien han seguido siendo aplicables frente a los cambios en la forma en que se organizan e implementan las aplicaciones, en algunos casos se requieren matices adicionales para comprender cómo se aplican las prácticas a los patrones de microservicios para desarrollar e implementar aplicaciones.
Este blog se centra en el Factor 3, Configuración de la tienda en el entorno , que establece:
Al avanzar hacia los microservicios, aún se pueden respetar estas directrices, pero no siempre de forma que se correspondan con la interpretación literal de la aplicación de doce factores. Algunas directrices, como proporcionar datos de configuración como variables de entorno, se aplican correctamente. Otras prácticas comunes de microservicios, si bien respetan los principios básicos de la aplicación de doce factores, son más bien extensiones de ella. En esta publicación, analizaremos tres conceptos fundamentales de la gestión de configuración para microservicios a través de la lente del Factor 3:
Antes de adentrarnos en la discusión sobre la adaptación de Factor 3 para microservicios, es útil comprender algunos términos y conceptos clave.
Con una aplicação monolítica, todos los equipos de la organización trabajan en la misma aplicação y la misma infraestructura circundante. Aunque las aplicaciones monolíticas generalmente parecen más simples que los microservicios en el papel, existen varias razones comunes por las que las organizaciones deciden migrar a microservicios:
Por supuesto, los microservicios vienen con sus propios desafíos, que incluyen mayor complejidad, menor observabilidad y la necesidad de nuevos modelos de seguridad, pero muchas organizaciones, especialmente las grandes o de rápido crecimiento, deciden que los desafíos valen la pena para dar a sus equipos más autonomía y flexibilidad para crear bases confiables y estables para las experiencias que brindan a sus clientes.
Al refactorizar una aplicación monolítica en microservicios, sus servicios deben:
Para una aplicación monolítica, pequeñas inconsistencias en los procesos y la dependencia de suposiciones compartidas no son críticas. Sin embargo, al haber muchos microservicios separados, esas inconsistencias y suposiciones pueden generar mucho dolor y caos. Muchos de los cambios que es necesario realizar con los microservicios son necesidades técnicas, pero una cantidad sorprendente tiene que ver con cómo los equipos trabajan internamente e interactúan con otros equipos.
Los cambios organizativos notables con una arquitectura de microservicios incluyen:
Un área de la arquitectura de microservicios en la que necesitamos ampliar el Factor 3 tiene que ver con la necesidad de definir claramente cierta información vital sobre un servicio, incluida su configuración, y asumir un mínimo de contexto compartido con otros servicios. El factor 3 no aborda esto directamente, pero es especialmente importante con un gran número de microservicios separados que contribuyen a la funcionalidad de la aplicação .
Como propietario de un servicio en una arquitectura de microservicios, su equipo posee servicios que desempeñan funciones específicas en el sistema en su conjunto. Otros equipos cuyos servicios interactúan con el suyo necesitan acceder al repositorio de su servicio para leer el código y la documentación, así como para realizar contribuciones.
Además, es una lamentable realidad en el campo del desarrollo de software que la composición del equipo cambia a menudo, no solo porque los desarrolladores se unen y abandonan la empresa, sino también debido a la reorganización interna. Además, la responsabilidad de un determinado servicio a menudo también se transfiere entre equipos.
En vista de estas realidades, su base de código y documentación deben ser extremadamente claros y consistentes, lo que se logra mediante lo siguiente:
Muchos marcos de aplicação proporcionan un medio para definir la configuración requerida. Por ejemplo, el paquete NPM convict
para aplicações Node.js utiliza un “esquema” de configuración completo almacenado en un solo archivo. Actúa como fuente de verdad para toda la configuración que una aplicación Node.js requiere para ejecutarse.
Un esquema sólido y fácilmente detectable permite que los miembros de su equipo y otras personas interactúen con confianza con su servicio.
Una vez que haya definido claramente qué valores de configuración necesita su aplicação , también debe respetar la importante distinción entre las dos fuentes principales de las que una aplicação de microservicios implementada extrae su configuración:
Los scripts de implementación son un patrón común de organización de código en las arquitecturas de microservicios. Como son nuevos desde la publicación original de la aplicación de doce factores, necesariamente representan una extensión de ella.
En los últimos años, se ha vuelto común tener una carpeta llamada infraestructura (o alguna variante de ese nombre) en el mismo repositorio que el código de la aplicação . Generalmente contiene:
A primera vista, esto podría parecer una violación de la prescripción de Factor 3 de que la configuración esté estrictamente separada del código.
De hecho, su ubicación junto a su aplicação significa que una carpeta de infraestructura realmente respeta la regla y al mismo tiempo permite mejoras de proceso valiosas que son fundamentales para los equipos que trabajan en entornos de microservicios.
Los beneficios de este patrón incluyen:
Tenga en cuenta que los beneficios que ofrece este patrón refuerzan la autonomía individual del equipo y, al mismo tiempo, garantizan que se aplique un rigor adicional al proceso de implementación y configuración.
En la práctica, utiliza los scripts de implementación almacenados en su carpeta de infraestructura para administrar tanto la configuración definida explícitamente en los mismos scripts como la recuperación de la configuración desde fuentes externas en el momento de la implementación, al tener el script de implementación para un servicio:
Los valores de configuración que son específicos de una determinada implementación de su servicio y que están totalmente bajo el control de su equipo se pueden especificar directamente en los archivos de la carpeta de infraestructura. Un ejemplo podría ser algo así como un límite en el tiempo durante el cual se permite ejecutar una consulta de base de datos iniciada por la aplicación. Este valor se puede cambiar modificando el archivo de implementación y volviendo a implementar la aplicação.
Un beneficio de este esquema es que los cambios en dicha configuración necesariamente pasan por revisión de código y pruebas automatizadas, lo que reduce la probabilidad de que un valor mal configurado provoque una interrupción. Los cambios en los valores que pasan por la revisión de código y los valores de las claves de configuración en un momento determinado se pueden descubrir en el historial de sus herramientas de control de código fuente.
Los valores que son necesarios para que la aplicação se ejecute, pero que no están bajo el control de su equipo, deben ser proporcionados por el entorno en el que se implementa la aplicação . Un ejemplo es el nombre de host y el puerto en el que el servicio se conecta a otro microservicio del que depende.
Como ese servicio no es propiedad de su equipo, no puede hacer suposiciones sobre valores como el número de puerto. Estos valores pueden cambiar en cualquier momento y es necesario registrarlos en algún almacenamiento de configuración central cuando se modifican, ya sea que el cambio se realice manualmente o mediante algún proceso automático. Luego podrán ser consultados por aplicações que dependen de ellos.
Podemos resumir estas pautas en dos prácticas recomendadas para la configuración de microservicios.
Puede parecer más sencillo codificar ciertos valores en sus scripts de implementación, por ejemplo, la ubicación de un servicio con el que interactúa su servicio. En la práctica, codificar ese tipo de configuración es peligroso, especialmente en entornos modernos donde las ubicaciones de los servicios suelen cambiar con frecuencia. Y es especialmente peligroso si no tienes el segundo servicio.
Puede pensar que puede confiar en su propia diligencia para mantener la ubicación del servicio actualizada en sus scripts o, peor aún, que puede confiar en que el equipo propietario le informará cuando la ubicación cambie. La diligencia a menudo falla en momentos de estrés y, dependiendo del rigor humano, su sistema corre el riesgo de fallar sin previo aviso.
Independientemente de que la información de ubicación esté codificada o no, su aplicação no debe depender de que la infraestructura crítica se encuentre en una ubicación determinada. En cambio, un servicio recién implementado necesita formular preguntas a alguna fuente común dentro del sistema como "¿dónde está mi base de datos?" y recibir una respuesta precisa sobre la ubicación actual de ese recurso externo. Hacer que cada servicio se registre en el sistema a medida que se implementa hace que las cosas sean mucho más sencillas.
Así como el sistema debe proporcionar respuestas a las preguntas "¿dónde está mi base de datos?" y "¿dónde está el 'servicio X' del que dependo?", un servicio debe estar expuesto al sistema de tal manera que otros servicios puedan encontrarlo y comunicarse con él fácilmente sin saber nada acerca de cómo está implementado.
Una práctica de configuración clave en las arquitecturas de microservicios es el descubrimiento de servicios: el registro de nueva información de servicio y la actualización dinámica de esa información a medida que acceden otros servicios. Después de explicar por qué el descubrimiento de servicios es necesario para los microservicios, exploremos un ejemplo de cómo lograrlo con NGINX Open Source y Consul.
Es una práctica común tener varias instancias (implementaciones) de un servicio ejecutándose al mismo tiempo. Esto permite no solo gestionar tráfico adicional, sino también actualizar un servicio sin tiempo de inactividad mediante el lanzamiento de una nueva implementación. Al actuar como proxy inverso y equilibrador de carga, herramientas como NGINX procesan el tráfico entrante y lo dirigen a la instancia más adecuada. Este es un patrón agradable, porque los servicios que dependen de su servicio envían solicitudes solo a NGINX y no necesitan saber nada sobre sus implementaciones.
A modo de ejemplo, supongamos que tiene una única instancia de un servicio llamado messenger ejecutándose detrás de NGINX que actúa como un proxy inverso.
¿Y ahora qué pasa si tu aplicación se vuelve popular? Eso se considera una buena noticia, pero luego te das cuenta de que debido al aumento del tráfico, la instancia de mensajería consume mucha CPU y tarda más en procesar las solicitudes, mientras que la base de datos parece estar funcionando bien. Esto indica que es posible que puedas resolver el problema implementando otra instancia del servicio de mensajería .
Cuando se implementa la segunda instancia del servicio de mensajería , ¿cómo sabe NGINX que está activo y comienza a enviarle tráfico? Agregar manualmente nuevas instancias a su configuración de NGINX es un enfoque, pero rápidamente se vuelve inmanejable a medida que más servicios aumentan o reducen su escala.
Una solución común es rastrear los servicios en un sistema con un registro de servicios de alta disponibilidad como Consul . Las nuevas instancias de servicio se registran en Consul a medida que se implementan. Consul monitorea el estado de las instancias enviándoles periódicamente controles de salud. Cuando una instancia no pasa las comprobaciones de estado, se elimina de la lista de servicios disponibles.
NGINX puede consultar un registro como Consul usando una variedad de métodos y ajustar su enrutamiento en consecuencia. Recuerde que cuando actúa como proxy inverso o balanceador de carga, NGINX enruta el tráfico a servidores “ascendentes”. Considere esta sencilla configuración:
# Define un grupo ascendente llamado "messenger_service"
upstream messenger_service {
servidor 172.18.0.7:4000;
servidor 172.18.0.8:4000;
}
servidor {
escuchar 80;
ubicación /api {
# Proxy del tráfico HTTP con rutas que empiezan por '/api' al bloque 'upstream' anterior. El algoritmo de balanceo de carga predeterminado, Round-Robin, alterna las solicitudes entre los dos servidores del bloque.
proxy_pass http://messenger_service;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
De forma predeterminada, NGINX necesita saber la dirección IP precisa y el puerto de cada instancia de mensajería para enrutar el tráfico hacia ella. En este caso, es el puerto 4000 tanto en 172.18.0.7 como en 172.18.0.8.
Aquí es donde entran en juego el Cónsul y la plantilla Cónsul . La plantilla de Consul se ejecuta en el mismo contenedor que NGINX y se comunica con el cliente de Consul que mantiene el registro del servicio.
Cuando cambia la información de registro, la plantilla Consul genera una nueva versión del archivo de configuración NGINX con las direcciones IP y los puertos correctos, la escribe en el directorio de configuración NGINX y le indica a NGINX que recargue su configuración. No hay tiempo de inactividad cuando NGINX recarga su configuración y la nueva instancia comienza a recibir tráfico tan pronto como se completa la recarga.
Con un proxy inverso como NGINX en este tipo de situación, hay un único punto de contacto para registrarse en el sistema como el lugar al que pueden acceder otros servicios. Su equipo tiene la flexibilidad de administrar instancias de servicio individuales sin tener que preocuparse de que otros servicios pierdan acceso al servicio en su totalidad.
Es cierto que los microservicios aumentan la complejidad, tanto en términos técnicos para sus servicios como en términos organizacionales para sus relaciones con otros equipos. Para disfrutar de los beneficios de una arquitectura de microservicios es importante reexaminar críticamente las prácticas diseñadas para monolitos para asegurarse de que todavía brinden los mismos beneficios cuando se aplican a un entorno muy diferente. En este blog, exploramos cómo el Factor 3 de la aplicación de doce factores aún aporta valor en un contexto de microservicios, pero puede beneficiarse de pequeños cambios en cómo se aplica concretamente.
Para obtener más información sobre cómo aplicar la aplicación de doce factores a las arquitecturas de microservicios, consulte la Unidad 1 de Microservicios de marzo de 2023 ( próximamente en el blog ). Regístrese gratuitamente para obtener acceso a un seminario web sobre este tema y a un laboratorio práctico.
"Esta publicación de blog puede hacer referencia a productos que ya no están disponibles o que ya no reciben soporte. Para obtener la información más actualizada sobre los productos y soluciones F5 NGINX disponibles, explore nuestra familia de productos NGINX . NGINX ahora es parte de F5. Todos los enlaces anteriores de NGINX.com redirigirán a contenido similar de NGINX en F5.com.