Come creare microservizi
Best practice per la transizione a un'architettura di microservizi
Sten Pittet
Product manager
Supponiamo che la tua applicazione si basi su un unico codice e che sia monolitica e di dimensioni piuttosto grandi. Ma adesso le sue prestazioni non sono più all'altezza delle esigenze. Vorresti che diventasse più resiliente, scalabile e distribuibile in modo indipendente. Per farlo, devi ripensare la struttura dell'applicazione a un livello granulare di microservizi.
Con l'aumento della complessità e della distribuzione delle applicazioni, i microservizi hanno acquisito sempre più popolarità. Il principio guida dei microservizi consiste nel creare un'applicazione suddividendone i componenti aziendali in servizi piccoli che possono essere distribuiti e gestiti indipendentemente l'uno dall'altro. La separazione degli interessi tra i servizi è definita "limite di servizio".
I limiti di servizio sono strettamente legati alle esigenze aziendali e ai confini gerarchici dell'organizzazione I singoli servizi possono essere legati a roadmap, budget e team separati. Alcuni esempi di limiti di servizio possono essere i servizi di "elaborazione dei pagamenti" e di "autenticazione utenti". I microservizi differiscono dalle pratiche di sviluppo software legacy perché tutti i componenti sono raggruppati insieme.
In questo documento verrà fatto riferimento a una pizzeria startup immaginaria, chiamata "Pizzup", per illustrare l'applicazione dei microservizi in un'azienda software moderna.
Prova Compass gratis
Migliora la tua esperienza di sviluppatore, cataloga tutti i servizi e aumenta l'integrità del software.
Come creare microservizi
Passaggio 1: Inizia con un monolite
La prima best practice relativa ai microservizi è che probabilmente non ti servono. Se non disponi di utenti per l'applicazione, è molto probabile che i requisiti aziendali cambieranno rapidamente mentre è in corso la creazione del tuo MVP. Ciò è dovuto semplicemente alla natura dello sviluppo del software e al ciclo di feedback che deve verificarsi durante l'identificazione delle funzionalità aziendali chiave che devono essere fornite dal sistema. I microservizi aggiungono sovraccarico e complessità di gestione esponenziali. Per questo motivo, nel caso dei nuovi progetti, mantenere tutto il codice e la logica all'interno di una sola base di codice comporta un sovraccarico notevolmente inferiore e rende più semplice spostare i confini dei diversi moduli dell'applicazione.
Ad esempio, con Pizzup iniziamo con un problema semplice da risolvere: vogliamo offrire ai clienti la possibilità di ordinare la pizza online.
materiale correlato
Microservizi e architettura monolitica a confronto
Scopri la soluzione
Migliora l'esperienza di sviluppo con Compass
Iniziando ad esaminare il problema dell'ordinazione della pizza riusciamo a individuare le varie funzionalità di cui deve disporre l'applicazione per poter soddisfare questa esigenza. Sarà necessario gestire un elenco delle pizze tra cui scegliere, consentire ai clienti di selezionare una o più pizze, gestire i pagamenti, programmare la consegna e così via. Potremmo stabilire che se i clienti creassero un account sarà più semplice fare un nuovo ordine la prossima volta che usano Pizzup. Dopo aver parlato con i primi utenti, potremmo renderci conto che il tracciamento in tempo reale della consegna e il supporto dei dispositivi mobili potrebbero avvantaggiarci rispetto alla concorrenza.
Ciò che inizialmente era una semplice necessità, si è trasformato rapidamente in un elenco di nuove funzioni.
I microservizi funzionano bene se si conoscono a fondo i diversi servizi necessari per il sistema. Tuttavia, i microservizi sono molto più difficili da gestire se i requisiti di base di un'applicazione non sono ben definiti. È piuttosto costoso ridefinire le interazioni tra i servizi, le API e le strutture dei dati nei microservizi, dal momento che in genere vi sono molte più parti in movimento da coordinare. Noi consigliamo di mantenere le cose semplici finché non si sono raccolti sufficienti feedback degli utenti per essere sicuri di aver compreso le necessità di base dei clienti e aver programmato il lavoro in funzione di queste.
Attenzione: compilare un monolite può portare rapidamente a un codice complicato difficile da suddividere in porzioni più piccole. È meglio identificare moduli chiari da poter estrarre in un secondo momento dal monolite. Puoi inoltre iniziare separando la logica dall'interfaccia utente web e assicurandoti che interagisca con il back-end tramite un'API RESTful su HTTP. In questo modo, la transizione ai microservizi è più semplice quando sposti le risorse API su servizi diversi.
Passaggio 2: Organizza i team nel modo giusto
Fino ad ora, può sembrare che la creazione dei microservizi sia un'attività esclusivamente tecnica. Occorre suddividere la base di codice in più servizi, implementare gli schemi giusti per limitare le conseguenze degli errori e ripristinare i problemi di rete, gestire i dati con coerenza, monitorare il carico del servizio e così via. I nuovi concetti da apprendere sono molti. Ma probabilmente l'unico aspetto da non ignorare è che occorre ristrutturare il modo in cui sono organizzati i team.
La legge di Conway è reale ed è possibile osservarla in tutti i tipi di team. Se un team software è organizzato con un team di back-end, un team di front-end e un team delle operazioni che lavorano in modo indipendente, rilascerà monoliti di front-end e back-end separati che verranno passati al team delle operazioni che si occuperà del rilascio nell'ambiente di produzione. Questo tipo di struttura dei team non è adatto ai microservizi, perché ogni servizio dovrebbe essere trattato come un singolo prodotto da rilasciare indipendentemente dagli altri.
Invece, occorre creare team DevOps più piccoli che abbiano tutte le competenze necessarie per sviluppare e gestire i servizi di cui sono responsabili. Strutturare i team in questo modo comporta enormi vantaggi. Innanzitutto, tutti gli sviluppatori comprenderanno meglio l'impatto del codice nell'ambiente di produzione, questo li aiuterà a produrre rilasci migliori riducendo il rischio che si verifichino problemi nei rilasci per i clienti. In secondo luogo, le distribuzioni diventano naturali per ogni team, dal momento che questi collaborano sui miglioramenti al codice oltre che sull'automazione della pipeline di distribuzione.
Passaggio 3: Suddividi il monolite per creare un'architettura di microservizi
Dopo aver identificato i limiti dei servizi e aver capito come ristrutturare i team, puoi iniziare a suddividere il monolite per creare i microservizi. Di seguito sono riportati i punti chiave da tenere presenti una volta arrivati a questo punto.
Usa un'API RESTful per mantenere la comunicazione semplice tra i servizi
Se non hai già adottato un'API RESTful, questo è un buon momento per farlo. Come spiega Martin Fowler, è consigliabile avere "endpoint intelligenti e pipe stupide", ovvero il protocollo di comunicazione tra i servizi dovrebbe essere il più semplice possibile e con l'unico compito di trasmettere i dati senza trasformarli. La magia avviene negli stessi endpoint: ricevono una richiesta, la elaborano ed emettono una risposta.
L'architettura di microservizi cerca di mantenere le cose il più semplici possibile per evitare componenti strettamente accoppiati. In alcuni casi, potresti trovarti a utilizzare un'architettura basata su eventi con comunicazioni asincrone basate su messaggi. Ma, ancora una volta, occorre esaminare i servizi di coda dei messaggi di base, come RabbitMQ, ed evitare di aggiungere complessità ai messaggi trasmessi tramite la rete.
Dividi i dati in contesti o domini di dati circoscritti
Le applicazioni monolitiche utilizzano un singolo database per tutte le funzioni aziendali dell'applicazione. Dal momento che un monolite viene suddiviso in microservizi, questo singolo database potrebbe non avere più senso. Un database centrale può trasformarsi in un collo di bottiglia per il ridimensionamento del traffico. Se un servizio in particolare accede al database con carico elevato, potrebbe interrompere l'accesso al database di altri servizi. Inoltre, un singolo database può diventare un collo di bottiglia di collaborazione per più team che tentano di modificare lo schema nello stesso momento. Ciò potrebbe rendere necessario suddividere il database o aggiungere ulteriori strumenti di archiviazione dei dati per supportare le esigenze di dati dei microservizi.
Il refactoring di uno schema di database monolitico può essere un'operazione delicata. È importante identificare chiaramente i set di dati di cui necessita ciascun servizio e le eventuali sovrapposizioni. Questa pianificazione dello schema può essere effettuata utilizzando contesti circoscritti, ovvero schemi provenienti dall'approccio Domain Driven Design. Un contesto circoscritto definisce un sistema autonomo, inclusi gli elementi che possono entrare e uscire da quest'ultimo.
In questo sistema, quando un utente accede a un ordine è possibile vedere le informazioni sull'utente nella tabella, che può essere utilizzata anche per popolare la fattura gestita dal sistema di fatturazione. Questo processo può sembrare logico e semplice, ma con i microservizi i servizi devono essere disaccoppiati per consentire l'accesso alle fatture anche in caso di inattività del sistema di ordinazione. Inoltre, ciò consente di ottimizzare o sviluppare la tabella di fatturazione indipendentemente dalle altre. Ogni servizio avrà quindi un suo proprio archivio dati per accedere ai dati di cui ha bisogno.
Ciò introduce nuovi problemi, poiché alcuni dati verranno duplicati in database diversi. I contesti circoscritti possono rappresentare la strategia migliore per gestire i dati condivisi o duplicati. Potresti adottare un'architettura basata su eventi per agevolare la sincronizzazione dei dati su più servizi. Ad esempio, i servizi di fatturazione e tracciamento della consegna potrebbero restare in ascolto degli eventi del servizio degli account quando i clienti aggiornano le loro informazioni personali. Alla ricezione dell'evento, questi servizi aggiorneranno l'archivio dati di conseguenza. Questa architettura basata su eventi semplifica la logica del servizio degli account, poiché non ha la necessità di conoscere gli altri servizi dipendenti. Informa semplicemente il sistema delle attività svolte e gli altri servizi ascoltano e agiscono di conseguenza.
Puoi inoltre scegliere di tenere tutte le informazioni sui clienti nel servizio degli account e mantenere soltanto un riferimento chiave esterno nei servizi di fatturazione e consegna, che interagiranno quindi con il servizio degli account per ottenere i dati pertinenti sui clienti invece di duplicare i record esistenti. Dal momento che non esiste una soluzione universale per questi problemi, dovrai esaminare ciascun caso specifico per individuare l'approccio migliore.
Crea l'architettura di microservizi tenendo conto degli errori
Abbiamo visto in che modo i microservizi possono offrirti enormi vantaggi rispetto all'architettura monolitica. Hanno dimensioni ridotte e sono specializzati, ovvero facili da comprendere. Sono inoltre disaccoppiati, quindi è possibile eseguire il refactoring di un servizio senza il timore di danneggiare gli altri componenti del sistema o di rallentare le attività di sviluppo degli altri team. Offrono poi maggiore flessibilità agli sviluppatori che possono scegliere tecnologie diverse, se necessario, senza essere vincolati dalle esigenze degli altri servizi.
In breve, l'architettura di microservizi semplifica lo sviluppo e il mantenimento di tutte le funzionalità aziendali. Ma le cose si complicano se esaminiamo tutti i servizi nella loro totalità e il modo in cui questi devono interagire per portare a termine le azioni. Il sistema è adesso distribuito con più punti di errore devi tenerne conto. È necessario prendere in considerazione non solo i casi in cui un servizio non risponde, ma anche quelli in cui le risposte di rete sono più lente. Anche il ripristino da un errore può risultare difficile a volte poiché occorre assicurarsi che quando i servizi sono di nuovo online non vengano inondati dai messaggi in sospeso.
Quando inizi ad estrarre le funzionalità dai sistemi monolitici, assicurati che i progetti siano in grado di gestire gli errori fin dall'inizio.
Poni l'accento sul monitoraggio per semplificare i test dei microservizi
Le attività di test rappresentano un altro inconveniente dei microservizi rispetto ai sistemi monolitici. Un'applicazione compilata come base di codice singola non necessita particolarmente di un ambiente di test attivo e funzionante. Nella maggior parte dei casi, dovrai avviare un server di back-end accoppiato con un database per poter eseguire la suite di test.
Nel mondo dei microservizi le cose non sono così semplici. Nel caso dei test unitari, la procedura sarà comunque piuttosto simile a quella dell'architettura monolitica e non dovrebbero esserci difficoltà particolari. Tuttavia, nel caso dei test dell'integrazione e di sistema, le cose si complicano notevolmente. Potresti dover avviare più servizi in contemporanea, avere in esecuzione diversi archivi dati e la tua configurazione potrebbe dover includere code di messaggi di cui non avevi bisogno nell'architettura monolitica. In questa situazione diventa molto più costoso eseguire test funzionali, senza contare il fatto che il numero crescente di parti in movimento rende molto difficile prevedere i tipi diversi di errori che possono verificarsi.
Il monitoraggio permette di individuare i problemi nelle prime fasi e di agire di conseguenza. È necessario comprendere le linee di base dei diversi servizi e reagire non solo quando diventano inattivi, ma anche quando si comportano in modo imprevisto. Uno dei vantaggi dell'adozione di un'architettura di microservizi è che il sistema dovrebbe essere resiliente agli errori parziali, pertanto se inizi a rilevare anomalie nel servizio di tracciamento delle consegne dell'applicazione Pizzup, non sarà così grave come lo sarebbe in un sistema monolitico. La nostra applicazione dovrebbe essere progettata in modo che tutti gli altri servizi rispondano correttamente e consentano ai nostri clienti di ordinare la pizza mentre è in corso il ripristino del servizio di tracciamento in tempo reale.
Adotta la continuous delivery per ridurre gli attriti della distribuzione
Il rilascio manuale di un sistema monolitico nell'ambiente di produzione è un'attività noiosa e rischiosa, ma è fattibile. Naturalmente non consigliamo questo approccio e incoraggiamo i team software ad adottare la continuous delivery per tutti i tipi di sviluppo, anche se all'inizio di un progetto potresti voler eseguire le prime distribuzioni in prima persona tramite la riga di comando.
Questo approccio non è sostenibile se è presente un numero crescente di servizi da distribuire più volte al giorno. Pertanto, come parte della transizione ai microservizi, è di importanza critica adottare la continuous delivery per ridurre i rischi di errori di rilascio, oltre che per assicurarti che il tuo team si concentri sulla compilazione e l'esecuzione dell'applicazione, senza rimanere bloccato nella fase di distribuzione. Mettere in pratica la continuous delivery significa anche che il servizio ha superato i test di accettazione prima di passare all'ambiente di produzione. Chiaramente, si verificheranno comunque dei bug, ma nel tempo creerai una solida suite di test che aumenterà la fiducia del team nella qualità dei rilasci.
L'esecuzione dei microservizi non è uno sprint
I microservizi sono una best practice nota e ampiamente adottata nel settore. Per i progetti complessi, offrono maggiore flessibilità di compilazione e distribuzione del software. Aiutano inoltre a identificare e formalizzare i componenti aziendali del sistema, il che torna utile se si hanno molti team che lavorano sulla stessa applicazione. Ma vi sono anche alcuni chiari svantaggi legati alla gestione dei sistemi distribuiti, oltre al fatto che la divisione di un'architettura monolitica dovrebbe avvenire solo se c'è una chiara comprensione dei limiti di servizio.
La creazione di microservizi deve essere considerata un percorso piuttosto che l'obiettivo immediato di un team. Inizia a piccoli passi per capire i requisiti tecnici di un sistema distribuito, come limitare le conseguenze degli errori e come eseguire la scalabilità dei singoli componenti. Quindi, puoi estrarre gradualmente più servizi man mano che acquisisci esperienza e dimestichezza.
La migrazione a un'architettura di microservizi non deve essere completata in un unico sforzo olistico. Una strategia iterativa per la migrazione sequenziale dei componenti più piccoli ai microservizi è l'approccio migliore. Identifica i limiti di servizio meglio definiti all'interno di un'applicazione monolitica consolidata e lavora in modo iterativo per disaccoppiarli in un proprio microservizio.
In conclusione...
Per ricapitolare, i microservizi rappresentano una soluzione vantaggiosa per il processo di sviluppo di codice prettamente tecnico e per la strategia complessiva dell'organizzazione aziendale. I microservizi consentono di organizzare i team in unità concentrate sullo sviluppo e sulla proprietà di specifiche funzioni aziendali. Questo focus granulare migliora l'efficienza e la comunicazione aziendali generali. I vantaggi offerti dai microservizi richiedono dei compromessi. È importante che i limiti di servizio siano chiaramente definiti prima di effettuare la migrazione all'architettura di microservizi.
Sebbene presenti numerosi vantaggi, l'architettura di microservizi aumenta anche la complessità. Atlassian ha sviluppato Compass per aiutare le aziende a gestire le complessità delle architetture distribuite man mano che si ampliano. Si tratta di una piattaforma di sviluppo estensibile che riunisce i dati separati di tutti i risultati tecnici e della collaborazione tra i team in un'unica posizione centrale e ricercabile.
Condividi l'articolo
Argomento successivo
Letture consigliate
Aggiungi ai preferiti queste risorse per ricevere informazioni sui tipi di team DevOps e aggiornamenti continui su DevOps in Atlassian.