Dela via


Klusterhantering i Orleans

Orleans tillhandahåller klusterhantering via ett inbyggt medlemskapsprotokoll, som vi ibland kallar klustermedlemskap. Målet med det här protokollet är att alla silor (Orleans servrar) ska komma överens om uppsättningen silor som för närvarande lever, identifiera misslyckade silor och tillåta att nya silor ansluter till klustret.

Protokollet förlitar sig på en extern tjänst för att tillhandahålla en abstraktion av IMembershipTable. IMembershipTable är en platt beständig tabell som vi använder för två syften. För det första används den som en mötesplats för silor för att hitta varandra och Orleans klienter för att hitta silor. För det andra används den för att lagra den aktuella medlemskapsvyn (lista över levande silor) och hjälper till att samordna avtalet om medlemskapsvyn.

Vi har för närvarande 6 implementeringar av IMembershipTable: baserat på Azure Table Storage, Azure Cosmos DB, ADO.NET (PostgreSQL, MySQL/MariaDB, SQL Server, Oracle), Apache ZooKeeper, Consul IO, AWS DynamoDB, MongoDB, Redis, Apache Cassandraoch en minnesintern implementering för utveckling.

Förutom varje IMembershipTable silo deltar i ett fullständigt distribuerat peer-to-peer-medlemskapsprotokoll som identifierar misslyckade silor och når en överenskommelse om en uppsättning levande silor. Vi beskriver den interna implementeringen av Orleansmedlemskapsprotokoll nedan.

Medlemskapsprotokollet

  1. Vid start lägger varje silo till en post för sig själv i en välkänd, delad tabell med hjälp av en implementering av IMembershipTable. En kombination av siloidentitet (ip:port:epoch) och tjänstdistributions-ID (kluster-ID) används som unika nycklar i tabellen. Epok är bara tid i fästingar när den här silon startade, och som sådan ip:port:epoch garanteras vara unik i en viss Orleans distribution.

  2. Silos övervakar varandra direkt via programavsökningar ("är du vid liv" heartbeats). avsökningar skickas som direktmeddelanden från silo till silo, över samma TCP-socketar som silor använder för att kommunicera. På så sätt korrelerar avsökningar fullständigt med faktiska nätverksproblem och serverhälsa. Varje silo avsöker en konfigurerbar uppsättning andra silor. En silo väljer vem som ska avsökas genom att beräkna konsekventa hashar på andra silos identiteter, bilda en virtuell ring med alla identiteter och välja X efterföljande silor på ringen (detta är en välkänd distribuerad teknik som kallas konsekvent hashning och används ofta i många distribuerade hash-tabeller, som Chord DHT).

  3. Om en silo S inte får Y antal svar från sonder från en övervakad server P, misstänker den det genom att skriva misstanken med tidsstämpel i P:s rad i IMembershipTable.

  4. Om P har fler än Z-misstankar inom K sekunder skriver S att P är död i P:s rad och skickar en ögonblicksbild av den aktuella medlemskapstabellen till alla andra silor. Silos uppdaterar tabellen regelbundet, så ögonblicksbilden är en optimering för att minska den tid det tar för alla silor att lära sig mer om den nya medlemskapsvyn.

  5. Mer information:

    1. Misstanke skrivs IMembershipTabletill , i en särskild kolumn på raden som motsvarar P. När S misstänker P skriver det: "vid tidpunkten TTT S misstänkt P".

    2. En misstanke räcker inte för att förklara P död. Du behöver Z-misstankar från olika silor i ett konfigurerbart tidsfönster T, vanligtvis 3 minuter, för att deklarera P som död. Misstanken skrivs med optimistisk samtidighetskontroll som tillhandahålls av IMembershipTable.

    3. Den misstänkte siloN läser P:s rad.

    4. Om S är den sista misstänkte (det har redan funnits Z-1 misstänkta inom en period av T, som skrivits i misstankekolumnen), beslutar S att förklara P som död. I det här fallet lägger S till sig själv i listan över misstänkta och skriver även i P:s statuskolumn att P är Död.

    5. Annars, om S inte är den sista misstänkte, lägger S bara till sig själv i den misstänktes kolumn.

    6. I båda fallen använder tillbakaskrivningen versionsnumret eller ETag som lästes, så uppdateringarna av den här raden serialiseras. Om skrivningen misslyckades på grund av att version/ETag inte matchar, försöker S igen (läs igen och försöker skriva, såvida inte P redan har markerats som död).

    7. På hög nivå är den här sekvensen med "read, local modify, write back" en transaktion. Men vi använder inte nödvändigtvis lagringstransaktioner för att göra det. "Transaktionskod" körs lokalt på en server och vi använder optimistisk samtidighet som tillhandahålls av IMembershipTable för att säkerställa isolering och atomitet.

  6. Varje silo läser regelbundet hela medlemskapstabellen för distributionen. På så sätt lär sig silor om nya silor som ansluter sig och om andra silor som förklaras döda.

  7. Snapshot broadcast: För att minska frekvensen för periodiska tabellläsningar skickar den varje gång en silo skriver till tabellen (misstanke, ny koppling osv.) en ögonblicksbild av det aktuella tabelltillståndet till alla andra silor. Eftersom medlemskapstabellen är konsekvent och monotont versionerad skapar varje uppdatering en unikt version av ögonblicksbilden som kan delas på ett säkert sätt. Detta möjliggör omedelbar spridning av medlemskapsändringar utan att vänta på den periodiska läscykeln. Den periodiska läsningen underhålls fortfarande som en återställningsmekanism om ögonblicksbildsdistributionen misslyckas.

  8. Ordnade medlemskapsvyer: Medlemskapsprotokollet säkerställer att alla medlemskapskonfigurationer är globalt helt ordnade. Den här beställningen ger två viktiga fördelar:

    1. Garanterad anslutning: När en ny silo ansluter till klustret måste den verifiera dubbelriktad anslutning till varannan aktiv silo. Om en befintlig silo inte svarar (vilket potentiellt indikerar ett problem med nätverksanslutningen) tillåts inte den nya silon ansluta. Detta säkerställer fullständig anslutning mellan alla silor i klustret vid start. Se anteckningen om IAmAlive nedan för ett undantag vid katastrofåterställning.

    2. Konsekventa kataloguppdateringar: Protokoll på högre nivå, till exempel katalogen distribuerade korn, förlitar sig på att alla silor har en konsekvent, monoton vy över medlemskap. Detta möjliggör smartare lösning av duplicerade kornaktiveringar. Mer information finns i dokumentationen grain directory.

    Detaljer om implementering:

    1. IMembershipTable kräver atomiska uppdateringar för att garantera en global total ändringsordning:

      • Implementeringar måste uppdatera både tabellposterna (lista över silor) och versionsnumret atomiskt
      • Detta kan uppnås med hjälp av databastransaktioner (som i SQL Server) eller atomiska jämförelse- och växlingsåtgärder med hjälp av ETags (som i Azure Table Storage)
      • Den specifika mekanismen beror på funktionerna i det underliggande lagringssystemet
    2. En särskild medlemsversionsrad i tabellen spårar ändringar:

      • Varje skrivoperation till tabellen (misstankar, dödsdeklarationer, anslutningar) ökar det här versionsnumret.
      • Alla skrivoperationer serialiseras genom denna rad med hjälp av atomiska uppdateringar
      • Den monotont ökande versionen garanterar en total ordning på alla medlemskapsändringar
    3. När silo S uppdaterar statusen för silo P:

      • S läser först det senaste tabelltillståndet
      • I en enda atomisk åtgärd uppdaterar den både P:s rad och ökar versionsnumret
      • Om atomuppdateringen misslyckas (t.ex. på grund av samtidiga ändringar) försöks åtgärden på nytt med exponentiell fördröjning

    skalbarhetsöverväganden:

    Serialisering av alla skrivningar via versionsraden kan påverka skalbarheten på grund av ökad konkurrens. Protokollet har visat sig pålitligt i produktion med upp till 200 silor, men kan stöta på utmaningar när antalet överstiger tusen silor. För mycket stora distributioner förblir andra delar av Orleans (meddelanden, kornkatalog, värd) skalbara även om medlemskapsuppdateringar blir en flaskhals.

  9. Standardkonfiguration: Standardkonfigurationen har handjusterats under produktionsanvändningen i Azure. Som standard: varje silo övervakas av tre andra silor, två misstankar räcker för att förklara en silo död, misstankar endast från de senaste tre minuterna (annars är de inaktuella). sonder skickas var tionde sekund och du skulle behöva missa tre avsökningar för att misstänka en silo.

  10. Självövervakning: Feldetektorn innehåller idéer från Hashicorps Lifeguard forskning (artikel, föredrag, blogg) för att förbättra klusterstabiliteten under katastrofala händelser där en stor del av klustret drabbas av partiella fel. Komponenten LocalSiloHealthMonitor poängsätter varje silos hälsa med hjälp av flera heuristiker:

    • Aktiv status i medlemskapstabellen
    • Inga misstankar från andra silor
    • Senaste lyckade provsvar
    • Nyligen mottagna avsökningsbegäranden
    • Svarstid för trådpool (arbetsuppgifter som utförs inom 1 sekund)
    • Timer noggrannhet (aktivering inom 3 sekunder enligt schemat)

    En silos hälsopoäng påverkar dess timeouter för avsökning: ohälsosamma silor (poäng 1-8) har ökad timeout jämfört med friska silor (poäng 0). Detta har två fördelar:

    • Ger mer tid för försök att lyckas när nätverket eller systemet är under stress
    • Gör det mer troligt att ohälsosamma silor kommer att röstas döda innan de felaktigt kan rösta ut friska silor

    Detta är särskilt värdefullt i scenarier som en resursbrist i trådpool, där långsamma noder annars felaktigt misstänker friska noder på grund av att de inte kan bearbeta svar tillräckligt snabbt.

  11. Indirekt avsökning: En annan Livräddare-inspirerad funktion som förbättrar noggrannheten för felidentifiering genom att minska risken för att en felaktig eller partitionerad silo felaktigt deklarerar en felfri silo död. När en övervakningssilo har två sondförsök kvar för en målsilo innan den röstar för att förklara den som död, använder den indirekt sondering.

    • Övervakningssilon väljer slumpmässigt en annan silo som mellanhand och ber den att avsöka målet
    • Mellanhanden försöker kontakta målsilon
    • Om målet inte svarar inom tidsgränsen skickar mellanhanden en negativ bekräftelse
    • Om övervakningssilon får ett negativt kvitto från mellanhanden och mellanhanden förklarar sig själv som felfri (genom självövervakning, som beskrivs ovan), röstar övervakningssilon för att förklara målet som dött.
    • Med standardkonfigurationen, där två röster krävs, räknas en negativ bekräftelse från en indirekt sondering som båda rösterna, vilket möjliggör en snabbare process för att förklara silor som döda när felet bekräftas från flera perspektiv.
  12. Framtvinga perfekt felidentifiering: När en silo har dödförklarats i tabellen anses den vara död av alla, även om den inte är död (bara partitionerade tillfälligt eller pulsslagsmeddelanden gick förlorade). Alla slutar kommunicera med det och när det lär sig att det är dött (genom att läsa dess nya status från tabellen) begår det självmord och stänger ner sin process. Därför måste det finnas en infrastruktur för att starta om silon som en ny process (ett nytt epoknummer genereras vid start). När den finns i Azure sker det automatiskt. När den inte är det krävs en annan infrastruktur, till exempel en Windows-tjänst som konfigurerats för automatisk omstart vid fel eller en Kubernetes-distribution.

  13. Vad händer om tabellen inte är tillgänglig under en tid:

    När lagringstjänsten är avstängd, otillgänglig eller om det finns kommunikationsproblem med den Orleans , deklarerar protokollet INTE silor som döda av misstag. Driftssilor fortsätter att fungera utan problem. Men Orleans kommer inte att kunna anmäla en silo som inaktiv (om den upptäcker att en silo är inaktiv genom missade sonderingar, kommer den inte att kunna skriva detta faktum till tabellen) och kommer inte heller att kunna tillåta nya silor att gå med. Så fullständighet kommer att drabbas, men noggrannhet kommer inte att påverkas — partitionering från tabellen kommer aldrig att orsaka Orleans att felaktigt deklarera silon som död. I händelse av en partiell nätverkspartition (om vissa silor kan komma åt tabellen och vissa inte), kan det hända att Orleans deklarera en död silo som död, men det tar lite tid tills alla andra silor lär sig om det. Så identifieringen kan fördröjas, men Orleans kommer aldrig felaktigt att döda en silo på grund av att tabellen inte är tillgänglig.

  14. IAmAlive skriver för diagnostik och katastrofåterställning:

    Förutom pulsslag som skickas mellan silon uppdaterar varje silo regelbundet en "I Am Alive"-tidsstämpel på sin rad i tabellen. Detta har två syften:

    1. För diagnostik ger den systemadministratörer ett enkelt sätt att kontrollera klustrets liveness och avgöra när en silo senast var aktiv. Tidsstämpeln uppdateras vanligtvis var femte minut.
    2. Om en silo inte har uppdaterat sin tidsstämpel under flera perioder (konfigurerad via NumMissedTableIAmAliveLimit) ignorerar nya silor den under startanslutningskontroller, vilket gör att klustret kan återställas från scenarier där silor kraschade utan korrekt rensning.

Medlemskapstabell

Som redan nämnts IMembershipTable används som en mötesplats för silor för att hitta varandra och Orleans klienter för att hitta silor och hjälper också till att samordna avtalet om medlemskapsvyn. Huvudlagringsplatsen Orleans innehåller implementeringar för många system, till exempel Azure Table Storage, Azure Cosmos DB, PostgreSQL, MySQL/MariaDB, SQL Server, Apache ZooKeeper, Consul IO, Apache Cassandra, MongoDB, Redis, AWS DynamoDB och en minnesintern implementering för utveckling.

  1. Azure Table Storage – i den här implementeringen använder vi Azure-distributions-ID som partitionsnyckel och siloidentiteten (ip:port:epoch) som radnyckel. Tillsammans garanterar de en unik nyckel per silo. För samtidighetskontroll använder vi optimistisk samtidighetskontroll baserat på Azure Table ETags. Varje gång vi läser från tabellen lagrar vi ETag för varje läsrad och använder den ETag när vi försöker skriva tillbaka. ETags tilldelas automatiskt och kontrolleras av Azure Table-tjänsten vid varje skrivning. För transaktioner med flera rader använder vi stödet för batchtransaktioner som tillhandahålls av Azure-tabellen, vilket garanterar serialiserbara transaktioner över rader med samma partitionsnyckel.

  2. SQL Server – i den här implementeringen används det konfigurerade distributions-ID:t för att skilja mellan distributioner och vilka silor som tillhör vilka distributioner. Siloidentiteten definieras som en kombination av deploymentID, ip, port, epoch i lämpliga tabeller och kolumner. Relationsserverdelen använder optimistisk samtidighetskontroll och transaktioner, liknande proceduren för att använda ETags i Azure Table-implementeringen. Relationsimplementeringen förväntar sig att databasmotorn genererar den ETag som används. När det gäller SQL Server är den genererade ETag på SQL Server 2000 en som hämtats från ett anrop till NEWID(). På SQL Server 2005 och senare används ROWVERSION . Orleans läser och skriver relations-ETags som ogenomskinliga VARBINARY(16) taggar och lagrar dem i minnet som base64-kodade strängar. Orleans stöder infogningar med flera rader med hjälp av UNION ALL (för Oracle inklusive DUAL), som för närvarande används för att infoga statistikdata. Den exakta implementeringen och logiken för SQL Server kan ses på CreateOrleansTables_SqlServer.sql.

  3. Apache ZooKeeper – i den här implementeringen använder vi det konfigurerade distributions-ID:t som en rotnod och siloidentiteten (ip:port@epoch) som dess underordnade nod. Tillsammans garanterar de en unik väg per silo. För samtidighetskontroll använder vi optimistisk samtidighetskontroll baserat på nodversionen. Varje gång vi läser från distributionsrotnoden lagrar vi versionen för varje läst underordnad silonod och använder den versionen när vi försöker skriva tillbaka. Varje gång en nods data ändras ökar versionsnumret atomiskt av ZooKeeper-tjänsten. För transaktioner med flera rader använder vi multimetoden, som garanterar serialiserbara transaktioner över silonoder med samma överordnade distributions-ID-nod.

  4. Konsul-I/O – vi använde Consuls nyckel-/värdearkiv för att implementera medlemskapstabellen. Mer information finns i Konsuldistribution.

  5. AWS DynamoDB – I den här implementeringen använder vi klustrets distributions-ID som partitionsnyckel och Silo-identitet (ip-port-generation) som RangeKey som gör postens enhet. Den optimistiska samtidigheten görs av ETag attributet genom att villkorliga skrivningar görs på DynamoDB. Implementeringslogik liknar Azure Table Storage.

  6. Apacha Cassandra – I den här implementeringen använder vi kompositen av tjänst-ID och kluster-ID som partitionsnyckel och siloidentiteten (ip:port:epoch) som radnyckel. Tillsammans garanterar de en unik rad för varje silo. För samtidighetskontroll använder vi optimistisk samtidighetskontroll baserat på en statisk kolumnversion med hjälp av en Lightweight Transaction. Den här versionskolumnen delas för alla rader i partitionen/klustret och ger det konsekventa inkrementella versionsnumret till varje klusters medlemstabell. Det finns inga transaktioner med flera rader i den här implementeringen.

  7. Minnesintern emulering för konfiguration av utveckling. Vi använder ett särskilt systemkorn för den implementeringen. Det här kornet finns på en utsedd primär silo, som endast används för en utvecklingskonfiguration. I någon verklig produktionsanvändning krävs inte primär silo.

Designmotivering

En naturlig fråga som kan ställas är varför inte helt förlita sig på Apache ZooKeeper eller etcd för implementeringen av klustermedlemskap, eventuellt med hjälp av ZooKeepers färdiga stöd för gruppmedlemskap med tillfälliga noder? Varför har vi brytt oss om att implementera vårt medlemskapsprotokoll? Det fanns främst tre orsaker:

  1. Distribution/värd i molnet:

    Zookeeper är inte en värdbaserad tjänst. Det innebär att kunder i molnmiljön Orleans måste distribuera/köra/hantera sin instans av ett ZK-kluster. Detta är bara ännu en onödig börda, som vi inte ville tvinga på våra kunder. Genom att använda Azure Table förlitar vi oss på en värdbaserad, hanterad tjänst som gör våra kunders liv mycket enklare. I molnet använder du i princip Molnet som en plattform, inte som en infrastruktur. Å andra sidan, när du kör lokalt och hanterar dina servrar, är det ett genomförbart alternativ att förlita sig på ZK som en implementering av IMembershipTable det.

  2. Direkt felidentifiering:

    När du använder ZK:s gruppmedlemskap med tillfälliga noder utförs felidentifieringen mellan servrarna (ZK-klienterna Orleans ) och ZK-servrarna. Detta kanske inte nödvändigtvis korrelerar med de faktiska nätverksproblemen mellan Orleans servrar. Vår önskan var att felidentifieringen korrekt skulle återspegla kommunikationens intraklustertillstånd. Mer specifikt, i vår design, om en Orleans silo inte kan kommunicera med den IMembershipTable anses inte vara död och kan fortsätta arbeta. I motsats till detta har vi använt ZK-gruppmedlemskap med tillfälliga noder som en frånkoppling från en ZK-server kan orsaka att en Orleans silo (ZK-klient) förklaras död, medan den kan vara levande och fullt fungerande.

  3. Portabilitet och flexibilitet:

    Som en del av Orleansfilosofin vill vi inte tvinga fram ett starkt beroende av någon viss teknik, utan snarare ha en flexibel design där olika komponenter enkelt kan växlas med olika implementeringar. Det här är precis syftet med IMembershipTable abstraktionen.

Egenskaper för medlemskapsprotokollet

  1. Kan hantera valfritt antal fel:

    Vår algoritm kan hantera valfritt antal fel (d.v.s. f<=n), inklusive fullständig omstart av klustret. Detta står i kontrast till "traditionella" Paxos-baserade lösningar, som kräver ett kvorum, vilket vanligtvis är en majoritet. Vi har sett i produktionssituationer när mer än hälften av silor var nere. Vårt system förblev funktionellt, medan Paxos-baserat medlemskap inte skulle kunna göra framsteg.

  2. Trafiken till tabellen är mycket lätt:

    De faktiska avsökningarna går direkt mellan servrarna och inte till tabellen. Detta skulle generera mycket trafik plus skulle vara mindre exakt ur felidentifieringsperspektivet - om en silo inte kunde nå tabellen skulle den missa att skriva dess I am alive heartbeat, och andra skulle döda honom.

  3. Justerbar noggrannhet jämfört med fullständighet:

    Även om du inte kan uppnå både perfekt och korrekt felidentifiering, vill man vanligtvis ha en förmåga att kompromissa med noggrannhet (vill inte deklarera en silo som lever som död) med fullständighet (vill förklara död en silo som verkligen är död så snart som möjligt). De konfigurerbara rösterna för att förklara döda och missade sonder tillåter handel mellan dessa två. Mer information finns i Yale University: Computer Science Failure Detectors.

  4. Skalning:

    Protokollet kan hantera tusentals och förmodligen till och med tiotusentals servrar. Detta står i kontrast till traditionella Paxos-baserade lösningar, till exempel gruppkommunikationsprotokoll, som är kända för att inte skalas längre än tiotals.

  5. Diagnostik:

    Tabellen är också mycket praktisk för diagnostik och felsökning. Systemadministratörerna kan omedelbart hitta den aktuella listan över levande silor i tabellen, samt se historien om alla dödade silor och misstankar. Detta är särskilt användbart när du diagnostiserar problem.

  6. Varför behöver vi tillförlitlig beständig lagring för implementeringen av IMembershipTable:

    Vi använder beständig lagring för IMembershipTable i två syften. För det första används den som en mötesplats för silor för att hitta varandra och Orleans klienter för att hitta silor. För det andra använder vi tillförlitlig lagring för att hjälpa oss att samordna avtalet om medlemskapsvyn. Vi utför felidentifiering direkt på peer-to-peer-sätt mellan silor, men vi lagrar medlemskapsvyn i tillförlitlig lagring och använder mekanismen för samtidighetskontroll som tillhandahålls av den här lagringen för att nå en överenskommelse om vem som lever och vem som är död. På så sätt outsourcar vårt protokoll det hårda problemet med distribuerad konsensus till molnet. I och med att vi fullt ut använder kraften i den underliggande molnplattformen använder vi den verkligen som PaaS (Platform as a Service).

  7. Direkt IAmAlive skriver till tabellen endast för diagnostik:

    Förutom pulsslag som skickas mellan silon uppdaterar varje silo också regelbundet en "I Am Alive"-kolumn på sin rad i tabellen. Den här kolumnen "I Am Alive" används endast för manuell felsökning och diagnostik och används inte av själva medlemskapsprotokollet. Det skrivs vanligtvis med mycket lägre frekvens (en gång var 5:e minut) och fungerar som ett mycket användbart verktyg för systemadministratörer för att kontrollera klustrets livskraft eller enkelt ta reda på när silon senast levde.

Erkännanden

Vi vill uppmärksamma Alex Kogans bidrag till utformningen och implementeringen av den första versionen av det här protokollet. Detta arbete gjordes som en del av en sommarpraktik i Microsoft Research sommaren 2011. Implementeringen av ZooKeeper-baserade IMembershipTable gjordes av Shay Hazor, implementeringen av SQL IMembershipTable gjordes av Veikko Eeva, implementeringen av AWS DynamoDB IMembershipTable gjordes av Gutemberg Ribeiro och genomförandet av konsul baserad IMembershipTable gjordes av Paul North, och slutligen var implementeringen av Apache Cassandra IMembershipTable anpassad från OrleansCassandraUtils av Arshia001.