Sistemi distribuiti: quando è necessario crearli e come ridimensionarli. Una guida passo-passo.

Foto di Jeremy McKnight su Unsplash

Mi colpisce sempre il numero di sviluppatori junior che soffrono di sindrome di impostore quando hanno iniziato a creare il loro prodotto.

Capisco, ci sono molti esempi strabilianti delle migliori aziende con sistemi distribuiti incredibilmente complessi in grado di affrontare miliardi di richieste, aggiornare con garbo centinaia di applicazioni senza tempi di inattività, recuperare dal disastro in pochi secondi, rilasciare ogni 60 minuti e avere una velocità leggera tempi di risposta da qualsiasi parte del mondo.

Queste aspettative possono essere piuttosto travolgenti quando inizi il tuo progetto. Ma come molti di voi sanno già, la maggior parte di queste aziende ha iniziato con un sistema minimo praticabile e uno stack tecnologico molto scarso. C'è una semplice ragione per questo: non ne avevano bisogno quando hanno iniziato. Trascorrere più tempo nella progettazione del sistema anziché nella codifica potrebbe in effetti causare errori.

Questo articolo è un passo per passo come guidare. Ti mostrerò come, in Visage, abbiamo iniziato con il sistema più piccolo di sempre e creato un sistema distribuito scalabile ad alta disponibilità di base. Questo è un vero caso di studio per rimuovere i tuoi complessi se non hai mai avuto l'opportunità di farlo da solo.

Quando arrivai per la prima volta a Visage come CTO, ero l'unico ingegnere. Non sapevo nulla dello stack tecnologico, ma mi sono iscritto perché mi piaceva molto l'idea di poter reclutare senza reclutatori interni o un servizio risorse umane. Questa era l'idea alla base di Visage: crowdsourcing alimentato da molti reclutatori invisibili che lavorano insieme sui tuoi ruoli assistiti da intelligenza artificiale che cercherebbe il talento più adatto a te in pochi giorni. Quindi ti impegni direttamente con loro, nessun uomo di mezzo.

La "folla" nel crowdsourcing ha immediatamente innescato il mio cervello ingegneristico: ci saranno molte persone, che lavorano contemporaneamente, si aspettano buone prestazioni da qualsiasi parte del mondo. Mi è piaciuta la sfida.

Ma per quanto riguarda il sistema, le cose andavano male, davvero male. Questo è quello che ho trovato quando sono arrivato:

  • Un'istanza di Wordpress compromessa che esegue centinaia di plug-in difettosi obsoleti, in esecuzione in una macchina virtuale su un server condiviso
  • Cassette postali compromesse
  • Una tonnellata di documenti e fogli di lavoro Google.

E questo è perfettamente normale. Ancora una volta, non c'era un membro tecnico del team e mi aspettavo qualcosa di simile. Il team si era comunque concentrato su un'opportunità commerciale e ha fatto sembrare che il prodotto funzionasse magicamente mentre faceva tutto manualmente! (Fingi finché non ce la fai). Ed è quello che è stato davvero sorprendente.

Il nostro primo sistema (Sì, ha fatto schifo ma ha fatto il lavoro)!

Non sorprende che il mio primo compito sia stato quello di ricreare la VM, reinstallare una versione aggiornata di Wordpress, assicurarmi che tutti cambiassero le password, stabilire una politica delle password e rimuovere dozzine di malware dai computer dell'azienda ... ma passiamo alle considerazioni sui sistemi.

Da Wordpress a un'applicazione Web

Il tuo primo obiettivo quando inizi a costruire un prodotto devono essere i dati. I dati sono ciò che guida il valore della tua azienda. Sarà ciò che usi ogni giorno per prendere decisioni e ciò che mostri ai tuoi investitori per dimostrare progressi.

Devi dare un senso ai tuoi dati e recuperare i tuoi dati da fonti diverse con formati diversi sarà una grande perdita di tempo. Wordpress può essere un'ottima scelta in molti casi risparmiando parecchio tempo di ingegneria, ma per le loro esigenze, il team di Visage ha dovuto installare plugin fantasiosi che non venivano più mantenuti. Di conseguenza, non avevamo alcun controllo sul modello di dati generato e i dati che non potevano adattarsi al modello erano sparsi in dozzine di documenti e fogli di calcolo.

Quindi, a meno che non esista un prodotto che soddisfi già il 90% delle vostre esigenze, pensate a un modello di dati ideale e progettate e implementate un prodotto minimo sostenibile (MVP) che sarà in grado di contenere tutti i vostri dati.

Quindi pensa all'API. La tua applicazione deve avere un'API, sarà fondamentale quando alla fine la venderai. Non scalare immediatamente, ma codificare tenendo presente la scalabilità. Rendi la tua API senza stato e RESTful il più possibile poiché tutti si aspetteranno di essere in grado di interrogarla utilizzando metodi HTTP standard.

Abbiamo scelto NodeJS nel nostro caso, poiché la maggior parte del nostro codice elaborerebbe solo input e output. NodeJS non è bloccante e viene fornito con una libreria comoda per progettare API: ExpressJS.

Se hai bisogno di un sito web rivolto al cliente, hai diverse opzioni. Per prima cosa puoi creare un layer nel tuo server delle applicazioni che genererà le tue pagine oppure puoi creare un'applicazione Javascript a pagina singola che sarà servita da un server di web hosting statico.

In Visage abbiamo scelto la seconda opzione e abbiamo deciso di creare un'applicazione per gli utenti e una per gli amministratori. Questo semplicemente perché avremmo avuto aspettative molto più grandi per gli utenti di quelle di cui avevamo bisogno con gli amministratori e volevamo mantenere entrambe le basi di codice semplici (anche, per le considerazioni CORS in seguito). Ecco come appariva il nostro sistema:

Tutti i dati in un unico posto

Delegare l'archiviazione dei dati sensibili all'inizio

A meno che non sia fondamentale per la tua azienda, non vi è alcuna buona ragione per archiviare dati personali sensibili nei tuoi sistemi. La sicurezza è una questione complessa e se stai modificando il codice ogni giorno fino a quando non trovi il mercato del prodotto adatto, si romperà. Supponiamo che chiunque sia mal intenzionato potrebbe violare la tua domanda se lo desiderasse davvero.

La chiave qui è di non conservare alcun dato che sarebbe una rapida vittoria per un hacker. Nessuno ruba una banca che non ha soldi. Se stai progettando un prodotto SaaS, probabilmente avrai bisogno di autenticazione e pagamento online. Ci sono molte terze parti con cui puoi integrarti che lo affronteranno in un modo molto migliore di quello che potresti.

Auth0, ad esempio, è la terza parte più nota per gestire l'autenticazione. Stripe è anche una buona opzione per i pagamenti online. Dedicheranno tutte le loro risorse e i migliori team di ingegneria della sicurezza del pianeta per proteggere i tuoi dati, oppure non hanno un'attività commerciale.

Segno reale su un'auto a San Francisco

I servizi cloud sono i tuoi migliori amici

Quindi a questo punto abbiamo avuto modo di archiviare tutti i nostri dati, autenticazione, pagamento online e un'app Web che i clienti potevano utilizzare insieme a un'API che potevamo vendere ai partner per diversi casi d'uso. La nostra base di utenti stava crescendo ed è diventato ovvio che volevano poter accedere all'app in qualsiasi momento. Quindi era tempo di pensare alla scalabilità e alla disponibilità.

Facevamo affidamento su un server, ma poteva solo gestire così tante richieste e cambiare server o rilasciare una nuova versione avrebbe significato eliminare l'applicazione durante il rilascio. Le nostre priorità future erano: bilanciamento del carico, ridimensionamento automatico, registrazione, replica e backup automatici. Naturalmente, se sei l'unico ingegnere nella tua azienda, provare ad affrontare tutti questi problemi da solo sarebbe una follia completa.

Fortunatamente viviamo in un tempo in cui un solo ingegnere a tutto tondo può facilmente creare un tale sistema in un paio di giorni usando servizi cloud come Amazon Web Services, Google Cloud Services o Azure. Abbiamo deciso di spostare i nostri sistemi su AWS perché a quel tempo era la soluzione più completa e avevamo 2 anni di crediti gratuiti.

Questo è il motivo per cui parlerò principalmente delle soluzioni AWS in questo post, ma ci sono servizi equivalenti in altre piattaforme. Questo è anche il momento in cui abbiamo scelto di avviare l'esecuzione dei nostri moduli nei container Docker per molte altre ragioni che non saranno trattate in questo post (puoi leggere questo articolo per maggiori informazioni: https://medium.freecodecamp.org / amazon-Fargate-addio-infrastrutture-3b66c7e3e413).

Il modo in cui decidi di eseguire le tue applicazioni dipende davvero dal tuo caso d'uso, come la flessibilità di cui hai bisogno rispetto al tempo che puoi dedicare alla gestione della tua infrastruttura.

Non esiste una risposta buona o cattiva.

Puoi scegliere di containerizzare tutti i tuoi moduli e utilizzare un sistema di gestione dei container come ECS / EKS in AWS o motore Kubernetes in GCP. In caso contrario e non vuoi occuparti di cose come il ridimensionamento automatico e il bilanciamento del carico, puoi utilizzare Elastic Beanstalk o App Engine.

Se vuoi diventare completo Serverless puoi anche combinare l'uso delle funzioni Lambda e API Gateway. Abbiamo deciso di optare per ECS. Abbiamo distribuito 3 istanze in 3 zone di disponibilità, un bilanciamento del carico, impostato il ridimensionamento automatico in base all'utilizzo della CPU, integrato i registri di tutti i nostri contenitori con Cloudwatch e impostato le metriche per guardare errori, chiamate esterne e tempi di risposta dell'API.

Alta disponibilità: sapevi che le giraffe non dormono quasi mai? 99% di uptime

Per il nostro database, abbiamo usato MongoDB, perché il nostro modello è adatto per un database NoSQL e per la sua elevata coerenza. Abbiamo deciso di sfruttare MongoDB Atlas e distribuito 3 repliche per consentire l'alta disponibilità. Tra gli altri servizi, Atlas offre il ridimensionamento automatico, backup automatici e consente di tornare indietro nel tempo senza soluzione di continuità in caso di disastro.

Abbiamo anche deciso di ospitare tutti i nostri file web statici in S3 e abbiamo utilizzato Cloudfront come CDN in modo che le nostre app JS possano essere caricate molto rapidamente in qualsiasi parte del mondo ed essere servite tutte le volte che richiesto. Cloudflare è anche una buona opzione e offre una protezione DDOS pronta all'uso.

Per semplicità, abbiamo deciso di utilizzare Route 53 come DNS utilizzando i loro server dei nomi per tutti i nostri domini. Questo è uno dei miei servizi preferiti su AWS. Ti semplifica la vita. Ogni volta che vuoi servire qualcosa attraverso un nome di dominio, che si tratti di un'istanza EC2, un IP elastico, un bilanciamento del carico, una distribuzione Cloudfront o qualsiasi altra cosa, privatamente o pubblicamente, ti bastano pochi minuti perché è così ben integrato con tutto il altri servizi.

Combinalo con il Gestore certificati che ti consente di ottenere certificati SSL (caratteri jolly inclusi) gratuitamente in pochi minuti e di distribuirli su tutti i tuoi server spuntando una casella e avrai il modo più veloce e affidabile per abilitare HTTPS su tutti i tuoi moduli. Arrivederci "Let's Encrypt" certificati SSL che ho dovuto rinnovare e installare sui miei server ogni 3 mesi circa .

Iniziando a sembrare decente

Decidi una strategia di memorizzazione nella cache

Tutti odiano la gestione della cache, la memorizzazione nella cache può verificarsi in molti livelli diversi e i problemi relativi alla cache sono difficili da riprodurre e un incubo da debug.

Sfortunatamente, le prestazioni dei sistemi distribuiti dipendono fortemente da una buona strategia di memorizzazione nella cache. Ci sono molti buoni articoli su buone strategie di memorizzazione nella cache, quindi non entrerò nei dettagli. Sappi solo che se le tue risorse Web statiche sono pesanti, probabilmente vorrai sfruttare la cache del browser dell'utente utilizzando in modo intelligente l'intestazione di controllo della cache.

Se le pagine affiancate dell'utente vengono generate ripetutamente sui server delle applicazioni, utilizzare un proxy di memorizzazione nella cache come Squid. Ma soprattutto, c'è un'alta probabilità che tu faccia ripetutamente le stesse richieste al tuo database. Per ridurre il carico del database e risparmiare sui tempi di trasferimento dei dati, utilizzare un sistema di memorizzazione nella cache degli oggetti di memoria come memcached per oggetti che sono stati utilizzati di frequente e raramente aggiornati.

Abbiamo iniziato a prendere in considerazione l'utilizzo di memcached perché spesso abbiamo richiesto ripetutamente gli stessi profili candidati e le stesse offerte di lavoro. L'implementazione su una macchina ottimizzata per la memoria ha aumentato le prestazioni delle nostre API di oltre il 30% quando calcoliamo la media di tutti i tempi di risposta alle richieste in un giorno. Anche Memcached è distribuito, quindi può essere eseguito su server diversi ma agisce come se fosse solo un grande spazio di memoria per archiviare i tuoi oggetti.

cache, cache ovunque

Posizione, posizione, posizione

Ora abbiamo un sistema distribuito che non ha un singolo punto di errore (se si considerano ELB AWS e un memcached distribuito) e può ridimensionarsi automaticamente. Utilizziamo anche la memorizzazione nella cache per ridurre al minimo i trasferimenti di dati di rete. Sembra abbastanza buono A quel punto probabilmente vorrai controllare le tue terze parti per vedere se assorbiranno il carico così come te.

Tuttavia, alcuni dei nostri utenti si sono lamentati del fatto che l'app fosse un po 'più lenta per loro, soprattutto quando hanno caricato i file. In effetti, anche se i nostri file web statici sono stati memorizzati nella cache di tutto il mondo (per gentile concessione della CDN), tutti i nostri server applicazioni sono stati distribuiti solo nella parte occidentale degli Stati Uniti. Gli utenti dell'Asia orientale hanno sperimentato una latenza molto maggiore, in particolare per i trasferimenti di big data.

La soluzione è stata semplice: implementare lo stesso cluster ECS esatto in una nuova regione in Asia insieme a un nuovo bilanciamento del carico e fare affidamento sul routing geoproximity Route 53 per indirizzare gli utenti al bilanciamento del carico "più vicino". MongoDB Atlas ti consente anche di distribuire le tue repliche in diverse regioni, quindi non è necessario alcun lavoro aggiuntivo.

Ed eccoci qui! Il nostro sistema distribuito è pronto.

Conclusione

Mentre il sistema distribuito che vedi qui è stato semplificato per questo post, abbiamo esaminato le parti che molto probabilmente vedrai in molte applicazioni web moderne. Altri argomenti correlati ma non trattati sono l'architettura dei microservizi, l'archiviazione e la crittografia dei file, lo sharding del database, le attività pianificate, il calcolo parallelo asincrono ... forse nel prossimo post!

Il mio punto principale è: non provare a creare il sistema perfetto quando avvii il tuo prodotto. La maggior parte delle scelte progettuali sarà guidata da ciò che fa il tuo prodotto e da chi lo sta utilizzando. Saprai che quando raggiungerai il mercato del prodotto in forma e inizierai ad avere una buona panoramica della tua base di utenti, e ciò può richiedere anche mesi, anni.

Concentrati sul capire di cosa hanno bisogno le persone e prova a trovare una soluzione al loro problema, anche se ha molti passaggi manuali. Quindi pensa ai modi per automatizzare, dedicare il tuo tempo a programmare e distruggere e utilizzare terze parti dove ha senso.

Non ridimensionare, ma pensa sempre, codifica e pianifica il ridimensionamento. Costruisci il tuo sistema passo dopo passo, non affrontare i problemi di progettazione del sistema in base a funzionalità che non sono ancora maturi e infine cerca sempre di trovare il miglior compromesso tra il tempo che spenderai e il guadagno in termini di prestazioni, denaro e riduzione rischio.

Se ti è piaciuto questo articolo e ne hai trovato utile, premi quel pulsante di applauso e seguimi per ulteriori articoli su architettura e sviluppo!