Resiliencia de aplicaciones mediante Akka Cluster Sharding
Introducción
Este documento existe para comunicar cómo las aplicaciones IPF usan Akka Cluster Sharding para la recuperación y la resiliencia. No es una inmersión profunda en Akka Cluster Sharding y se proporcionan referencias para lecturas adicionales. La idea clave a tener en cuenta al revisar y entender cómo IPF proporciona resiliencia y recuperación es que IPF hace uso de funcionalidades de Akka y un flow de IPF es simplemente una entity dentro de un Actor system.
Akka Cluster Sharding
Akka Cluster Sharding funciona permitiendo que una aplicación escale a través de múltiples nodos (instancias de la aplicación), distribuyendo entities (flows en IPF) a través de varios nodos en un clúster, enviándoles mensajes a través de un ID único sin tener que preocuparte en qué nodo vive la entity en el clúster. Este último punto es crucial para cómo Akka proporciona resiliencia y puede mover de forma transparente la ubicación de una entity a otro nodo en caso de fallo.
Definiciones
Entity: para nuestra discusión, una entity es el equivalente a una instancia de un IPF Flow (es decir, la instancia de un flow de IPF procesando una transacción única concreta).
Shard: un shard es un grupo de entities que se gestionarán juntas.
Shard Region: actúa como mediador para shards y maneja el enrutamiento de mensajes a la entity correcta, ya sea a uno de sus shards o a través de otra shard region en otros nodos.
Node: entorno de ejecución para tu aplicación IPF (normalmente un pod dentro de un entorno contenedorizado).
Persistence: el almacenamiento del estado de las entities registrando todos sus events.
Puntos clave
-
Sharding significa que actores con un identificador, llamadas entities, pueden distribuirse automáticamente a través de múltiples nodos del clúster.
-
Cada actor entity (instancia de flow de IPF) se ejecuta solo en un lugar.
-
Los mensajes que debe procesar una entity pueden enviarse a la entity (instancia de flow de IPF) sin que el remitente necesite conocer la ubicación física del actor destino.
-
La ubicación física de la entity puede cambiar con el tiempo.
-
Persistence se usa junto con Cluster Sharding para recuperar el estado de una entity (IPF Flow) después de que se haya movido.
-
Dado que Akka persistence se basa en un principio de single-writer, solo un actor persistente está activo en cualquier parte del clúster.
-
Cluster sharding también ayuda con el escalado para permitir la distribución de muchos actores con estado a través de más de una unidad de procesamiento (típicamente un pod).
| Los siguientes escenarios hablan mucho de cómo el clustering de Akka funciona en IPF porque su funcionalidad core sustenta cómo se gestionan los flows. Donde leas la palabra "entity", intercámbiala con "instancia de flow de IPF". |
Operación BAU del clúster
Considera un despliegue típico de tu aplicación IPF de procesamiento de pagos (una aplicación SpringBoot que envuelve los flows de IPF, ejecutada dentro de un pod). En este ejemplo tenemos 3 nodos (3 instancias del pod).
Solo una instancia de una entity se ejecuta en el clúster en un momento dado, pero es muy posible que los mensajes entrantes lleguen a diferentes nodos.
Así, por ejemplo, cuando un mensaje llega a una Shard Region pero la ubicación de esa entity está en otra region en un nodo separado, el mensaje se enruta dentro del Akka Cluster al shard correcto para ser procesado por la entity única (instancia de flow de IPF). En el diagrama de abajo, lo siguiente sucede cuando se procesa un mensaje en un nodo específico:
-
Node 2 recibe el mensaje que es para entity/flow ID=6
-
La Shard Region (SR) en Node 2 no tiene esa entity dentro de ninguno de sus shards (la entity ID=6 no se ejecuta en Node 2)
-
La SR en Node 2 busca la ubicación de la entity y reenvía el mensaje a través del Akka Cluster a la SR que se ejecuta en Node 3
-
La SR en Node 3 conoce la entity ID=6 y reenvía el mensaje para que lo procese esa entity
El escenario real para esto, desde la perspectiva de procesamiento IPF, es cuando tienes múltiples nodos procesando desde los mismos topics de Kafka.
-
Cuando el flow de IPF envía una request a un external domain, el nodo que procesa la response no está controlado a nivel de Kafka.
-
Cada nodo leerá responses del mismo topic de Kafka, pero el nodo que lea la response puede que no haya sido el nodo que procesó la request (y por tanto la ubicación de la entity del flow)
Esto es transparencia de ubicación y significa que, para el procesamiento BAU, el clúster maneja el enrutamiento a una ubicación física y podemos simplemente referirnos a la entity por su ID lógico. Así es como IPF funciona para enrutar los mensajes a la instancia correcta del flow de IPF. Y esto es crucial no solo desde la perspectiva BAU sino también, desde una perspectiva de resiliencia y fallos, para hacer frente a fallos de nodo y reinicios.
Observa la introducción de un Shard Coordinator. Este tiene varias responsabilidades y es crucial para permitir ese enrutamiento y transparencia de ubicación. Los shards son gestionados por el Shard Coordinator (SC) y este:
-
es un cluster singleton (solo hay 1 dentro de un Akka Cluster)
-
monitoriza nodos del sistema y la ubicación de cada shard
-
asegura que el sistema sepa a dónde enviar mensajes para una entity específica
-
decide qué shard vive en qué shard region
-
Está respaldado por Akka persistence: para reproducir events al fallo del SC y permitirle reanudar su estado.
Escenario de caída de nodo
Lo siguiente muestra lo que sucede en el Akka Cluster cuando un nodo falla o se reinicia. La funcionalidad y el comportamiento aquí aseguran que las entities puedan reanudarse en otros nodos y que el procesamiento pueda continuar como BAU independientemente del fallo del nodo.
Dos cosas están ocurriendo en este escenario donde parte de la infraestructura está caída haciendo que una Shard Region (SR) no esté disponible.
-
Node 2 recibe el mensaje que es para entity/flow ID=6
-
La Shard Region (SR) en Node 2 no tiene esa entity dentro de ninguno de sus shards (la entity ID=6 no se ejecuta en Node 2)
-
La SR que recibe el mensaje intenta enviarlo a la SR que tiene la entity (esto estaba en Node 3)
-
Pero la SR en Node 3 ahora no está disponible
-
-
El Shard Coordinator interviene para mover el Shard a una SR diferente (en nuestro caso Node 1)
-
La SR en Node 2 ahora reenvía el mensaje a través del Akka Cluster a la SR que se ejecuta en Node 1
-
La SR en Node 1 conoce la entity ID=6 (su Shard acaba de moverse aquí) y reenvía el mensaje para que lo procese esa entity
Lo más importante a notar en este escenario es que la ubicación del flow en ejecución (entity) es transparente, y es el Akka Cluster el que proporciona el enrutamiento al shard correcto y, por ende, al nodo. Cada entity se ejecuta solo en un lugar y, por lo tanto, esas entities en ejecución pueden distribuirse y moverse por el clúster sin que ningún remitente tenga que conocer la ubicación/nodo del actor destino. Las entities son persistentes y, por lo tanto, pueden reanudarse en cualquier shard en cualquier nodo, ya que reproducirán los events para reanudar el estado correcto.
Particiones de red / resolución de Split Brain
Por defecto, implementamos el método por defecto de resolución de split-brain keep-majority en IPF proporcionado por Akka; esto permite la mejor combinación de escalabilidad en un entorno cloud.
| Estrategia | Requisitos |
|---|---|
keep-majority (por defecto) |
Número impar de nodos en el Akka Cluster global |
lease-majority |
Debe haber un único clúster de Kubernetes |
keep-oldest |
Podría llevar a que se termine el quórum más grande |
Si un cliente ejecuta en un entorno donde solo hay dos Data Centres disponibles y cada data centre tiene su propio clúster de Kubernetes aislado, entonces keep-oldest atiende escenarios en los que un único DC (la mitad de nodos) se particiona y utiliza el cluster singleton para actuar como arbitraje de salud del clúster.
Preguntas comunes
Q. ¿Cómo sabe la Shard Region (SR) qué entities/instancias de flow tiene?
La SR lleva el seguimiento de todos los shards y entities que tiene. Esto significa que no necesita resolver la ubicación externamente (con el Shard Coordinator) cada vez.
Q. Si el shard está en otra SR, ¿cómo llega el mensaje a la SR correcta (y por ende al nodo)?
Si el shard está en otra SR, entonces los mensajes deben reenviarse a esa SR. La SR que recibe el mensaje tiene que resolver la ubicación del shard destino desde el clúster y el Shard Coordinator (SC) es responsable de conocer la ubicación de todos los shards y entities en todo el clúster. Una vez que la ubicación se resuelve, los mensajes se entregan al shard destino.
Q. Mientras la SR resuelve la ubicación de un shard, ¿qué ocurre con los mensajes entrantes para esa entity?
Mientras se resuelve la ubicación del shard, los mensajes entrantes se almacenan en buffer y se entregarán después, una vez que se conozca la ubicación del shard. Cualquier mensaje subsiguiente resuelto a ese shard 'remoto' puede entregarse al destino inmediatamente sin involucrar al shard coordinator para determinar la ubicación.
Q. ¿Cómo aseguramos que no se pierda estado al mover entities/flows entre SR?
Usamos Akka persistence para almacenar los events, y permitir recuperar el estado de un actor reproduciendo events al reiniciar la entity/flow.
| el shard coordinator también está respaldado por Akka persistence, para reproducir events al fallo y permitir reanudar su estado. |
Q. Si un nodo envía una request a un external domain (p. ej., Sanctions) pero su response se lee desde Kafka por otro nodo, ¿cómo recibe la response el flow correcto?
Este es el escenario descrito arriba en "Operación BAU del clúster" y es la operación normal ya que queremos transparencia de ubicación para la entity (lo que significa que puede moverse o recuperarse según sea necesario).
Q. Si un nodo cae, ¿qué pasa con las entities/flows procesando en la shard region de ese nodo?
Este es el escenario descrito arriba en "Escenario de caída de nodo" y cubre cómo los shards con sus entities se mueven para ejecutarse dentro de otra shard region.
Q. ¿Qué es el reequilibrio de shards?
Este es el proceso mediante el cual el shard coordinator facilita el reequilibrio de shards cuando se agregan nuevos miembros al clúster. Así, las entities pueden moverse de un nodo a otro. El proceso de reequilibrio hará que las shard regions almacenen en buffer los mensajes entrantes para un shard y el coordinator no responderá a las solicitudes de ubicación hasta que el reequilibrio se complete. Esos mensajes de entity se almacenan en buffer hasta que el coordinator responde a las solicitudes de ubicación, momento en el cual el enrutamiento de esos mensajes almacenados puede reanudarse.
Q. ¿Qué ocurre si cae el shard coordinator?
El estado del shard coordinator es persistente y, por lo tanto, puede recuperarse. Si el coordinator se cae o el nodo se vuelve inalcanzable, un nuevo shard coordinator singleton tomará el control y el estado se recupera desde el almacén persistente. Durante este tiempo, las ubicaciones conocidas aún están disponibles para las shard regions, pero cualquier solicitud de ubicación se almacena en buffer hasta que el coordinator vuelva a estar en línea.
Referencias
Para más sobre conceptos de cluster sharding, ver aquí.
Para una inmersión más completa en detalles técnicos de Akka Cluster Sharding, por favor lee aquí.
Para una inmersión técnica en cómo funciona Akka Clustering (con ejemplos de código) esto merece verse, parte 1 aquí.
Para detalles sobre Split Brain Resolution ver aquí.