Administración de clústeres en Orleans
Orleans proporciona gestión de clústeres a través de un protocolo de membresía integrado, que a veces denominamos membresía del clúster . El objetivo de este protocolo es que todos los silos (servidores de OrleansOrleans) se pongan de acuerdo sobre el conjunto de silos activos en ese momento, detecten silos con errores y permitan la unión de nuevos silos al clúster.
El protocolo se basa en un servicio externo para proporcionar una abstracción de IMembershipTable. IMembershipTable es una tabla duradera plana que usamos para dos propósitos. En primer lugar, se usa como punto de encuentro para que los silos se encuentren entre sí y los clientes de Orleans encuentren silos. En segundo lugar, se usa para almacenar la vista de pertenencia actual, (lista de silos activos), y para coordinar el acuerdo sobre la vista de pertenencia.
Actualmente tenemos 6 implementaciones de la IMembershipTable: basadas en Azure Table Storage, azure Cosmos DB, ADO.NET (PostgreSQL, MySQL/MariaDB, SQL Server, Oracle), Apache ZooKeeper, Consul IO, AWS DynamoDB, MongoDB, Redis, Apache Cassandray una implementación en memoria para el desarrollo.
Además de IMembershipTable, cada silo participa en un protocolo de pertenencia punto a punto totalmente distribuido que detecta silos con errores y llega a un acuerdo sobre un conjunto de silos activos. Estamos describiendo a continuación la implementación interna del protocolo de pertenencia de Orleans.
Protocolo de pertenencia
Al iniciarse, cada silo agrega una entrada para sí mismo en una tabla compartida conocida, mediante una implementación de IMembershipTable. Se usa una combinación de la identidad del silo (
ip:port:epoch
) y el identificador de implementación del servicio (id. de clúster) como claves únicas en la tabla. La época es simplemente el tiempo en tics de cuando se inició el silo y, como tal, se garantiza queip:port:epoch
sea único en una implementación de Orleans determinada.Los silos se supervisan entre sí directamente, por medio de sondeos de aplicación («estás activo»
heartbeats
). Los sondeos se envían como mensajes directos de silo a silo, a través de los mismos sockets TCP por los que se comunican los silos. De este modo, los sondeos se correlacionan completamente con los problemas de red reales y el estado del servidor. Cada silo sondea un conjunto configurable de otros silos. Un silo elige a quién sondear al calcular hash coherentes en la identidad de otros silos, formar un anillo virtual de todas las identidades y escoger X silos sucesores en el anillo, (se trata de una conocida técnica distribuida denominada hash coherente y se usa ampliamente en muchas tablas de hash distribuidas, como Chord DHT).Si un silo S no obtiene Y respuestas de sondeo de los servidores P supervisados, lo sospecha escribiendo su sospecha con una marca de tiempo en la fila P de IMembershipTable.
Si P tiene más de Z sospechas dentro de K segundos, S escribe que P está inactivo en la fila de P y envía una instantánea de la tabla de pertenencia actual a todos los demás silos. Silos actualizan la tabla periódicamente, por lo que la instantánea es una optimización para reducir el tiempo necesario para que todos los silos obtengan información sobre la nueva visualización de membresía.
Más información:
La sospecha se escribe en IMembershipTable, en una columna especial en la fila que se corresponde con P. Cuando S sospecha de P escribe: «a la hora TTT, S sospechó de P».
Una sospecha no es suficiente para declarar P como inactivo. Se necesitan Z sospechas de distintos silos en un período de tiempo T que se puede configurar, normalmente tres minutos, para declarar P como inactivo. La sospecha se escribe con el control de simultaneidad optimista que ofrece la IMembershipTable.
El silo S que sospecha lee la fila de P.
Si
S
es el último que sospecha (ha habido Z-1 que sospechaban en un período de T, como se escribe en la columna de sospecha), S decide declarar P como Inactivo. En este caso, S se agrega a la lista de los que sospechan y también escribe en la columna de estado de P que P está Inactivo.De lo contrario, si S no es el último que sospecha, S simplemente se agrega a la columna de los que sospechan.
De otra forma, la operación de escritura diferida usa el número de versión o ETag que se leyó, para que las actualizaciones de esta fila se serializaran. En el caso de que no haya podido hacerse la operación de escritura debido al error de coincidencia de la versión o ETag, S reintenta (leer de nuevo e intentar escribir, a menos que P ya estuviera marcado como inactivo).
En un nivel elevado, esta secuencia de «lectura, modificación local, reescritura» es una transacción. Sin embargo, no estamos usando necesariamente transacciones de almacenamiento para hacerlo. El código de «transacción», se ejecuta localmente en un servidor y se usa simultaneidad optimista que proporciona IMembershipTable para garantizar el aislamiento y la atomicidad.
Cada silo periódicamente lee la tabla de pertenencia completa para su implementación. De este modo, los silos obtienen información sobre la unión de silos nuevos y sobre otros silos que se declaran inactivos.
Difusión de instantáneas: para reducir la frecuencia de lecturas periódicas de la tabla, cada vez que un silo escribe en la tabla (sospecha, nueva combinación, etc.), envía una instantánea del estado de la tabla actual a todos los demás silos. Dado que la tabla de miembros tiene una versión coherente y monotónica, cada actualización genera una instantánea con una versión única que se puede compartir de forma segura. Esto permite la propagación inmediata de los cambios de pertenencia sin tener que esperar al ciclo de lectura periódico. La lectura periódica se sigue manteniendo como un mecanismo de reserva en caso de que falle la distribución de instantáneas.
Visualizaciones de pertenencia ordenadas: el protocolo de pertenencia garantiza que todas las configuraciones de pertenencia estén ordenadas de manera global. Esta ordenación proporciona dos ventajas clave:
conectividad garantizada: cuando un nuevo silo se une al clúster, debe validar la conectividad bidireccional con cada otro silo activo. Si algún silo existente no responde (lo que podría indicar un problema de conectividad de red), el nuevo silo no puede unirse. Esto garantiza la conectividad completa entre todos los silos del clúster en tiempo de inicio. Consulte la nota sobre IAmAlive a continuación para obtener una excepción en el caso de la recuperación ante desastres.
Actualizaciones coherentes de directorios: los protocolos de nivel superior, como el directorio de grano distribuido, dependen de que todos los silos tengan una vista uniforme y monotónica de pertenencia. Esto permite una resolución más inteligente de activaciones de activación duplicadas. Para obtener más información, consulte la documentación del directorio de grano .
detalles de implementación:
El IMembershipTable requiere actualizaciones atómicas para garantizar un orden total global de cambios:
- Las implementaciones deben actualizar las entradas de tabla (lista de silos) y el número de versión de forma atómica
- Esto se puede lograr mediante transacciones de base de datos (como en SQL Server) o operaciones atómicas de comparación y intercambio mediante ETags (como en Azure Table Storage)
- El mecanismo específico depende de las funcionalidades del sistema de almacenamiento subyacente.
Una fila especial de versión de pertenencia en la tabla realiza un seguimiento de los cambios:
- Cada escritura en la tabla (sospechas, declaraciones de inactividad, combinaciones) incrementa este número de versión.
- Todas las escrituras se serializan a través de esta fila mediante actualizaciones atómicas.
- La versión que aumenta de forma monotónica garantiza una ordenación total de todos los cambios de pertenencia
Cuando silo S actualiza el estado del silo P:
- S lee primero el estado de la tabla más reciente.
- En una sola operación atómica, actualiza la fila de P e incrementa el número de versión.
- Si se produce un error en la actualización atómica (por ejemplo, debido a modificaciones simultáneas), la operación se reintenta con retroceso exponencial.
consideraciones de escalabilidad:
La serialización de todas las escrituras a través de la fila de versión puede afectar a la escalabilidad debido a un aumento de la contención. El protocolo se ha demostrado en producción con hasta 200 silos, pero puede enfrentar desafíos más allá de mil silos. En el caso de implementaciones muy grandes, otras partes de Orleans (mensajería, directorio de granos, alojamiento) permanecen escalables incluso si las actualizaciones de membresía se convierten en un cuello de botella.
configuración predeterminada: la configuración predeterminada se ha ajustado manualmente durante el uso de producción en Azure. De forma predeterminada: cada silo está supervisado por tres otros silos, dos sospechas son suficientes para declarar un silo muerto, sospechas solo de los últimos tres minutos (de lo contrario, están obsoletos). los sondeos se envían cada diez segundos y se necesitan tres sondeos para sospechar de un silo.
Autosupervisión: el detector de errores incorpora ideas de la investigación Lifeguard de Hashicorp (artículo, charla, blog) para mejorar la estabilidad del clúster durante eventos catastróficos en los que una gran parte del clúster experimenta un fallo parcial. El componente
LocalSiloHealthMonitor
puntúa el estado de cada silo mediante varias heurística:- Estado activo en la tabla de membresía
- No hay sospechas por parte de otros silos
- Respuestas de sondeo correctos recientes
- Solicitudes de sondeo recientes recibidas
- Capacidad de respuesta del grupo de subprocesos (elementos de trabajo que se ejecutan en menos de un segundo)
- Precisión del temporizador (activación dentro de los 3 segundos del horario programado)
La puntuación de salud de un silo afecta a sus tiempos de espera de sondeo: los silos no saludables (puntuación 1-8) tienen tiempos de espera prolongados en comparación con los silos saludables (puntuación 0). Esto tiene dos ventajas:
- Proporciona más tiempo para que los sondeos se realicen correctamente cuando la red o el sistema están bajo estrés
- Hace más probable que los silos incorrectos se voten como inactivos antes de que puedan votar incorrectamente silos correctos.
Esto es especialmente valioso durante escenarios como el agotamiento del grupo de subprocesos hilos, donde los nodos lentos podrían sospechar incorrectamente de nodos que están en buen estado, simplemente porque no pueden procesar las respuestas con la suficiente rapidez.
Sondeo indirecto: otra característica inspirada en Lifeguard que mejora la precisión de la detección de fallos al reducir la probabilidad de que un silo incorrecto o con particiones declare incorrectamente un silo correcto como inactivo. Cuando un silo de supervisión tiene dos intentos de sondeo restantes para un silo de destino antes de declararlo inactivo, emplea sondeos indirectos:
- El silo de supervisión selecciona aleatoriamente otro silo como intermediario y le pide que sondee el destino.
- El intermediario intenta ponerse en contacto con el silo de destino.
- Si el destino no responde dentro del período de tiempo de espera, el intermediario envía una confirmación negativa.
- Si el silo de supervisión recibe una confirmación negativa del intermediario y el intermediario se declara saludable (a través de la autosupervisión, descrita anteriormente), el silo de supervisión emite un voto para declarar muerto al objetivo.
- Con la configuración predeterminada de dos votos necesarios, un reconocimiento negativo de un sondeo indirecto cuenta como ambos votos, lo que permite una declaración más rápida de silos inactivos cuando varias perspectivas confirman el fallo.
Aplicación de la detección perfecta de fallos: una vez que un silo se declara inactivo en la tabla, se considera inactivo por todos, aunque no lo esté (simplemente está particionado temporalmente o se han perdido los mensajes de latido). Todo el mundo deja de comunicarse con él y una vez que está inactivo (al leer su estado nuevo de la tabla), se declara inactivo y cierra el proceso. Como resultado, tiene que haber una infraestructura en vigor para reiniciar el silo como en un proceso nuevo y se genera un nuevo número de época al iniciarse. Cuando se hospeda en Azure, se produce automáticamente. Cuando no es así, se requiere otra infraestructura, como un servicio de Windows configurado para reiniciar automáticamente cuando se produce un error o una implementación de Kubernetes.
Qué se puede hacer si no se puede acceder a la tabla durante cierto tiempo:
Si el servicio de almacenamiento está inactivo, no está disponible, o hay problemas de comunicación con este, el protocolo Orleans No declara los silos como inactivos por error. Los silos operativos seguirán funcionando sin problemas. Sin embargo, Orleans no podrá declarar un silo inactivo (si detecta que algún silo está muerto a través de sondeos perdidos, no podrá escribir este hecho en la tabla) y tampoco podrá permitir que se unan nuevos silos. Por lo tanto, la integridad se verá afectada, pero no así la precisión, la partición de la tabla nunca provocará que Orleans se declare un silo inactivo por error. Además, en el caso de una partición de red parcial (si algunos silos pueden acceder a la tabla y algunos no), podría ocurrir que Orleans se declare un silo muerto como inactivo, pero tardará algún tiempo hasta que todos los demás silos obtengan esta información. De este modo, la detección podría retrasarse, pero Orleans nunca declarará inactivo un silo erróneamente debido a una tabla no disponible.
Escrituras de IAmAlive para diagnósticos y recuperación ante desastres:
Además de los latidos que se envían entre los silos, cada silo actualiza periódicamente una marca de «Estoy activo» en su fila de la tabla. Esto sirve para dos propósitos:
- En el caso de los diagnósticos, proporciona a los administradores del sistema una manera sencilla de comprobar la vida del clúster y determinar cuándo estaba activo por última vez un silo. La marca de tiempo se actualiza normalmente cada 5 minutos.
- Para la recuperación ante desastres, si un silo no ha actualizado su marca durante varios períodos (configurada a través de
NumMissedTableIAmAliveLimit
), los nuevos silos la omitirán durante las comprobaciones de conectividad de inicio, lo que permite al clúster recuperarse de escenarios en los que los silos se bloquean sin una limpieza adecuada.
Tabla de pertenencia
Como ya se mencionó, IMembershipTable se usa como punto de encuentro para que los silos se encuentren entre sí y que los clientes de Orleans los encuentren y también para coordinar el acuerdo sobre la vista de pertenencia. El repositorio principal de Orleans contiene implementaciones para muchos sistemas, como Azure Table Storage, Azure Cosmos DB, PostgreSQL, MySQL/MariaDB, SQL Server, Apache ZooKeeper, Consul IO, Apache Cassandra, MongoDB, Redis, AWS DynamoDB y una implementación en memoria para el desarrollo.
Azure Table Storage: en esta implementación se usa el id. de implementación de Azure como clave de partición y la identidad del silo (
ip:port:epoch
) como clave de fila. En su conjunto garantizan una clave única por silo. Para controlar la simultaneidad, se usa el control de simultaneidad optimista basado en etiquetas ETag de tabla de Azure. Cada vez que se lee la tabla, se almacena la etiqueta ETag para cada fila de lectura que se usa cuando se intenta reescribir. Las etiquetas ETag se asignan y se comprueban automáticamente mediante el Servicio Azure Table en cada escritura. En el caso de las transacciones de varias filas, se usa la compatibilidad con las transacciones por lotes proporcionadas por la tabla de Azure para garantizar transacciones serializables en filas con la misma clave de partición.SQL Server: en esta implementación, el id. de implementación configurado se usa para distinguir las implementaciones y los silos que pertenecen a las implementaciones. La identidad del silo se define como una combinación de
deploymentID, ip, port, epoch
en las tablas y columnas correspondientes. El back-end relacional utiliza el control de simultaneidad optimista y las transacciones, de forma parecida a cómo se usan las ETags en la implementación de Azure Table. La implementación relacional espera que el motor de base de datos genere la ETag. En el caso de SQL Server, en SQL Server 2000, la ETag se obtiene a partir de una llamada aNEWID()
. En SQL Server 2005 y en sus versiones posteriores se usa ROWVERSION. Orleans lee y escribe ETag relacionales como etiquetasVARBINARY(16)
opacas y las almacena en memoria como cadenas codificadas en base64. Orleans admite inserciones de varias filas medianteUNION ALL
(para Oracle, incluido DUAL), que actualmente se usa para insertar datos de estadísticas. En CreateOrleansTables_SqlServer.sql se puede consultar la implementación y la justificación exactas de SQL Server.Apache ZooKeeper: en esta implementación se usa el id. de implementación configurado como un nodo raíz y la identidad del silo (
ip:port@epoch
) como su nodo secundario. En su conjunto garantizan una única ruta por silo. Para controlar la simultaneidad, se usa el control de simultaneidad optimista basado en la versión del nodo. Cada vez que se lee desde el nodo raíz de implementación, se almacena la versión de cada nodo de silo secundario de lectura y se usa esa versión cuando se intenta volver a escribir. Cada vez que se cambian los datos de un nodo, el servicio ZooKeeper aumenta automáticamente el número de versión. En el caso de las transacciones de varias filas, se usa el método múltiple, con el que se garantizan las transacciones serializables sobre nodos de silo con el mismo nodo de id. de implementación primario.Consul IO: se usa el almacén de valor o clave de Consul para implementar la tabla de pertenencia. Si desea más información, consulte Consul-Deployment.
AWS DynamoDB: en esta implementación, se usa el id. de implementación del clúster como la clave de partición e identidad del silo (
ip-port-generation
), como la RangeKey que hace la unidad del registro. El atributo realiza laETag
simultaneidad optimista mediante la realización de escrituras condicionales en DynamoDB. La lógica de implementación es bastante parecida a la de Azure Table Storage.Apacha Cassandra: en esta implementación se usa la composición de Id. de servicio e Id. de clúster como clave de partición y la identidad del silo (
ip:port:epoch
) como clave de fila. En su conjunto garantizan una fila única por silo. Para el control de concurrencia, usamos el control de concurrencia optimista basado en una versión de columna estática mediante una transacción ligera. Esta columna de versión se comparte para todas las filas en la partición o clúster, por lo que ofrece un número de versión que se incrementa de manera coherente para la tabla de pertenencia de cada clúster. No hay transacciones de varias filas en esta implementación.Emulación en memoria para la configuración del desarrollo. Usamos un tipo especial de grano para esa implementación. Este grano reside en un silo primario designado, usado solo en una configuración de desarrollo. En cualquier silo principal de uso real de producción no se necesita.
Justificación del diseño
Sería lógico preguntarse por qué no se depende completamente de Apache ZooKeeper o etcd para la implementación de la pertenencia del clúster, posiblemente usando la compatibilidad integrada de ZooKeeper para la pertenencia del grupo con nodos efímeros. ¿Por qué se implementó el protocolo de pertenencia? Fundamentalmente, por tres razones:
Implementación u hospedaje en la nube:
Zookeeper no es un servicio hospedado. Esto significa que los clientes de Orleans del entorno de nube tendrían que implementar, ejecutar o administrar su instancia de un clúster ZK. Se trataría de otra carga innecesaria, a la que no queríamos obligar a nuestros clientes. Al usar Azure Table, se depende de un servicio administrado y hospedado, lo que hace la vida de nuestros clientes más sencilla. Básicamente, en la nube, debe usarse la nube como plataforma y no como infraestructura. Por otra parte, al ejecutar localmente y administrar los servidores, una opción viable es depender de ZK Como una implementación de IMembershipTable.
Detección directa de errores:
Al usar la pertenencia a grupos de ZK con nudos efímeros, la detección de errores se realiza entre los servidores de Orleans (clientes ZK) y servidores ZK. Esto no significa necesariamente que haya problemas de red reales entre los servidores de Orleans. Pretendíamos que la detección de errores reflejara con precisión el estado interior del clúster de la comunicación. De forma específica, en nuestro diseño, si un silo de Orleans no se puede comunicar con el elemento IMembershipTable, no se considera inactivo y puede seguir funcionando. Por el contrario, si hubiéramos usado la pertenencia a grupos ZK con nodos efímeros, una desconexión de un servidor ZK podría provocar que un silo de Orleans (cliente ZK) fuera declarado inactivo, mientras que podría estar activo y ser completamente funcionando.
Portabilidad y flexibilidad:
Como parte de la filosofía de Orleans, no queremos obligar a depender de una tecnología en particular, sino que preferimos un diseño flexible en el que se puedan cambiar fácilmente distintos componentes con diferentes implementaciones. La abstracción IMembershipTable persigue exactamente este propósito.
Propiedades del protocolo de pertenencia
Poder controlar cualquier número de errores:
El algoritmo puede controlar cualquier número de errores (es decir, f<=n), en el que se incluye el reinicio completo del clúster. Contrasta con las soluciones basadas en Paxos tradicionales, que requieren quorum, que suele ser una mayoría. Se ha visto en situaciones de producción cuando más de la mitad de los silos estaban caídos. El sistema se mantuvo funcional y la pertenencia basada en Paxos no podía avanzar.
El tráfico a la tabla es muy ligero:
Los sondeos reales se desplazan directamente entre servidores y no van a la tabla. Se generaría una gran cantidad de tráfico, además de ser menos preciso desde la perspectiva de la detección de errores, si un silo no alcanzara la tabla, perdería escribir su latido activo de Estoy activo y otros lo desactivarían.
Comparación entre la precisión ajustable y la integridad:
Aunque no se puede conseguir que la detección de errores sea al mismo tiempo perfecta y precisa, normalmente se desea una capacidad de compensación de precisión (no se quiere declarar inactivo un silo que está activo) con integridad (se quiere declarar inactivo un silo que realmente está inactivo lo antes posible). Es posible negociar los dos aspectos mediante los votos configurables para declarar sondeos inactivos y perdidos. Si desea más información, lea Yale University: Computer Science Failure Detector, (Universidad de Yale: Detectores de errores informáticos).
Escala:
El protocolo puede controlar miles e incluso decenas de miles de servidores. Lo anterior contrasta con las soluciones tradicionales basadas en Paxos, como los protocolos de comunicación de grupo, que, como se sabe, no escalan cifras superiores a las decenas.
Diagnósticos:
La tabla también es muy adecuada para diagnosticar y solucionar problemas. Los administradores del sistema pueden encontrar instantáneamente en la tabla la lista actual de silos activos, además de ver el historial de todos los silos que se desactivaron y las sospechas. Resulta especialmente útil al diagnosticar problemas.
Razones para tener un almacenamiento persistente y seguro para implementarIMembershipTable:
Usamos el almacenamiento persistente para la IMembershipTable con dos fines. En primer lugar, se usa como punto de encuentro para que los silos se encuentren entre sí y los clientes de Orleans encuentren silos. En segundo lugar, se usa almacenamiento seguro para poder coordinar el acuerdo sobre la vista de pertenencia. Aunque la detección de errores se hace directamente de punto a punto entre los silos, se almacena la vista de pertenencia en un almacenamiento seguro y se usa el mecanismo de control de simultaneidad de este almacenamiento para llegar a un acuerdo de quién está activo y quién está inactivo. De esta forma, en cierto modo, el protocolo externaliza a la nube la difícil cuestión del consenso distribuido. Es este caso, se usa la eficacia de la plataforma de nube subyacente, usándola realmente como plataforma como servicio (PaaS).
IAmAlive directo escribe solo en la tabla para diagnósticos:
Además de los latidos que se envían entre los silos, cada silo también actualiza periódicamente una columna «Estoy activo» en su fila de la tabla. Esta columna «Estoy activo» solo se usa para solucionar problemas manualmente y hacer diagnósticos y no lo usa el propio protocolo de pertenencia. Se suele escribir con una frecuencia mucho menor (una vez cada cinco minutos) y sirve como una herramienta muy útil para que los administradores del sistema comprueben la ejecución del clúster o descubran fácilmente cuándo el silo estuvo activo por última vez.
Agradecimientos
Nos gustaría reconocer la contribución de Alex Kogan al diseño e implementación de la primera versión de este protocolo. Este trabajo formó parte de unas prácticas de verano en Microsoft Research en el verano de 2011.
La implementación de IMembershipTable basada en ZooKeeper se realizó mediante Shay Hazor, la implementación de SQL IMembershipTable se realizó mediante Veikko Eeva, la implementación de AWS DynamoDB IMembershipTable se realizó mediante Gutemberg Ribeiro y la implementación de la IMembershipTable basada en Consul fue realizada por Paul North, y finalmente la implementación del IMembershipTable apache Cassandra se adaptó de OrleansCassandraUtils
por Arshia001.