Praticamente ogni applicazione aziendale di successo, prima o poi, entra in una fase in cui è necessario eseguire la scalabilità orizzontale. In molti casi, è possibile semplicemente avviare una nuova istanza e ridurre il carico medio. Ma ci sono anche casi meno banali in cui dobbiamo assicurarci che i nodi diversi conoscano l'uno dell'altro e distribuiscano correttamente il carico di lavoro.

È andata così bene che erlang, che abbiamo scelto per la sua sintassi piacevole e il clamore attorno, ha una prima classe . In teoria suona del tutto banale:
La trasmissione di messaggi tra processi su nodi diversi, così come tra riferimenti e monitor è trasparente […]
In pratica, tutto è un po' più complicato. Il sistema distribuito erlang è stato progettato quando "container" significava quella grande cassa di metallo per il trasporto, e "docker" era semplicemente sinonimo di scaricatore portuale. Nel IP4 c'erano molti indirizzi liberi, nei guasti di rete di solito erano i ratti che avevano rosicchiato i cavi a essere a fare danni, e il tempo medio di attività di un sistema produttivo si misurava in decenni.
Ora siamo tutti straordinariamente autosufficienti, impacchettati e facciamo girare un sistema distribuito erlang in un ambiente in cui gli indirizzi IP dinamici vengono assegnati secondo il grande caso, e i nodi possono apparire e scomparire a piacimento della sinistra piede del pianificatore. Per evitare una montagna di codice predefinito in ogni progetto che esegue un sistema distribuito, per affrontare un ambiente ostile, è necessario un aiuto. erlang: sono a conoscenza che esiste
Notalibcluster Ciò di cui avevo bisogno personalmente era una libreria che si prendesse cura della gestione del cluster e avesse le seguenti caratteristiche:
Requisiti
lavoro trasparente sia con un elenco di nodi codificato rigidamente che con la rilevazione dinamica tramite servizi
- callback completamente funzionale ad ogni cambiamento della topologia (nodo qui, nodo lì, instabilità di rete, split); erlang;
- callback completamente funzionale ad ogni modifica della topologia (nodo qui, nodo là, instabilità di rete, split);
- interfaccia trasparente per avviare un cluster con nomi lunghi e brevi, come e con
:nonode@nohost; - supporto Docker integrato, senza la necessità di scrivere codice infrastrutturale.
Questo significa che dopo aver testato l'applicazione localmente in :nonode@nohost, o in un ambiente artificialmente distribuito utilizzando , voglio semplicemente lanciare docker-compose up --scale my_app=3 e vedere come esegue tre istanze in Docker senza alcuna modifica al codice. Voglio anche che le applicazioni dipendenti, ad esempio mnesia — quando la topologia cambia, ricostruiscano il cluster in tempo reale senza alcun ulteriore input da parte dell'applicazione.
Cloister non è progettato come una libreria in grado di fare tutto: dal supporto del cluster alla preparazione del caffè. Non è una soluzione universale che cerca di coprire tutti i casi possibili o essere una soluzione accademicamente completa nel senso in cui i teorici di CS interpretano questo termine. Questa libreria è destinata a servire uno scopo molto chiaro, ma eseguire il proprio lavoro non troppo complesso in modo perfetto. Questo scopo consiste nel garantire una trasparenza totale tra l'ambiente di sviluppo locale e l'ambiente elastico distribuito, pieno di contenitori ostili.
L'approccio scelto
Cloister si prevede di avviare come applicazione, anche se gli utenti esperti possono lavorare con la costruzione e la manutenzione del cluster manualmente, avviando direttamente Cloister.Manager nell'albero dei supervisori dell'applicazione target.
Quando avviato come applicazione, la libreria si basa su config, da cui legge i seguenti valori di base:
config :cloister,
otp_app: :my_app,
sentry: :"cloister.local", # oppure ~w|n1@foo n2@bar|a
consensus: 3, # numero di nodi da considerare
# il cluster è attivo
listener: MyApp.Listener # listener da chiamare quando
# l'anello è cambiatoI parametri sopra significano letteralmente quanto segue: Cloister utilizzato per l'applicazione OTP :my_app, utilizza scoperta dei servizi Erlang per connettere i nodi, almeno tre, e MyApp.Listener modulo (che implementa ) è configurato per ricevere notifiche sui cambiamenti della topologia. Una descrizione dettagliata della configurazione completa può essere trovata in .
Con questa configurazione, l'applicazione Cloister si applicherà , rinviando il processo di avvio dell'app principale fino a raggiungere un consenso (tre nodi collegati e connessi, come nell'esempio sopra). Questo fornisce all'app principale la possibilità di presupporre che, quando è stata avviata, il cluster sia già disponibile. A ogni modifica della topologia (ce ne saranno molte, poiché i nodi non vengono avviati completamente in modo sincrono), verrà chiamato un gestore . Nella maggior parte dei casi eseguiamo un'azione quando riceviamo un messaggio con lo stato %Cloister.Monitor{status: :up}, il che significa: «pronto, il cluster è stato assemblato».
Nella maggior parte dei casi, impostare consensus: 3 è ottimale, perché anche se ci aspettiamo che si colleghino più nodi, il callback passerà attraverso status: :rehashing → status: :up su qualsiasi nodo nuovo aggiunto o rimosso.
Quando viene avviato in modalità di sviluppo, è sufficiente impostare consensus: 1 e Cloister salterà felicemente l'attesa per l'assemblaggio del cluster, vedendo :nonode@nohost, o :node@host, o :node@host.domain — a seconda di come è stato configurato il nodo (:none | :shortnames | :longnames).
Gestione delle applicazioni distribuite
Le applicazioni distribuite non esistono in un vuoto e di solito includono anche dipendenze distribuite, come mnesia. È facile gestirne la riconfigurazione dallo stesso callback on_state_change/2. Ecco, per esempio, una descrizione dettagliata di come riconfigurare mnesia al volo in .
Il principale vantaggio nell'utilizzo di Cloister è che esegue tutte le operazioni necessarie per la ricostruzione del cluster dopo una modifica della topologia sotto il cofano.L'applicazione viene semplicemente avviata in un ambiente distribuito già preparato, con tutti i nodi connessi, indipendentemente dal fatto che conosciamo gli indirizzi IP e, quindi, i nomi dei nodi in anticipo, o che siano stati assegnati/cambiati dinamicamente. Ciò non richiede alcuna configurazione speciale del Docker e, dal punto di vista dello sviluppatore di applicazioni, non c'è differenza tra l'esecuzione in un ambiente distribuito o in locale su :nonode@nohost. Maggiori informazioni su questo possono essere trovate in .
Sebbene una gestione complessa delle modifiche alla topologia sia possibile tramite una propria implementazione MyApp.Listener, potrebbero sempre esserci casi limite in cui queste limitazioni della libreria e un approccio parziale alla configurazione diventino un ostacolo all'implementazione. Va bene, basta prendere quanto sopra menzionato . È davvero fantastico, ha oltre mille stelle, l'autore è noto nella comunità, e così via. Se ti bastano i metodi offerti da questo pacchetto per creare e mantenere un cluster – sono felice per te. Purtroppo, io ho bisogno di molto di più. Voglio gestire la configurazione nei dettagli e non essere un semplice spettatore nel teatro di riforma del cluster., che è più universale, oppure gestisci autonomamente il cluster a basso livello. L'obiettivo di questa libreria di codice non è coprire tutti gli scenari possibili, ma utilizzare lo scenario più comune senza dolore inutile e ingombranti pratiche di copia e incolla.
Nota: in questo punto si trovava nella versione originale la frase «Happy clustering!», e Yandex, che utilizzo per la traduzione (non è certo il caso di cercare nei dizionari), mi ha proposto «Sfortunato raggruppamento!». Un migliore traduzione, soprattutto alla luce della situazione geopolitica attuale, è difficile da immaginare.
Fonte: habr.com
