Kloster → enkel OTP-klyngehåndtering

Nesten hver vellykket forretningsapplikasjon går før eller siden inn i en fase der horisontal skalering er nødvendig. I mange tilfeller kan du ganske enkelt starte en ny instans og redusere belastningsgjennomsnittet. Men det er også mindre trivielle tilfeller der vi må sørge for at ulike noder vet om hverandre og fordeler arbeidsmengden nøye.

Kloster → enkel OTP-klyngehåndtering

Det viste seg så heldig at erlang, som vi valgte for sin hyggelige syntaks og hype rundt den, har en førsteklasses støtte for distribuerte systemer. I teorien høres dette helt trivielt ut:

Meldingsoverføring mellom prosesser på forskjellige noder, så vel som mellom lenker og monitorer, er gjennomsiktig […]

I praksis er alt litt mer komplisert. Distribuert erlang ble utviklet da "container" betydde en stor jernkasse for frakt, og "docker" var ganske enkelt et synonym for longshoreman. I IP4 det var mange ledige adresser, nettverksbrudd var vanligvis forårsaket av rotter som tygget gjennom kabelen, og den gjennomsnittlige oppetiden til produksjonssystemet ble målt i flere tiår.

Nå er vi alle utrolig selvforsynt, pakket og kjører distribuert erlang i et miljø hvor dynamiske IP-adresser deles ut etter prinsippet om stor tilfeldighet, og noder kan dukke opp og forsvinne etter innfall av venstre hæl på planleggeren. For å unngå haugevis av standardkode i hvert prosjekt som kjører en distribuert erlang, for å bekjempe det fiendtlige miljøet, er det nødvendig med hjelp.

Note: Jeg er klar over at det er libcluster. Det er veldig kult, det har over tusen stjerner, forfatteren er kjent i samfunnet, og alt det der. Hvis metodene som tilbys av denne pakken for å opprette og vedlikeholde en klynge er nok for deg, er jeg glad i deg. Dessverre trenger jeg mye mer. Jeg ønsker å detaljstyre oppsettet og ikke være en ekstern tilskuer i teatret for klyngreorganisering.

Krav

Det jeg personlig trengte var et bibliotek som ville overta administrasjonen av klyngen og ville ha følgende egenskaper:

  • transparent arbeid med både en hardkodet liste over noder og dynamisk oppdagelse gjennom tjenester erlang;
  • fullt funksjonell tilbakeringing for hver topologiendring (node ​​der, node her, nettverksustabilitet, splittelser);
  • gjennomsiktig grensesnitt for å lansere en klynge med lange og korte navn, som med :nonode@nohost;
  • Docker-støtte ut av esken, uten å måtte skrive infrastrukturkode.

Det siste betyr at etter at jeg testet applikasjonen lokalt i :nonode@nohost, eller i et kunstig distribuert miljø ved hjelp av test_cluster_task, jeg vil bare løpe docker-compose up --scale my_app=3 og se hvordan den kjører tre forekomster i docker uten noen kodeendringer. Jeg vil også ha avhengige applikasjoner som mnesia - når topologien endres, bak kulissene bygger de om klyngen live uten noe ekstra kick fra applikasjonen.

Kloster var ikke ment å være et bibliotek med alt fra å støtte en klynge til å lage kaffe. Det er ikke en sølvkule som har som mål å dekke alle mulige saker, eller være en faglig komplett løsning i den forstand at teoretikere fra CS lagt inn i dette begrepet. Dette biblioteket er designet for å tjene et veldig klart formål, men gjør den ikke altfor store jobben perfekt. Dette målet vil være å gi fullstendig åpenhet mellom det lokale utviklingsmiljøet og et distribuert elastisk miljø fullt av fiendtlige containere.

Valgt tilnærming

Kloster er ment å kjøres som en applikasjon, selv om avanserte brukere kan arbeide med montering og vedlikehold av klyngen manuelt ved å kjøre direkte Cloister.Manager i målapplikasjonens veiledertre.

Når det kjøres som en applikasjon, er biblioteket avhengig av config, hvorfra den leser følgende grunnleggende verdier:

config :cloister,
  otp_app: :my_app,
  sentry: :"cloister.local", # or ~w|n1@foo n2@bar|a
  consensus: 3,              # number of nodes to consider
                             #    the cluster is up
  listener: MyApp.Listener   # listener to be called when
                             #    the ring has changed

Parametrene ovenfor betyr bokstavelig talt følgende: Kloster brukes til OTP-applikasjon :my_app, bruker erlang service discovery for å koble sammen noder, minst tre, og MyApp.Listener modul (implementering @behaviour Cloister.Listener) er konfigurert til å motta varsler om topologiendringer. En detaljert beskrivelse av den komplette konfigurasjonen finner du i dokumentasjon.

Med denne konfigurasjonen vil applikasjonen Kloster vil lansering i etapper, forsinke prosessen med å starte hovedapplikasjonen til konsensus er oppnådd (tre noder er koblet sammen og koblet sammen, som i eksempelet ovenfor.) Dette gir hovedapplikasjonen muligheten til å anta at når den starter, er klyngen allerede tilgjengelig. Hver gang topologien endres (det vil være mange av dem, fordi nodene ikke starter helt synkront), vil behandleren bli kalt opp MyApp.Listener.on_state_change/2. Mesteparten av tiden utfører vi en handling når vi mottar en statusmelding %Cloister.Monitor{status: :up}, som betyr: "Hei, klyngen er satt sammen."

I de fleste tilfeller, installasjon consensus: 3 er optimal fordi selv om vi forventer at flere noder kobles til, vil tilbakeringingen gå gjennom status: :rehashingstatus: :up på enhver ny lagt til eller fjernet node.

Når du starter i utviklingsmodus, trenger du bare å stille inn consensus: 1 и Kloster vil gjerne hoppe over ventetiden på klyngemontering når han ser :nonode@nohostEller :node@hostEller :[email protected] - avhengig av hvordan noden ble konfigurert (:none | :shortnames | :longnames).

Distribuert applikasjonsadministrasjon

Distribuerte applikasjoner som ikke er i et vakuum inkluderer vanligvis distribuerte avhengigheter, som f.eks mnesia. Det er enkelt for oss å håndtere rekonfigureringen deres fra samme tilbakeringing on_state_change/2. Her er for eksempel en detaljert beskrivelse av hvordan du rekonfigurerer mnesia på fly inn dokumentasjon Kloster.

Den største fordelen med å bruke Kloster er at den utfører alle nødvendige operasjoner for å gjenoppbygge klyngen etter en topologiendring under panseret. Applikasjonen kjører ganske enkelt i et allerede forberedt distribuert miljø, med alle noder tilkoblet, uavhengig av om vi kjenner IP-adressene og dermed nodenavnene på forhånd, eller de er dynamisk tildelt/endret. Dette krever absolutt ingen spesielle docker-konfigurasjonsinnstillinger, og fra en applikasjonsutviklers synspunkt er det ingen forskjell mellom å kjøre i et distribuert miljø eller å kjøre i et lokalt. :nonode@nohost. Du kan lese mer om dette i dokumentasjon.

Selv om kompleks håndtering av topologiendringer er mulig gjennom en tilpasset implementering MyApp.Listener, kan det alltid være edge-tilfeller der disse bibliotekbegrensningene og konfigurasjonsskjevhetene viser seg å være hjørnesteinene i implementeringen. Det er ok, bare ta ovenstående libcluster, som er mer generell, eller til og med håndtere lavnivåklyngen selv. Målet med dette kodebiblioteket er ikke å dekke alle mulige scenarioer, men å bruke det vanligste scenariet uten unødvendig smerte og tungvint copy-paste.

Merk: på dette tidspunktet i originalen var det uttrykket "Happy clustering!", og Yandex, som jeg oversetter med (jeg trenger ikke å gå gjennom ordbøker selv), tilbød meg alternativet "Happy clustering!" Det er kanskje umulig å tenke seg en bedre oversettelse, spesielt i lys av dagens geopolitiske situasjon.

Kilde: www.habr.com

Legg til en kommentar