Administración de clústeres en Orleans

Orleans proporciona administración de clústeres mediante un protocolo de pertenencia integrado, en ocasiones denominado pertenencia de silo. 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 del tipo No-SQL sin formato que se usa con 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. En este momento, hay seis implementaciones de IMembershipTable: basadas en Azure Table Storage, SQL Server, Apache ZooKeeper, Consul IO, AWS DynamoDB y emulació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. Para comenzar, se describirá la implementación interna del protocolo de pertenencia de Orleans y, luego, la implementación de IMembershipTable.

El protocolo de pertenencia básica

  1. Al iniciarse, cada silo agrega una entrada para sí mismo en una tabla compartida conocida, mediante una implementación de IMembershipTable. Una combinación de identidad de silo (ip:port:epoch) e id. de implementación de servicio se usa 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 que ip:port:epoch sea único en una implementación de Orleans determinada.

  2. Los silos se supervisan entre sí directamente, por medio de pings de aplicación («estás activo» heartbeats). Los pings se envían como mensajes directos de silo a silo, en los mismos sockets de TCP con los que se comunican los silos. De este modo, los pings se correlacionan de forma completa con los problemas de la red actuales y el estado del servidor. Cada silo ejecuta ping a un conjunto configurable de otros silos. Un silo elige a quién hacer ping 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).

  3. Si un silo S no obtiene Y respuestas de ping de los servidores P supervisados, lo sospecha escribiendo su sospecha con una marca de tiempo en la fila P de IMembershipTable.

  4. Si P tiene más de Z sospechas en un espacio de K segundos, S escribe después que P está inactivo en la fila de P y difunde una solicitud para que todos los silos vuelvan a leer la tabla de pertenencia, (que se hará periódicamente).

  5. Más información:

    1. 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».

    2. 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.

    3. El silo S que sospecha lee la fila de P.

    4. 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.

    5. De lo contrario, si S no es el último que sospecha, S simplemente se agrega a la columna de los que sospechan.

    6. 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).

    7. En un nivel elevado, esta secuencia de «lectura, modificación local, reescritura» es una transacción. Sin embargo, no se usan transacciones de almacenamiento con este objetivo. 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.

  6. 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.

  7. Configuración: se proporciona una configuración predeterminada, que se ajustó manualmente durante el uso de la producción en Azure. En este momento, el valor predeterminado es: tres silos supervisan cada silo, dos sospechas son suficientes para declarar un silo inactivo, sospechas solo de los tres últimos (de lo contrario, están obsoletos). Los pings se envían cada diez segundos y es necesario tres pings para sospechar de un silo.

  8. Aplicación de la detección de errores perfecta: en teoría es posible que un silo se declare inactivo si pierde la comunicación con otros solos, mientras el proceso de silo se está ajustando. Para resolver este problema una vez que el silo se declara inactivo en la tabla, todo el mundo lo considera inactivo, incluso si no está inactivo, (simplemente ha perdido mensajes de latido o se ha particionado temporalmente). 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 necesita otra infraestructura. Por ejemplo, un servicio de Windows configurado para reiniciarse automáticamente cuando se produce un error, o una implementación de Kubernetes.

  9. Optimización para reducir la frecuencia de lecturas de las tablas de forma periódica y agilizar todos los silos que obtienen información sobre los nuevos silos de unión y los silos inactivos. Cada vez que cada silo escribe cualquier cosa correctamente en la tabla (sospecha, nueva combinación, etc.), también difunde a todos los demás de silos: «vaya y vuelva a leer la tabla ahora». El silo No le dice a los otros que escribió en la tabla, (ya que está información podría estar ya obsoleta o ser incorrecta), simplemente le dice que vuelvan a leer la tabla. De este modo, se obtiene información muy rápidamente sobre los cambios de pertenencia sin necesidad de esperar el ciclo de lectura periódico completo. Todavía es necesaria una lectura periódica, en caso de que se pierda el mensaje «volver a leer la tabla».

Propiedades del protocolo de pertenencia básica

  1. 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.

  2. El tráfico a la tabla es muy ligero:

    Los pings 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.

  3. 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 pings inactivos y perdidos. Si desea más información, lea Yale University: Computer Science Failure Detector, (Universidad de Yale: Detectores de errores informáticos).

  4. Escala:

    El protocolo básico 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.

  5. 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.

  6. Razones para tener un almacenamiento persistente y seguro para implementarIMembershipTable:

    El almacenamiento persistente (tabla de Azure, SQL Server, AWS DynamoDB, Apache ZooKeeper o Consul IO KV) se usa en IMembershipTable con 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 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).

  7. 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 algún silo inactivo a través de pings perdidos, no podrá escribir este hecho en la tabla) y tampoco se 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.

  8. 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.

Extensión para ordenar las vistas de pertenencia

El protocolo de pertenencia básico descrito anteriormente se amplió posteriormente para admitir vistas de pertenencia ordenadas. Se describirán de forma resumida las razones de esta extensión y cómo se implementa. La extensión no cambia ningún aspecto del diseño anterior, simplemente agrega la propiedad que todas las configuraciones de pertenencia se ordenan globalmente.

Razones por las que conviene ordenar las vistas de pertenencia

  • Permite serializar la combinación de nuevos silos en el clúster. De este modo, cuando un nuevo silo se une al clúster, puede validar la conectividad bidireccional con cada otro silo ya iniciado. Si algunos de los silos que ya están unidos no responden, (lo que puede indicar un problema de conectividad de red con el nuevo silo), no se permite al nuevo silo unirse. Así se garantiza que al menos cuando se inicia un silo, se produce conectividad completa entre todos los silos del clúster (se implementa).

  • Los protocolos de nivel superior del silo, como un directorio de grano distribuido, pueden utilizar el hecho de que las vistas de pertenencia se ordenan y usan esta información para hacer una resolución de activaciones duplicadas más inteligente. En concreto, cuando el directorio detecta que se han creado dos activaciones cuando la pertenencia estaba en flujo, es posible que decida desactivar la activación anterior en función de una información de pertenencia que está obsoleta.

Protocolo de pertenencia extendida:

  1. Para implementar esta característica, se utiliza la compatibilidad para transacciones en varias filas que proporciona MembershipTable.

  2. Se agrega una fila de versión de pertenencia a la tabla que hace un seguimiento de los cambios en la tabla.

  3. Cuando el silo S quiere escribir la declaración de sospecha o inactividad del silo P:

    1. S lee el contenido más reciente de la tabla. Si P ya está inactivo, no hay que hacer nada. En caso contrario,
    2. en la misma transacción, se escriben los cambios en la fila de P, además de incrementar el número de versión y volver a escribirlo en la tabla.
    3. Las dos operaciones de escritura están condicionadas con ETags.
    4. Si la transacción se anula debido a un error de coincidencia en la ETag en la fila de P o en la fila de la versión, es necesario intentarlo de nuevo.
  4. Todas las escrituras de la tabla modifican e incrementan la fila de versión. De este modo, todas las escrituras en la tabla se serializan (mediante la serialización de las actualizaciones de la fila de versión) y, dado que los silos solo incrementan el número de versión, las escrituras también se ordenan totalmente en orden creciente.

Escalabilidad del protocolo de pertenencia ampliada:

En la versión ampliada del protocolo, todas las escrituras se serializan a través de una fila. Se puede dañar así potencialmente la escalabilidad del protocolo de administración de clústeres, debido a que aumenta el riesgo de conflictos entre las escrituras simultáneas de tablas. Para mitigar en parte este problema, los silos vuelven a intentar todas sus operaciones de escritura en la tabla mediante un retroceso exponencial. Se han observado protocolos ampliados funcionado sin contratiempos en un entorno de producción de Azure con hasta doscientos silos. Por otra parte, se cree que el protocolo podría presentar problemas al escalar más de mil silos. En las configuraciones de estas dimensiones, las actualizaciones de la fila de versión pueden deshabilitarse sin problemas, fundamentalmente manteniendo el resto del protocolo de administración del clúster y renunciando a la propiedad de ordenación total. Tenga en cuenta también que aquí nos referimos a la escalabilidad del protocolo de administración de clústeres, no a los demás componentes de Orleans. Se cree que otras partes del entorno de ejecución de Orleans (mensajería, directorio distribuido, hospedaje de granos, conectividad de cliente a puerta de enlace) se pueden escalar en una cifra muy superior a centenares de silos.

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. En este momento hay seis implementaciones disponibles de IMembershipTable: basadas en Azure Table, SQL Server, Apache ZooKeeper, Consul IO, AWS DynamoDB y emulación en memoria para el desarrollo.

  1. 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.

  2. 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 a NEWID(). En SQL Server 2005 y en sus versiones posteriores se usa ROWVERSION. Orleans lee y escribe ETag relacionales como etiquetas VARBINARY(16) opacas y las almacena en memoria como cadenas codificadas en base64. Orleans admite inserciones de varias filas mediante UNION 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.

  3. 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.

  4. 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.

  5. 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 la ETag 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.

  6. Emulación en memoria para la configuración del desarrollo. Usamos un grano especial del sistema, llamado MembershipTableGrain, 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.

Configuración

El protocolo de pertenencia se configura mediante el elemento Liveness de la sección Globals del archivo OrleansConfiguration.xml. A lo largo de los años del uso de producción en Azure, se ajustaron los valores predeterminados hasta convertirse en una buena configuración predeterminada. En general, no es necesario cambiarlos.

Elemento de configuración de ejemplo:

<Liveness ProbeTimeout="5s"
    TableRefreshTimeout="10s"
    DeathVoteExpirationTimeout="80s"
    NumMissedProbesLimit="3"
    NumProbedSilos="3"
    NumVotesForDeathDeclaration="2" />

Existen cuatro tipos de ejecución implementados. El tipo del protocolo de actividad se configura mediante el atributo SystemStoreType del elemento SystemStore de la sección Globals del archivo OrleansConfiguration.xml.

  1. MembershipTableGrain: la tabla de pertenencia se almacena en un grano en el silo principal. Se trata solo de una configuración de desarrollo.
  2. AzureTable: la tabla de pertenencia se almacena en la tabla de Azure.
  3. SqlServer: la tabla de pertenencia se almacena en una base de datos relacional.
  4. ZooKeeper: la tabla de pertenencia se almacena en un conjunto de ZooKeeper.
  5. Consul: se configura como un almacén de sistema personalizado con MembershipTableAssembly = "OrleansConsulUtils". Si desea más información, consulte Consul-Deployment.
  6. DynamoDB: se configura como un almacén de sistema personalizado con MembershipTableAssembly = "OrleansAWSUtils".

Con todos los tipos de ejecución, las variables de configuración frecuentes se definen en el elemento Globals.Liveness:

  1. ProbeTimeout: la cantidad de segundos para sondear sobre la actividad de otros silos o para que el silo envíe un mensaje de latido «Estoy activo» sobre sí mismo. El valor predeterminado es 10 segundos.
  2. TableRefreshTimeout: el número de segundos para capturar actualizaciones de la tabla de pertenencia. El valor predeterminado es 60 segundos.
  3. DeathVoteExpirationTimeout: el tiempo de expiración para el voto de inactividad en la tabla de pertenencia. El valor predeterminado es 120 segundos
  4. NumMissedProbesLimit: el número de mensajes de latidos «Estoy activo» perdidos de un silo o el número de sondeos sin respuesta que conducen a sospechar que el silo está inactivo. El valor predeterminado es 3.
  5. NumProbedSilos: el número de silos que sondea sobre la actividad de cada silo. El valor predeterminado es 3.
  6. NumVotesForDeathDeclaration: el número de votos no expirados necesarios para declarar un silo como inactivo (debe ser como máximo NumMissedProbesLimit). El valor predeterminado es 2.
  7. UseLivenessGossip: indica si se debe usar la optimización de chismes para acelerar la propagación de la información sobre actividad. El valor predeterminado es true.
  8. IAmAliveTablePublishTimeout: el número de segundos que escribe periódicamente en la tabla de pertenencia que el silo está activo. Se usa únicamente en diagnósticos. El valor predeterminado es 5 minutos.
  9. NumMissedTableIAmAliveLimit: el número de actualizaciones perdidas de «Estoy activo» en la tabla de un silo que causa el registro de una advertencia. No afecta al protocolo de actividad. El valor predeterminado es 2.
  10. MaxJoinAttemptTime: el número de segundos para intentar unirse a un clúster de silos antes de renunciar. El valor predeterminado es 5 minutos.
  11. ExpectedClusterSize: el tamaño esperado de un clúster. No es necesario ser muy preciso, puede ser una sobreestimación. Se usa para ajustar el algoritmo de retroceso exponencial de las veces que se ha intentado escribir en la tabla de Azure. El valor predeterminado es 20.

Justificación del diseño

Sería lógico preguntarse por qué no se depende completamente de Apache ZooKeeper para la implementación de la pertenencia del clúster, posiblemente usando su completamente integrada para la pertenencia del grupo con nodos efímeros. ¿Por qué se implementó el protocolo de pertenencia? Fundamentalmente, por tres razones:

  1. Implementación u hospedaje en la nube:

    Zookeeper no es un servicio hospedado, (al menos en el momento de escribir este documento en julio de 2015 y definitivamente cuando implementamos por primera vez este protocolo en el verano de 2011 no había ninguna versión de Zookeeper que se ejecutase como un servicio hospedado por un proveedor importante de nube). 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.

  2. 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.

  3. 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.

Agradecimientos

Nos gustaría agradecer la contribución de Alex Kogan en el diseño y la implementación de la primera de ese protocolo. Este trabajo formó parte de unas prácticas de verano en Microsoft Research en el verano de 2011. La implementación de ZooKeeper basada en IMembershipTable fue hecha por Shay Hazor, la implementación de SQL IMembershipTable fue realizada por Veikko Eeva, la implementación de AWS DynamoDB IMembershipTable fue realizada por Gutemberg Ribeiro y la implementación de Consul basada IMembershipTable es obra de Paul North.