Liberarsi dal monolito

E come ci siamo evoluti con Microservices

(19 luglio 2018)

Un monolito che governa le scimmie del codice

Al Conio tutto è iniziato con quello che comunemente viene chiamato “Monolite”. Cioè, una singola base di codice contenente tutto dellapplicazione completa: i suoi componenti frontend, componenti backend, servizi API, attività in background; diavolo, anche gli script devops. E ha funzionato molto bene allinizio. Solo un paio di ingegneri del software, che lavorano su aree separate del codice base (quindi poche possibilità di conflitti di modifica del codice), facili da distribuire. Concentrazione totale sulla scrittura delle funzionalità dellapplicazione, senza preoccuparsi di molto altro. Come ci siamo avvicinati alla distribuzione? Con solo pochi clienti beta consapevoli dei progressi continui, non era un vero problema chiudere i servizi per un po , implementare la base di codice completa (non importa quanto piccole o grandi fossero le modifiche generali e se includessero migrazioni del database), e poi riproporre i servizi.

È stata sicuramente una soddisfazione vedere un prodotto prendere forma da zero e ricevere apprezzamenti dai clienti finali. Tuttavia, sapevamo molto bene che questo approccio non è adatto a una moderna azienda Fintech.

E allora?

Con la maggior parte delle applicazioni software, i clienti sono abbastanza tolleranti. Sì, Whatsapp potrebbe smettere di funzionare e avere uninterruzione che dura per alcune ore: sicuramente un fastidio, ma non un problema percepito. Lo stesso vale per Pokemon Go o la tua app di gioco preferita. Tuttavia, non è così quando si tratta di denaro : cambia lumore se non riesci ad accedere al tuo conto bancario o non sei in grado di farlo fare operazioni commerciali. Questo è anche peggio nel caso delle applicazioni di criptovaluta: la maggior parte delle persone ricorda i famigerati errori del passato e ogni volta che non sono in grado di accedere ai propri fondi di criptovaluta anche per un breve periodo di tempo, sorgono speculazioni. È giusto. Sono i tuoi soldi e dovresti avere pochi o nessun problema quando vuoi usarlo.

Il monolito sopra non è adatto per uno scenario del genere: qualsiasi modifica alla base di codice in produzione richiederebbe una distribuzione completa, con tempi di inattività associati. Ogni giorno lavoriamo per migliorare i nostri servizi correggendo bug, rendendo la nostra interfaccia ancora più amichevole, rimuovendo vecchie funzionalità e aggiungendone di nuove che hanno un migliore utilizzo. Spesso rilasciamo questi aggiornamenti su base giornaliera in modo che i nostri clienti possano trarne vantaggio immediato e ci sforziamo di non avere alcun impatto sullesperienza del cliente. Cioè, qualunque sia la formula che inventiamo dietro le quinte, deve essere invisibile al mondo esterno (almeno, la maggior parte delle volte). Quindi, ci siamo allontanati da Monolith e abbiamo scelto quella che comunemente viene chiamata “architettura dei microservizi”.

Evoluzione attraverso i microservizi

Lenorme base di codice singola strettamente incollata è ora scomposta in parti più piccole, ognuna delle quali rappresenta un servizio particolare. Ogni volta che vengono eseguiti, i servizi comunicano tra loro in modo sincrono tramite protocollo HTTP standard e in modo asincrono tramite code (gestite ad esempio da RabbitMQ e Apache Kafka).

Interazioni in unarchitettura di microservizi

È piuttosto difficile iniziare a suddividere il monolite in componenti più piccoli, ma ne vale davvero la pena lo sforzo. In termini militari, è molto simile a ciò che fece Giulio Cesare per governare costantemente il vasto territorio della Gallia: “ dividi e conquista “.

1) Il prodotto può essere distribuito continuamente. Un aggiornamento del codice ora si applica solo a un microservizio: nella maggior parte dei casi può essere immediatamente distribuito in produzione e rilasciato senza alcun impatto per il cliente

2) Il codice è più facile da gestire. Dal punto di vista dellorganizzazione aziendale, le cose cambiano quando un team di 2 ingegneri del software diventa un team di 10 ingegneri del software. È più efficace e con meno conflitti di codice quando ogni membro del team è responsabile del proprio microservizio.

3) Il codice è più facile da mantenere. Unarchitettura Microservices richiede, per sua natura, la definizione di uninterfaccia per comunicare con il mondo esterno (sia essa lapp di frontend o un altro servizio di backend) ed è completamente isolata da ogni altro punto di vista. Ciò consente di rivedere, riprogettare o persino riscrivere completamente da zero (anche in lingue diverse se conveniente) singoli componenti dellapplicazione senza influire sul resto.

4) Le prestazioni possono essere migliorate. Ogni microservizio può ora utilizzare il proprio linguaggio più appropriato. I componenti di calcolo crittografico pesanti possono ad esempio essere ottimizzati in C, mentre i servizi API in Python e le attività a esecuzione prolungata in Go.

5) Isolamento e sicurezza del codice migliorati. Ogni microservizio può essere eseguito nel proprio container Docker, fornendo così isolamento dei privilegi, separazione della rete e dei dati e, di fondamentale importanza per una fase di crescita, un enorme potenziale di scalabilità.

I microservizi sono la risposta allora?

Ovviamente non esiste un pranzo gratis. Unarchitettura Microservices presenta anche una propria serie di sfide impegnative:

1) Complessità operativa. Gli ingegneri DevOps sono assolutamente necessari per smussare le complessità del nuovo processo di distribuzione.

2) Hardware gonfio. I microservizi vengono spesso eseguiti in contenitori Docker; non appena il numero di microservizi prolifera, diventa sempre più difficile eseguire lapplicazione completa sullo stesso hardware di prima.

3) Overhead di intercomunicazione: ogni richiesta potrebbe dover interagire con una o più differenti microservizi attraverso la rete. Ciò potrebbe causare una maggiore latenza e potrebbe essere soggetto a guasti temporanei. Per implementare servizi resilienti e migliorare la scalabilità dellintero sistema, è necessario spostare le interazioni verso la messaggistica asincrona (es. Utilizzando Apache Kafka e / o RabbitMQ)

4) Consistenza finale. Questa è probabilmente la sfida più difficile di unarchitettura Microservices. Dato un singolo microservizio, è possibile creare transazioni RDBMS entro i suoi confini . Sfortunatamente, tuttavia, un problema comune nelle architetture distribuite è quello di gestire più transazioni che non rientrano negli stessi confini. Di conseguenza, il sistema potrebbe finire in uno stato illegale e irrecuperabile. Per mitigare tali problemi, Conio adotta diverse strategie:

  1. Seguendo le pratiche del Domain Driven Design, scomporre i domini di livello superiore in sottodomini e confinarli in contesti limitati ; ogni contesto delimitato viene implementato come microservizio, in cui vengono applicati i limiti delle transazioni. Questo risolve la possibilità di avere incongruenze per sottodomini specifici.
  2. Implementa interazioni asincrone idempotenti, che prima o poi risolvono le incongruenze.
  3. Quando possibile, evita qualsiasi azione che potrebbe coinvolgere più sottodomini.

5) Rapporti complessi. Poiché ogni sottodominio risiede allinterno di uno specifico contesto delimitato, report complessi che coinvolgono più sottodomini potrebbero richiedere di interrogare dati da più origini dati: ciò potrebbe avere un impatto negativo sia sullespressività dei domini che sulla scalabilità del sistema. Qui in Conio abbiamo adottato unarchitettura CQRS per supportare lattività di backoffice e i rapporti di analisi aziendale.

6 ) Sistema di registrazione. Ogni elemento in un sistema distribuito contribuisce alla creazione del registro dellintero sistema. Tuttavia, è necessario implementare strumenti in grado di creare le connessioni necessarie tra tutti questi registri separati per avere un registro unificato per ogni interazione. Qui in Conio utilizziamo lo stack ELK (ElasticSearch, Logstash, Kibana) per archiviare e interrogare i dati di log: ogni log è arricchito con gli id ​​di correlazione necessari che consentono il log unificato sopra menzionato.

Non fermare mai levoluzione

La nostra opinione? La scomposizione della singola base di codice iniziale deve essere vista come unattività a lungo termine , con perfezionamenti continui. In Conio ci sono voluti un paio danni in cui, passo dopo passo, siamo passati da una massiccia base di codice a oltre 100 microservizi. Siamo arrivati ​​a un punto in cui ci sentiamo orgogliosi dei risultati, ma allo stesso tempo continuiamo a esplorare. Sono possibili molteplici nuove ottimizzazioni: passare da Docker Swarm a Kubernetes? Migrazione di servizi backend per frontend a funzioni lambda serverless? Stai passando a un flusso operativo di distribuzione continua completo? Le possibilità sono infinite.

Qui abbiamo toccato una serie di argomenti e tecnologie. Nei prossimi articoli condivideremo maggiori dettagli sui nostri risultati e progressi. Se vuoi, non esitare a commentare e raccontarci la tua esperienza.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *