Bijna elke succesvolle zakelijke applicatie komt vroeg of laat op een punt waarop horizontale schaalvergroting noodzakelijk is. In veel gevallen kan je eenvoudig een nieuwe instantie opstarten en de gemiddelde belasting verlagen. Maar er zijn ook minder triviale gevallen waarin we ervoor moeten zorgen dat verschillende nodes elkaar kennen en de werklast zorgvuldig verdelen.

Zo gelukkig is het gegaan dat erlang, dat we gekozen hebben voor de prettige syntaxis en de hype eromheen, heeft eersteklas . In theorie klinkt dit heel triviaal:
Berichten verzenden tussen processen op verschillende nodes en ook tussen links en monitors is transparant […]
In de praktijk is het iets ingewikkelder. Het gedistribueerde erlang werd ontwikkeld toen 'container' nog een grote metalen doos voor vervoer betekende en 'docker' gewoon een synoniem was voor een havensleepboot. In IP4 waren er veel ongebruikte adressen, netwerkonderbrekingen werden doorgaans veroorzaakt door knaagdieren die de kabel hadden doorgebeten, en de gemiddelde tijd tussen storingen van productiesystemen werd in decennia gemeten.
Nu zijn we allemaal onvoorstelbaar zelfvoorzienend, verpakt, en draaien een gedistribueerde erlang in een omgeving waar dynamische IP-adressen op basis van groot toeval worden uitgegeven, en nodes kunnen verschijnen en verdwijnen naar believen van de scheduler. Om een hoop sjablonencode in elk project dat een gedistribueerde erlangopstart, dat moet worden gebouwd tegen een onvriendelijke omgeving, te vermijden, is hulp vereist.
Opmerking: ik weet dat er is. Het is echt geweldig, het heeft meer dan duizend sterren, de auteur is bekend in de gemeenschap, enzovoorts. Als de geboden manieren om een cluster te creëren en te onderhouden voor jou voldoende zijn - ben ik blij voor je. Helaas heb ik veel meer nodig. Ik wil de configuratie gedetailleerd beheren en niet een onbewogen toeschouwer zijn in het theater van clusterherstructurering.
Vereisten
Wat ik persoonlijk nodig had, was een bibliotheek die het clusterbeheer op zich neemt en de volgende eigenschappen heeft:
- transparante werking met zowel een hardcoded lijst van nodes als dynamische detectie via services erlang;
- een volledig functionele callback bij elke verandering in de topologie (node daar, node hier, netwerkinstabiliteit, splitsingen);
- doorzichtige interface voor het opzetten van een cluster met lange en korte namen, net zoals met
:nonode@nohost; - ondersteuning voor Docker uit de doos, zonder dat je infrastructuurcode hoeft te schrijven.
Dit betekent dat nadat ik de applicatie lokaal heb getest in :nonode@nohost, of in een kunstmatig gedistribueerde omgeving met behulp van , wil ik gewoon uitvoeren docker-compose up --scale my_app=3 en zien hoe het drie instanties uitvoert in Docker zonder enige codewijzigingen. Ik wil ook dat afhankelijkheidsapplicaties, zoals mnesia — wanneer de topologie verandert, het cluster achter de schermen live opnieuw opbouwt zonder enige extra duw van de applicatie.
Cloister was niet bedoeld als een bibliotheek die alles kan: van clusteraansluiting tot koffiezetten. Het is geen zilveren kogel die probeert alle mogelijke scenario's te dekken of een academisch volledig antwoord te zijn in de zin die theoretici van CS aan deze term toekennen. Deze bibliotheek is bedoeld om een zeer duidelijk doel te dienen, maar haar relatief beperkte werk perfect uit te voeren. Dit doel is volledige transparantie te bieden tussen de lokale ontwikkelomgeving en de gedistribueerde elastische omgeving, vol vijandige containers.
De gekozen aanpak
Cloister is bedoeld om als applicatie te draaien, hoewel ervaren gebruikers het cluster handmatig kunnen opbouwen en onderhouden door Cloister.Manager uit te voeren in de boom van supervisoren van de doelsectie.
Bij het draaien als applicatie, vertrouwt de bibliotheek op -bestand, deze gegevens naar de server verzendt, waaruit de volgende basiswaarden worden gelezen:
config :cloister,
otp_app: :my_app,
sentry: :"cloister.local", # of ~w|n1@foo n2@bar|a
consensus: 3, # aantal knooppunten dat in overweging wordt genomen
# het cluster is operationeel
listener: MyApp.Listener # listener die wordt aangeroepen wanneer
# de ring is veranderdDe bovenstaande parameters betekenen letterlijk het volgende: Cloister wordt gebruikt voor een OTP-applicatie :my_app, maakt gebruik van erlang servicesdiscovering om ten minste drie knooppunten te verbinden en MyApp.Listener module (die ) is ingesteld om meldingen over topologiewijzigingen te ontvangen. Een gedetailleerde beschrijving van de volledige configuratie is te vinden in .
Met deze configuratie, kan de applicatie Cloister zal , waarbij het proces van het starten van de hoofdapplicatie wordt uitgesteld totdat er consensus is bereikt (drie knooppunten zijn verbonden en gekoppeld, zoals in het bovenstaande voorbeeld). Dit geeft de hoofdapplicatie de mogelijkheid om aan te nemen dat wanneer deze is gestart, het cluster al beschikbaar is. Bij elke wijziging in de topologie (er zullen er veel zijn, omdat de knooppunten niet volledig synchroon opstarten), zal een handler worden aangeroepen . In de meeste gevallen ondernemen we actie wanneer we een statusbericht ontvangen %Cloister.Monitor{status: :up}, wat betekent: 'Hallo, cluster is opgebouwd'.
In de meeste gevallen is het instellen van consensus: 3 optimaal, omdat zelfs als we verwachten dat meer knooppunten verbinding maken, de callback zal verlopen via status: :rehashing → status: :up op elke nieuwe toegevoegd of verwijderd knooppunt.
Bij het opstarten in de ontwikkelmodus, is het voldoende om gewoon consensus: 1 en Cloister verlaten, verheugd om de wachttijd voor het opbouwen van het cluster te omzeilen, bij het zien van :nonode@nohost, of :node@host, of :node@host.domain — afhankelijk van hoe het knooppunt is ingesteld (:none | :shortnames | :longnames).
Beheer van gedistribueerde applicaties
Gedistribueerde applicaties bevinden zich niet in een vacuüm en omvatten meestal ook gedistribueerde afhankelijkheden, zoals mnesia. We kunnen hun herconfiguratie gemakkelijk afhandelen vanuit dezelfde callback on_state_change/2. Hier is bijvoorbeeld een gedetailleerde beschrijving van hoe je mnesia on the fly kunt herconfigureren in .
Het belangrijkste voordeel van het gebruik van Cloister is dat het alle noodzakelijke operaties voor het herbouwen van het cluster uitvoert na een wijziging in de topologie onder de motorkap. De applicatie wordt gewoon gestart in een al voorbereide gedistribueerde omgeving, met alle aangesloten knooppunten, ongeacht of we de IP-adressen kennen en dus de namen van de knooppunten vooraf, of ze dynamisch zijn toegewezen/wijzigingen ondergaan. Dit vereist absoluut geen speciale configuratie-instellingen van Docker en vanuit het perspectief van de ontwikkelaar zijn er geen verschillen tussen opstarten in een gedistribueerde omgeving of lokaal op :nonode@nohost. Meer hierover kan worden gelezen in .
Ondanks dat complexe verwerking van topologiewijzigingen mogelijk is via een eigen implementatie MyApp.Listener, kunnen er altijd grensgevallen zijn waarin deze beperkingen van de bibliotheek en de bevooroordeelde benadering van configuratie een bottleneck vormen voor de implementatie. Dit is normaal, neem gewoon de bovenstaande libcluster, dat universeler is, of zelfs een laag-niveau cluster zelf te beheren. Het doel van deze codebibliotheek is niet om alle mogelijke scenario's te dekken, maar om het meest voorkomende scenario te gebruiken zonder onnodige pijn en omslachtige kopieer-en-plakacties.
Opmerking: op deze plaats stond in het origineel de zin ‘Happy clustering!’, en Yandex, die ik gebruik voor vertalingen (ik ga niet zelf woordenboeken doorzoeken), stelde me voor om ‘Vrolijk clusteren!’ te vertalen. Een betere vertaling is waarschijnlijk niet mogelijk, vooral gezien de huidige geopolitieke situatie.
Bron: habr.com
