Los ciclos de versión más rápidos son una de las principales ventajas de las arquitecturas de microservicios. Pero sin un buen proceso de CI/CD, no logrará la agilidad que prometen los microservicios. En este artículo se describen los desafíos y se recomiendan algunos enfoques para el problema.
¿Qué es CI/CD?
Cuando hablamos de CI/CD, realmente estamos hablando de varios procesos relacionados: integración continua, entrega continua e implementación continua.
integración continua. Los cambios de código se combinan con frecuencia en la rama principal. Los procesos de compilación y prueba automatizados garantizan que el código de la rama principal siempre sea de calidad de producción.
entrega continua. Los cambios de código que pasan el proceso de CI se publican automáticamente en un entorno similar a producción. La implementación en el entorno de producción en vivo puede requerir la aprobación manual, pero de lo contrario se automatiza. El objetivo es que el código siempre esté listo implementar en producción.
implementación continua. Los cambios de código que pasan los dos pasos anteriores se implementan automáticamente en producción.
Estos son algunos objetivos de un proceso sólido de CI/CD para una arquitectura de microservicios:
Cada equipo puede crear e implementar los servicios que posee de forma independiente, sin afectar a otros equipos ni interrumpirlos.
Antes de implementar una nueva versión de un servicio en producción, se implementa en entornos de desarrollo, pruebas y control de calidad para la validación. Las puertas de calidad se aplican en cada fase.
Una nueva versión de un servicio se puede implementar en paralelo con la versión anterior.
Se han implementado directivas de control de acceso suficientes.
En el caso de las cargas de trabajo en contenedor, puede confiar en las imágenes de contenedor que se implementan en producción.
¿Por qué es importante una canalización sólida de CI/CD?
En una aplicación monolítica tradicional, hay una única canalización de compilación cuya salida es el ejecutable de la aplicación. Todo el trabajo de desarrollo se alimenta en esta canalización. Si se encuentra un error de alta prioridad, se debe integrar, probar y publicar una corrección, lo que puede retrasar la versión de las nuevas características. Puede mitigar estos problemas teniendo módulos bien factorados y usando ramas de características para minimizar el impacto de los cambios de código. Pero a medida que la aplicación crece más compleja y se agregan más características, el proceso de liberación de un monolito tiende a ser más frágil y probablemente romperse.
Siguiendo la filosofía de los microservicios, nunca debe haber un tren de versión larga en el que todos los equipos tengan que ponerse en línea. El equipo que compila el servicio "A" puede publicar una actualización en cualquier momento, sin esperar a que los cambios en el servicio "B" se combinen, prueben e implementen.
monolítica de CI/CD
Para lograr una alta velocidad de versión, la canalización de versión debe automatizarse y ser muy confiable para minimizar el riesgo. Si publica en producción una o varias veces al día, las regresiones o las interrupciones del servicio deben ser poco frecuentes. Al mismo tiempo, si se implementa una actualización incorrecta, debe tener una manera confiable de revertir o revertir rápidamente a una versión anterior de un servicio.
Desafíos
Muchas bases de código independientes pequeñas. Cada equipo es responsable de crear su propio servicio, con su propia canalización de compilación. En algunas organizaciones, los equipos pueden usar repositorios de código independientes. Los repositorios independientes pueden provocar una situación en la que el conocimiento de cómo compilar el sistema se distribuye entre equipos y nadie de la organización sabe cómo implementar toda la aplicación. Por ejemplo, ¿qué ocurre en un escenario de recuperación ante desastres, si necesita implementar rápidamente en un nuevo clúster?
mitigación: tener una canalización unificada y automatizada para compilar e implementar servicios, de modo que este conocimiento no esté "oculto" dentro de cada equipo.
varios lenguajes y marcos. Con cada equipo con su propia combinación de tecnologías, puede ser difícil crear un único proceso de compilación que funcione en toda la organización. El proceso de compilación debe ser lo suficientemente flexible como para que cada equipo pueda adaptarlo para su elección de lenguaje o marco.
mitigación: contenedorice el proceso de compilación para cada servicio. De este modo, el sistema de compilación solo debe poder ejecutar los contenedores.
integración y pruebas de carga. Con los equipos que publiquen actualizaciones a su propio ritmo, puede ser difícil diseñar pruebas completas sólidas, especialmente cuando los servicios tienen dependencias en otros servicios. Además, ejecutar un clúster de producción completo puede ser costoso, por lo que es poco probable que cada equipo ejecute su propio clúster completo a escalas de producción, solo para pruebas.
Administración de versiones. Cada equipo debe poder implementar una actualización en producción. Esto no significa que todos los miembros del equipo tengan permisos para hacerlo. Pero tener un rol centralizado del Administrador de versiones puede reducir la velocidad de las implementaciones.
mitigación: cuanto mayor sea el proceso de CI/CD se automatice y sea confiable, menos debe haber necesidad de una autoridad central. Dicho esto, es posible que tenga directivas diferentes para publicar actualizaciones de características principales frente a correcciones de errores menores. Estar descentralizado no significa que la gobernanza sea cero.
Service actualiza. Al actualizar un servicio a una nueva versión, no debe interrumpir otros servicios que dependan de él.
mitigación: use técnicas de implementación como blue-green o canary release para cambios no importantes. Para realizar cambios importantes en la API, implemente la nueva versión en paralelo con la versión anterior. De este modo, los servicios que consumen la API anterior se pueden actualizar y probar para la nueva API. Consulte Servicios de actualización, a continuación.
Monorepo frente a varios repositorios
Antes de crear un flujo de trabajo de CI/CD, debe saber cómo se estructurará y administrará la base de código.
- ¿Los equipos funcionan en repositorios independientes o en un monorepo (repositorio único)?
- ¿Cuál es la estrategia de bifurcación?
- ¿Quién puede insertar código en producción? ¿Hay un rol de administrador de versiones?
El enfoque monorepo ha estado ganando favor, pero hay ventajas y desventajas para ambos.
Monorepo | Varios repositorios | |
---|---|---|
ventajas | Uso compartido de código Más fácil de estandarizar el código y las herramientas Más fácil de refactorizar código Detectabilidad: vista única del código |
Borrar la propiedad por equipo Potencialmente menos conflictos de combinación Ayuda a aplicar la desacoplamiento de microservicios |
desafíos de |
Los cambios en el código compartido pueden afectar a varios microservicios Mayor potencial para conflictos de combinación Las herramientas deben escalarse a una base de código grande Control de acceso Proceso de implementación más complejo |
Más difícil compartir código Más difícil de aplicar estándares de codificación Administración de dependencias Base de código difuso, mala detectabilidad Falta de infraestructura compartida |
Actualización de servicios
Hay varias estrategias para actualizar un servicio que ya está en producción. Aquí se describen tres opciones comunes: Actualización gradual, implementación azul-verde y versión de valor controlado.
Actualizaciones graduales
En una actualización gradual, se implementan nuevas instancias de un servicio y las nuevas instancias comienzan a recibir solicitudes inmediatamente. A medida que aparezcan las nuevas instancias, se quitan las instancias anteriores.
Ejemplo. En Kubernetes, las actualizaciones graduales son el comportamiento predeterminado al actualizar la especificación de pod para una implementación de . El controlador de implementación crea un nuevo ReplicaSet para los pods actualizados. A continuación, escala verticalmente el nuevo Objeto ReplicaSet al reducir verticalmente el anterior, para mantener el recuento de réplicas deseado. No elimina los pods antiguos hasta que los nuevos estén listos. Kubernetes mantiene un historial de la actualización, por lo que puede revertir una actualización si es necesario.
Ejemplo. Azure Service Fabric usa la estrategia de actualización gradual de forma predeterminada. Esta estrategia es más adecuada para implementar una versión de un servicio con nuevas características sin cambiar las API existentes. Service Fabric inicia una implementación de actualización mediante la actualización del tipo de aplicación a un subconjunto de los nodos o un dominio de actualización. A continuación, se reenvía al siguiente dominio de actualización hasta que se actualicen todos los dominios. Si no se puede actualizar un dominio de actualización, el tipo de aplicación se revierte a la versión anterior en todos los dominios. Tenga en cuenta que un tipo de aplicación con varios servicios (y si todos los servicios se actualizan como parte de una implementación de actualización) son propensos a errores. Si un servicio no se actualiza, toda la aplicación se revierte a la versión anterior y los demás servicios no se actualizan.
Un desafío de las actualizaciones graduales es que durante el proceso de actualización, se está ejecutando y recibiendo tráfico una combinación de versiones antiguas y nuevas. Durante este período, cualquier solicitud podría enrutarse a cualquiera de las dos versiones.
Para realizar cambios importantes en la API, se recomienda admitir ambas versiones en paralelo hasta que se actualicen todos los clientes de la versión anterior. Consulte elde control de versiones de la API de
Implementación azul-verde
En una implementación azul-verde, se implementa la nueva versión junto con la versión anterior. Después de validar la nueva versión, cambie todo el tráfico a la vez de la versión anterior a la nueva. Después del modificador, supervisará la aplicación para detectar cualquier problema. Si algo va mal, puede volver a cambiar a la versión anterior. Suponiendo que no hay problemas, puede eliminar la versión anterior.
Con una aplicación monolítica o de N niveles más tradicional, la implementación azul-verde generalmente significaba el aprovisionamiento de dos entornos idénticos. Implementaría la nueva versión en un entorno de ensayo y, a continuación, redirigiría el tráfico de cliente al entorno de ensayo; por ejemplo, intercambiando direcciones VIP. En una arquitectura de microservicios, las actualizaciones se producen en el nivel de microservicio, por lo que normalmente implementaría la actualización en el mismo entorno y usaría un mecanismo de detección de servicios para intercambiar.
ejemplo. En Kubernetes, no es necesario aprovisionar un clúster independiente para realizar implementaciones azules y verdes. En su lugar, puede aprovechar las ventajas de los selectores. Cree un nuevo recurso Deployment con una nueva especificación de pod y otro conjunto de etiquetas. Cree esta implementación, sin eliminar la implementación anterior ni modificar el servicio que apunte a ella. Una vez que se ejecutan los nuevos pods, puede actualizar el selector del servicio para que coincida con la nueva implementación.
Una desventaja de la implementación azul-verde es que, durante la actualización, se ejecutan el doble de pods para el servicio (actual y siguiente). Si los pods requieren una gran cantidad de recursos de CPU o memoria, es posible que tenga que escalar horizontalmente el clúster temporalmente para controlar el consumo de recursos.
Versión de valor controlado
En una versión de valor controlado, implementará una versión actualizada en un pequeño número de clientes. Después, supervise el comportamiento del nuevo servicio antes de implementarlo en todos los clientes. Esto le permite realizar una implementación lenta de forma controlada, observar datos reales y detectar problemas antes de que todos los clientes se vean afectados.
Una versión controlada es más compleja de administrar que la actualización azul-verde o gradual, ya que debe enrutar dinámicamente las solicitudes a diferentes versiones del servicio.
ejemplo. En Kubernetes, puede configurar un Service para abarcar dos conjuntos de réplicas (uno para cada versión) y ajustar los recuentos de réplicas manualmente. Sin embargo, este enfoque es bastante general, debido a la forma en que Kubernetes equilibra la carga entre pods. Por ejemplo, si tiene un total de 10 réplicas, solo puede desplazar el tráfico en 10% incrementos. Si usa una malla de servicio, puede usar las reglas de enrutamiento de malla de servicio para implementar una estrategia de versión controlada más sofisticada.
Pasos siguientes
- ruta de aprendizaje de : definir e implementar de integración continua
- aprendizaje : Introducción a la entrega continua
- de arquitectura de microservicios de
- Por qué usar un enfoque de microservicios para compilar aplicaciones