Despliegues con rolling upgrades con IPF SDK
Introducción
Los despliegues con rolling upgrade son una estrategia crucial para mantener la disponibilidad del sistema durante las actualizaciones. Para evitar caídas y asegurar disponibilidad continua, un rolling upgrade actualiza un sistema de forma incremental — instancia por instancia o, en el caso de clústeres grandes, varias instancias a la vez. Para garantizar que los rolling upgrades funcionen correctamente, todos los componentes de la nueva versión de tu servicio de orquestación deben ser compatibles hacia atrás con la versión anterior.
El IPF SDK uses many technologies, pero, de forma crucial, el IPF Orchestration Framework clave está construido sobre Akka, un potente toolkit para construir aplicaciones con gran concurrencia, distribuidas y resilientes, dirigidas por mensajes, que proporciona un soporte robusto para rolling upgrades, especialmente cuando se usan actores con event sourcing para persistencia de estado, como hace IPF.
Este documento está dirigido a una audiencia técnica y describe el proceso y las consideraciones para implementar despliegues con rolling upgrade de servicios de orquestación construidos con el IPF SDK.
Estrategia de despliegue con rolling upgrade
Un servicio de orquestación construido con el IPF SDK representa una aplicación con estado que se despliega como un único clúster de Akka. Como consecuencia, realizar rolling upgrades en un sistema así requiere una estrategia más compleja que la de un microservicio HTTP típico.
Los siguientes pasos describen el proceso para realizar rolling upgrades de servicios de orquestación IPF.
Paso 1: preparar la nueva versión
Durante un rolling upgrade, se producirá un rebalanceo de shards de Akka. Combinado con remembering entities de Akka habilitado, esto hará que con frecuencia instancias de flows iniciadas en una versión antigua de un nodo del servicio se reinicien en un nodo del servicio que ejecute la nueva versión. Además del rebalanceo de shards de Akka, ciertos transports de mensajes (como Kafka, Pulsar, AWS Kinesis, etc.) también necesitarán reasignación de particiones a medida que se detienen nodos consumidores antiguos y se inician nuevos.
|
Todo el trabajo de rebalanceo que sucede como parte de un rolling upgrade puede causar que flows sensibles al tiempo expiren, especialmente si el rolling upgrade se realiza mientras el servicio procesa volúmenes cercanos a su capacidad. Para evitar un gran impacto en el procesamiento, es aconsejable programar tus actualizaciones durante los periodos de bajo volumen típicos de tu servicio. |
Mantener compatibilidad hacia atrás requiere lo siguiente:
-
Asegurar que las versiones existentes de los flows no cambien — una vez publicada, una versión de flow debe considerarse inmutable.
-
Cambiar un flow sin versionado introduce imprevisibilidad: un flow en curso iniciado en la versión antigua del servicio puede no poder progresar cuando se rehidrata en la nueva versión debido a diversas incompatibilidades y, por lo tanto, quedaría atascado en un bucle infinito de recuperación-fallo.
-
Si un flow necesita actualizarse — se cambian transitions, se introducen nuevos states, etc. — debe crearse una nueva versión del flow y realizar los cambios en esa versión. Consulta Versioning Flows en la sección del tutorial para instrucciones sobre cómo hacerlo.
-
-
Asegurar que no se eliminen versiones activas de flows de la nueva versión del servicio.
-
Eliminar una versión de flow presente en la versión antigua del servicio abre la puerta a dejar flows en curso huérfanos. IPF usa roles de Akka Cluster para decidir qué nodos pueden alojar qué flows. Si, para cuando el rolling upgrade alcance el nodo final, aún hay flows en curso ejecutando la versión eliminada, el clúster se encontrará en un estado donde ningún nodo podrá alojar la versión antigua del flow, causando que el flow en curso quede huérfano y permanentemente incompleto.
-
Si la versión antigua de tu servicio tenía la capacidad de iniciar un número de flows de cierta versión, esos flows no deben eliminarse en la nueva versión.
-
Elimina flows solo cuando no existan instancias en curso de ellos y la versión antigua no tenga forma de iniciarlos.
-
Para determinar si puede iniciarse un determinado flow, debes inspeccionar tus routing rules (cubiertas en el siguiente punto).
-
-
Proveer routing rules para los nuevos flows y actualizar las reglas para los antiguos.
-
Context-Based Flow Version Selection te permite crear reglas de enrutamiento impulsadas por configuración que se basan en headers de mensajes para realizar el enrutamiento a diferentes versiones de un flow.
-
Alternativamente, como se indica en Versioning Flows, tienes control programático directo sobre qué versión de un flow iniciar, por lo que también es posible crear reglas de enrutamiento arbitrarias.
-
Por defecto, si no se especifica versión, se usa la última.
-
Dependiendo del nivel de confianza en la corrección de tus nuevas versiones de flow, puedes decidir seguir iniciando las versiones antiguas hasta que se cumplan ciertas condiciones: un cliente con un feature flag inicia el flow, el mensaje de iniciación contiene un header específico, aplica un predicado de configuración estática, etc. La selección de versión basada en contexto mencionada también puede usarse para realizar pruebas canary y permitirte migrar lentamente a una nueva versión de un flow. Consulta Selección de versión de flow basada en contexto para pruebas canary para más detalles.
-
-
Mantener compatibilidad con los event schemas existentes.
-
Los schemas se definen por los business data elements listados para el event en el DSL.
-
Romper un event schema hará que tus flows en curso no se puedan recuperar en un rebalanceo, dejándolos huérfanos e incapaces de completarse sin un despliegue hotfix.
-
Incluso si un flow no cambia los business data elements de un event, podría producirse un cambio no intencionado en un tipo usado en algún lugar del grafo de objetos, por ejemplo, al actualizar la versión de una biblioteca común.
-
Para asegurar que los event schemas no se rompan, debes crear una suite de tests que verifique tus schemas en build time.
-
Si por alguna razón poco probable no puedes evitar romper un event schema, puedes usar el IPF domain event schema evolution support como último recurso.
-
-
Asegurar que los consumers de responses de external domains sean compatibles hacia atrás.
-
Los consumers que procesan responses de tus external domains deben ser capaces de enviar los inputs adecuados tanto a las versiones antiguas como a las nuevas de tus flows.
-
No soportar las versiones antiguas además de las nuevas hará que los flows antiguos en curso expiren o queden huérfanos en un estado inconsistente.
-
-
Evitar cambios breaking en las APIs expuestas — si tu servicio define una API con la que integran otros componentes, la API debe permanecer compatible hacia atrás.
Paso 2: actualizar configuración
Actualiza la configuración de despliegue para soportar rolling upgrades:
-
Asegura que la configuración de Akka haya cambiado de formas compatibles.
-
Bastantes opciones de configuración de Akka — especialmente relacionadas con clustering, p. ej., estrategias de Split Brain Resolver, número de shards, etc. — requieren que todos los nodos del clúster estén configurados con el mismo valor para formar un clúster con éxito.
-
Si necesitas cambiar un valor de configuración de Akka que requiere consistencia a través del clúster, tendrás que detener todo tu clúster.
-
-
Asegura que la configuración de IPF haya cambiado de formas compatibles.
-
La configuración relacionada con reintentos de actions y timeouts es sensible a cambios de versión de flows; ver Versioning Flows para más detalles.
-
Debes considerar cuidadosamente al actualizar ciertas configuraciones del journal processor, ya que pueden romper la compatibilidad de rolling upgrade:
-
Incrementar el número de event partitions (ver Partitioning Events para más detalles) puede causar retrasos en el procesamiento, especialmente si el journal processor se despliega como una aplicación separada. En esos escenarios, debes asegurar que la configuración del journal processor se actualice y despliegue antes de empezar a desplegar tu servicio de orquestación.
-
Usar
EVENT_STREAM_PER_TAGcomo tuevent-streaming-typeen combinación con iniciar nuevas versiones de flows antes de que el rolling upgrade esté completo causará errores de deserialización y — dependiendo de la configuración de manejo de errores de tu journal processor — incluso podría resultar en pérdida permanente de datos.Si usas flow canary testing via version selection, puedes configurar la selección de versiones de forma que asegure que las nuevas versiones de flow solo se inicien con tu aprobación, mitigando así el riesgo de errores y pérdida de datos.
-
-
Paso 3: desplegar la nueva versión de forma incremental
Despliega la nueva versión a un subconjunto de nodos, asegurando que el sistema permanezca operativo:
-
Comienza actualizando un pequeño número de nodos (p. ej., 10% del clúster).
-
Incrementa gradualmente el porcentaje de nodos actualizados hasta que todo el clúster ejecute la nueva versión.
-
Monitoriza el servicio para detectar cualquier problema en el procesamiento.
-
El IPF SDK viene con un predefined set of monitoring tools y soporte para distributed tracing. Integrarlos en tu ecosistema de observabilidad resultará beneficioso al solucionar problemas.
-
-
Dependiendo de las routing rules, genera algo de tráfico que inicie las nuevas versiones de flows para asegurar que funcionan correctamente.
-
Incluso si tu pipeline de CD incluye un entorno pre-prod que refleja de cerca producción y has realizado pruebas extensas de tus nuevas versiones de flows en pre-prod, aún puede ser aconsejable aumentar lentamente el inicio de nuevas versiones justo después del despliegue, especialmente si los flows llaman a endpoints completamente nuevos de servicios externos.
-
Puedes apoyarte en los existing application metrics para rastrear qué versiones de qué flows se están iniciando y completando.
-
Paso 4a: completar la actualización
Una vez que todos los nodos se hayan actualizado y validado:
-
Sigue monitorizando el sistema para cualquier problema posterior a la actualización.
-
En este punto, cualquier problema que se encuentre probablemente se resuelva con tus procedimientos normales de corrección de errores.
-
-
Siéntete libre de eliminar cualquier código y configuraciones obsoletas relacionadas con la versión anterior.
-
Esto incluye versiones de flows que ya no están activas en tu servicio; con todas las instancias en curso de los flows antiguos completadas en producción y las routing rules impidiendo que se inicien de nuevo, ya no serán necesarias en la siguiente versión de tu servicio.
-
Por otro lado, si no te preocupa el tamaño de tus JARs, no hay daño en mantener los flows antiguos, especialmente si deseas permitirte la capacidad de construir journal processors que funcionen con todos los datos históricos del journal.
-
-
Documenta el proceso de actualización y cualquier lección aprendida para referencia futura.
Paso 4b: volver atrás (rollback)
En el improbable caso de que un problema con una de las nuevas versiones de flow solo se descubra cuando ya lo has desplegado en producción, podrías considerar volver a la versión anterior.
Algunas cosas a considerar antes de hacer rollback:
-
Todas las instancias en curso de las nuevas versiones de flow quedarán huérfanas y atascadas una vez que hagas rollback.
-
Incluso con remembering entities de Akka habilitado, no hay nada que se pueda hacer para permitir la reactivación de esos flows en la versión antigua del servicio de orquestación, ya que el código necesario para ejecutarlos ya no existe.
-
-
Si las versiones antiguas de tus flows funcionan correctamente — es decir, el error está presente solo en las nuevas versiones — entonces podría bastar con revertir tus routing rules para impedir el inicio de los nuevos flows.
-
Dependiendo de cómo configures tus routing rules, esto puede o no requerir reiniciar todos los nodos del clúster.
-
Revertir las routing rules no dejará ningún flow huérfano, pero el problema subyacente aún podría dejar los flows atascados y podría requerirse intervención manual.
-
A diferencia de hacer un rollback completo de la versión del servicio, revertir las routing rules te permitiría crear utilidades ad hoc que abort programáticamente cualquiera de los flows que estén atascados, o al menos passivatearlos. Usar la Transaction Operations API también es una opción para crear scripts de recuperación ad hoc.
-
Tiempo de inactividad requerido
Aunque el despliegue con rolling upgrade ofrece varias ventajas, como mínima interrupción y disponibilidad continua, hay escenarios donde no puede usarse de manera efectiva y se requiere downtime.
A continuación, algunas circunstancias que pueden impedir el uso de rolling upgrades.
Cambios incompatibles de base de datos
Cuando una nueva versión de una aplicación requiere cambios de base de datos que no son compatibles hacia atrás, los rolling upgrades pueden ser problemáticos o incluso inviables.
Aunque el IPF SDK promete mantener los esquemas de base de datos compatibles hacia atrás, algunos cambios pueden estar fuera del control del IPF SDK (por ejemplo, Akka cambiando sus representaciones internas) y algunos cambios pueden resultar necesarios para cumplir objetivos no funcionales (p. ej., sharding o estructurar colecciones de manera diferente para hacer ciertas consultas más eficientes).
Actualizaciones de dependencias
Si la nueva versión del servicio depende de una actualización a una dependencia crítica que no es compatible hacia atrás, los rolling upgrades podrían no ser factibles.
Ejemplos incluyen:
-
Actualizar a una nueva versión de una base de datos o cola de mensajes que requiere una actualización simultánea de todos los servicios cliente.
-
Actualizar a una nueva versión del IPF SDK. En este momento, el IPF SDK no soporta despliegues heterogéneos: todas las instancias deben usar la misma versión del SDK.
-
Introducir una nueva versión de un framework o librería con la que la versión antigua de la aplicación no pueda integrarse: por ejemplo, una actualización de versión de Akka que rompa la compatibilidad binaria con la versión antigua del servicio, impidiendo efectivamente que las instancias de la nueva versión formen un clúster con las de la antigua.
Vulnerabilidades de seguridad
Si la nueva versión resuelve vulnerabilidades de seguridad críticas que deben parchearse inmediatamente, un rolling upgrade puede no ser lo suficientemente rápido para mitigar el riesgo. En tales casos, podría ser necesario un despliegue completo para asegurar que todas las instancias se actualicen simultáneamente, ya que las actualizaciones retrasadas pueden exponer el sistema a amenazas de seguridad.
Conclusión
Los despliegues con rolling upgrade con el IPF SDK proporcionan una estrategia robusta y eficiente para mantener la disponibilidad del sistema durante las actualizaciones. Aprovechando las capacidades de Akka para gestionar aplicaciones concurrentes, distribuidas y resilientes, el IPF Orchestration Framework asegura transiciones suaves entre versiones de servicio.
Consideraciones clave para rolling upgrades exitosos incluyen:
-
Compatibilidad hacia atrás: Asegura que todos los componentes de la nueva versión del servicio sean compatibles hacia atrás para evitar interrupciones en la ejecución de flows.
-
Actualizaciones de configuración: Actualiza cuidadosamente las configuraciones de Akka e IPF para mantener la compatibilidad en todo el clúster.
-
Despliegue incremental: Despliega la nueva versión de forma incremental para monitorizar y abordar cualquier problema que surja sin afectar la disponibilidad general del servicio.
-
Monitorización y validación: Usa las herramientas de monitorización proporcionadas y el tracing distribuido para validar el nuevo despliegue y solucionar problemas.
-
Manejo de actualizaciones y rollbacks: Prepárate tanto para completar las actualizaciones como para posibles rollbacks, entendiendo las implicaciones para los flows en curso y manteniendo flexibilidad en las routing rules.
Siguiendo estas pautas, puedes gestionar eficazmente los rolling upgrades, asegurando mínima interrupción y manteniendo disponibilidad continua del servicio. Documentar el proceso y las lecciones aprendidas ayudará además a refinar futuros despliegues, contribuyendo a la resiliencia y robustez de tus servicios de orquestación construidos con el IPF SDK.