In cosa consiste, quali sono le sostanziali innovazioni rispetto ai processi tradizionali e quali vantaggi comporta l'architettura a microservizi
Oggi si sente parlare ormai molto spesso di architettura a microservizi, container e containerizzazione. Ma che significa?
Negli ultimi dieci anni lo sviluppo di nuove applicazioni è stato interessato da scenari di profonda trasformazione, che hanno visto il cloud diventare sempre più protagonista. Migrare sulla nuvola il software legacy, piuttosto che sviluppare ex novo con logiche cloud native, ha creato terreno fertile per l’applicazione di nuove metodologie, impattanti soprattutto nella ricerca di una gestione più agile e flessibile per progetti sempre più complessi e articolati a livello di funzioni ed esigenze da soddisfare.
Le tradizionali applicazioni monolitiche stanno progressivamente cedendo il loro spazio a nuove architetture, come l’architettura a microservizi. Vediamo pertanto in cosa consistono, quali sono le sostanziali innovazioni rispetto ai processi tradizionali e quali vantaggi comporta lo sviluppo in ambiente cloud, senza trascurare le criticità che una errata valutazione può comportare lungo tutto il ciclo di vita del software, a partire dal rendere inutilmente più complesso ciò che non avrebbe alcuna ragione di esserlo.
Cos’è una architettura a microservizi
Per capire cosa si intende per architettura a microservizi è utile innanzitutto capire le ragioni che ne stanno alla base, in particolare perché si è avvertita la necessità di creare un modello alternativo rispetto alla tradizionale struttura monolitica del software. I microservizi infatti costituiscono una architettura IT concepita appositamente per supportare un modo di intendere lo sviluppo software di nuova concezione, basato sulla scomposizione in pacchetti, ormai tipica del cloud, in particolare del modello a servizi (SaaS).
Nel contesto tradizionale, il software è stato concepito di fatto come una singola entità, un progetto unico, sviluppato con un solo linguaggio di programmazione, distribuito in un unico pacchetto gestito da un file eseguibile. Si tratta di un modello per sua natura molto efficace nel contesto on-premises, che ha condizionato varie stagioni della storia evolutiva del software, finendo inevitabilmente per subire i segni del tempo, soprattutto nell’ottica in cui un’applicazione viene concepita come un insieme di servizi molto eterogenei, che diventa complesso e macchinoso gestire in maniera indistinta.
Il software monolitico ha infatti iniziato a manifestare una serie di criticità oggettive sia per quanto concerne lo sviluppo, sia per quanto riguarda l’esperienza dell’utente. Basti sapere che ogni singola modifica comporta la ridistribuzione dell’intera applicazione, mediante la pubblicazione di un nuovo eseguibile. Questa evidenza comporta una serie di conseguenze piuttosto gravose da gestire. Non è infatti possibile risolvere al volo un problema, ma occorre pianificare l’uscita di una serie di service update che raccolgono una serie di migliorie e bug fixing, il che costringe gli utenti a lunghi attese prima di vedere implementata una funzione che, da sola, potrebbe richiedere non più di qualche ora di lavoro per uno sviluppatore. Il modello monolitico preclude inoltre una gestione a distribuzione continua (continous delivery), totalmente trasparente per l’utente finale.
Un altro limite oggettivo nella gestione di un software monolitico è dato dal fatto che, trattandosi di un unico pacchetto, ogni sviluppatore che entra nel team deve conoscere il funzionamento di tutta l’applicazione, anche se il suo ruolo consiste nella modifica di una singola feature. Questo comporta tempi e costi di onboarding sempre più ingiustificati, nel contesto in cui il software deve essere sempre più reattivo nel rispondere alle esigenze dei propri utenti.
Come funzionano i microservizi
In tempi più recenti, anche facendo riferimento soltanto alle criticità appena espresse, si è sentita più che mai viva l’esigenza di variare l’approccio monolitico, spostando la complessità a livello della gestione del progetto, per alleggerire e rendere più snelle le procedure sui singoli processi. Il monolite viene dunque scomposto in vari servizi, mediante una specializzazione orientata a distinguere le parti funzionali. Questo consente agli sviluppatori di concentrarsi soltanto su una parte del software, contribuendo alla creazione dei singoli pacchetti che compongono l’applicazione complessiva.
Tra i criteri che consentono di partizionare un sistema in servizi non ci sono regole assolute, ma una serie di prassi diffuse. Due opzioni sono consentite dalla suddivisione per verbi (azioni da fare) o per sostantivi (risorse e entità). A prescindere dalle possibili interpretazioni etimologiche, proviamo a fare un semplice esempio, in modo da comprendere i concetti che stanno alla base di un software pacchettizzato.
Un caso ricorrente di applicazione moderna è un e-commerce. Vediamo perché abbia tutto il senso svilupparla con una suddivisione a servizi piuttosto che mediante un approccio di tipo monolitico. Un’applicazione e-commerce è di norma costituita da un front-end, finalizzato all’interazione con l’utente finale, e da un back-end, utilizzato per gestire tutte le operazioni necessarie al suo funzionamento. Semplificando il più possibile lo scenario, possiamo presumere che un front-end si relazioni con un sistema di acquisto, un sistema di gestione del carrello e un sistema di gestione degli ordini e delle fatture, oltre a tutti gli aspetti legati all’interfaccia e alla visualizzazione dei prodotti implementati a loro volta in un catalogo. A ciò si aggiungano i servizi relativi al supporto post vendita.
Se noi gestissimo tutti questi processi in maniera monolitica, andremmo a rendere inutilmente macchinosa la gestione di una struttura che è naturalmente strutturata per essere suddivisa in servizi. Il design stesso dell’applicazione è strutturato per servizi. E così avviene di fatto anche negli elementi che compongono l’intera applicazione. Questo consente di avere dei processi molto più agili e snelli nello sviluppo, a fronte di una naturale crescita della complessità nella gestione dell’intero progetto.
Qualora si manifestasse l’esigenza di aggiornare una feature relativa alla gestione degli ordini, sulla base di un’architettura basata sui singoli servizi, potrò intervenire soltanto sul servizio per rilasciarlo una volta testato sulle istanze necessarie. Secondo le logiche di distribuzione in cloud, questo consente una distribuzione continua del software che il modello monolitico non potrebbe garantire, essendo vincolato all’aggiornamento dell’intera applicazione per ciascuna modifica introdotta.
Quando utilizzare l’architettura a microservizi
Per supportare la creazione del software pacchettizzato si sono dunque diffuse le architetture a microservizi, che consentono di svolgere in maniera agile le fasi di sviluppo, test e implementazione delle applicazioni sfruttando le potenzialità offerte dagli ambienti cloud. Non è un caso che tra i microservizi più diffusi rientrino i container.
Concettualmente simili alle macchine virtuali, i container nascono appositamente per rispondere alle esigenze dello sviluppo software. A differenza delle VMM infatti non riproducono l’intera macchina, con tanto di virtualizzazione dell’hardware utile al suo funzionamento, ma si concentrano su una singola applicazione, isolandone l’ambiente di sviluppo rispetto al sistema fisico del server che li esegue. Quando sentiamo parlare di applicazioni containerizzate, possiamo quindi immaginare un sistema che distribuisce sviluppo, test e implementazione dei vari pacchetti del software in varie istanze, definite appunto dagli stessi container.
Esistono naturalmente varie tipologie di container, che vanno dal generico container Linux fino a tecnologie più specifiche come i container Docker, capaci di suddividere le applicazioni nei vari processi che li compongono, grazie a strumenti più evoluti in funzione dell’esperienza utente. Un altro termine molto diffuso nel lessico dei microservizi è Kubernetes, uno strumento di orchestrazione in ambiente Linux che consente di gestire in modo razionale un numero di container anche decisamente elevato. Grazie a Kubernetes possiamo ad esempio selezionare e raggruppare i container che ci interessano per consentire la distribuzione dei servizi (rete, sicurezza, ecc.). L’universo dei microservizi per lo sviluppo software è quindi decisamente ampio e variegato. In sintesi, vediamo quali sono i principali vantaggi e le criticità più ricorrenti con cui gli sviluppatori si confrontano ogni giorno durante il loro lavoro. Si tratta di fattori discriminanti in funzione della scelta dell’architettura da utilizzare, per capire se un modello basato sui microservizi possa rivelarsi di fatto la miglior alternativa possibile.
I vantaggi dei microservizi
Uno degli aspetti più evidenti dell’utilizzo dei microservizi è dato dalla possibilità di sfruttare tutti i vantaggi derivanti dal cloud computing, che consentono di eliminare i costi relativi alle infrastrutture e alle risorse IT necessarie per sostenere le fasi di sviluppo, collaudo e implementazione delle applicazioni, potendo inoltre contare sulla scalabilità, sulla sicurezza e sugli ambienti di sviluppo pronti all’uso che i provider PaaS (Platform as a Service) mettono a disposizione degli ISV. Il risultato è quello di poter creare applicazioni più semplici da mantenere ed aggiornare lungo l’intero ciclo di vita. Vediamo inoltre quali sono i principali vantaggi specifici derivanti dall’impiego di un’architettura a microservizi.
- Utilizzo di più linguaggi di sviluppo: gestire in maniera separata più parti del software consente di programmarle con linguaggi differenti. Questo consente di gestire i servizi utilizzando le migliori tecnologie disponibili, soltanto dove effettivamente occorrono. Ciò si traduce inoltre in una notevole indipendenza per i singoli team di sviluppo, con la possibilità di facilitare notevolmente anche il lavoro delle risorse umane dell’azienda durante il processo di selezione.
- Impulso all’innovazione: la possibilità di gestire in maniera indipendente i vari pacchetti consente di testarli in maniera più agile e veloce, incentivando la sperimentazione di nuove tecnologie.
- Cicli di iterazione rapidi: il fatto di potersi concentrare su parti specifiche dell’applicazione consente di pianificare cicli di manutenzione e aggiornamento molto rapidi, in modo da garantire un modello a distribuzione continua, su cui si basano ad esempio i Saas (Software as a Service).
- Ottimizzazione della scalabilità: la combinazione tra le caratteristiche del PaaS e l’indipendenza dei pacchetti dell’applicazione consente di ottimizzare con un elevato livello di controllo la scalabilità in cloud, secondo la quantità di risorse hardware richiesta dai singoli servizi.
- Correzione dei singoli punti di guasto: se i componenti sono separati, qualora si verificasse un bug, sarebbe possibile isolarli, testare una soluzione su più istanze e rilasciare il servizio funzionante senza condizionare il funzionamento del resto dell’applicazione. Un vantaggio non da poco soprattutto per quei software che devono supportare servizi e soluzioni per cui si presume un elevato livello di resilienza.Orchestrazione snella dei servizi: la possibilità di organizzare sviluppo, collaudo e rilascio dei servizi sui container consente di gestirli in maniera semplice ed intuitiva anche nel caso di progetti di grande dimensione, il che in generale agevola moltissimo il mantenimento di un elevato livello di coerenza dell’applicazione.
Svantaggi e problematiche dei microservizi
La maggior parte dei problemi relativi all’utilizzo dei microservizi deriva da errori di valutazione iniziali, quando si tratta di scegliere come sviluppare un progetto software, piuttosto che da un project management non ottimale lungo il suo ciclo di vita. Anche in questo caso, ci sono degli aspetti limitanti, tipici dell’architettura, che se sottovalutati rischiano di condizionare il successo di un’iniziativa. Vediamo una rapida rassegna.
- Orchestrazione onerosa dei servizi: non si tratta di una incoerenza rispetto all’ultimo punto evidenziato nei vantaggi, quanto di un aspetto complementare. Se l’orchestrazione di tanti servizi è più snella, al tempo stesso il fatto di avere tanti servizi da gestire comporta un impegno non differente, ovviabile soltanto con un elevato livello di automazione, non sempre semplice da implementare.
- Controllo del livello generale di coerenza dell’applicazione: l’indipedenza dei servizi può generare delle operazioni capaci di creare delle incoerenze a livello generale. La supervisione del progetto assume dunque una rilevanza cruciale ed è tanto più complessa quanto più lo è la struttura dell’applicazione implementata su un’architettura a microservizi.
- Quantità e varietà dei test: occorre fare una distinzione tra il test a livello di singolo servizio e il test generale dell’applicazione, in cui diventa fondamentale valutare soprattutto l’integrazione. In questi casi diventa molto importante la configurazione e l’automatizzazione dei test, soprattutto per le app a distribuzione continua che possono prevedere anche più deploy nella singola giornata.
- Comunicazione tra i singoli servizi: a livello tecnico, più servizi vengono tra loro disaccoppiati rispetto ad un blocco monolitico, più si genera una oggettiva esigenza di comunicazione per far dialogare l’applicazione nel suo insieme. Occorre quindi garantire bassi livelli di latenza e code di messaggi per instradare le comunicazioni con la maggior continuità possibile. Al tempo stesso bisogna resistere alla tentazione di moltiplicare i servizi, generando una situazione frammentaria ed oggettivamente difficile da gestire anche in termini di comunicazione.
- Scalabilità complessa: se l’indipendenza dei servizi consente di scalarli in maniera ottimale, al tempo stesso garantire la disponibilità di un elevato livello di servizi costituisce un altro degli aspetti di complessità che va valutato con estrema attenzione quando si pensa all’applicazione nella sua interezza.
- Database decentralizzati: la scomposizione in servizi si porta dietro anche una gestione del dato capace di soddisfare il funzionamento di ogni componente del software. Occorre dunque capire come partizionare un database che prevede accessi plurimi e non può più godere dei vantaggi esclusivi della centralizzazione. Ci sono pertanto pro e contro, ma in generale una maggior complessità da gestire, soprattutto nel caso di database articolati e di grandi dimensioni.
- Refactoring delle applicazioni monolitiche: più che di una criticità, si tratta ovviamente di una condizione molto diffusa, soprattutto quando si prende in considerazione la possibilità di migrare in cloud un software legacy di grandi dimensioni. Una strategia comune è data dal pacchettizzare il monolite e portare questi servizi sul PaaS, cercando ove possibile di containerizzarli. La complessità dell’operazione è data dal dover adattare un’applicazione che non è stata concepita per essere gestita in cloud, il che comporta la riscrittura di parti anche piuttosto estese ai fini di garantire tutte le funzionalità originariamente previste.
Architetture alternative
I microservizi costituiscono con ogni probabilità l’alternativa più diffusa al tradizionale modello monolitico, ma non sono certamente l’unica opzione a disposizione degli sviluppatori. Non possiamo trascurare la presenza dei Web Service e della Service Oriented Architecture (SOA).
Il Web Service, fonte W3C, è un sistema software progettato per supportare l’interoperabilità tra diversi elaboratori su una medesima rete oppure in un contesto distribuito. Consentono quindi l’interoperabilità tra diversi software, anche scritti con linguaggi differenti, su differenti piattaforme hardware. Sono concepiti per l’utilizzo sul web, quindi capaci di utilizzare il protocollo http, semplice ed universalmente diffuso, anche se non sempre ottimale dal punto di vista della sicurezza. In ogni caso l’utente finale non percepisce la complessità dei framework utilizzati per il loro sviluppo, in quanto le interfacce web based consentono di fruire direttamente dell’esecuzione del servizio.
I Web Service presentano dunque molte analogie con i microservizi ma si differenziano ad esempio per via della propria natura votata all’interoperabilità, che consente ad un’applicazione di sfruttarne un’altra per via della notevole apertura alle integrazioni. Un caso molto ricorrente di Web Service è Google Maps, che consente a chiunque di condividere informazioni e contenuti al suo interno, oltre a poterle integrare nelle interfacce di altre applicazioni accessibili via browser web.
Il SOA (Service Oriented Architecture) è un modello architetturale che ha preceduto i microservizi, cercando di dare per primo una risposta all’esigenza di partizionare i modelli monolitici dei software. Tra i documenti descrittivi risulta molto interessante il Manifesto SOA, che afferma come: “l’orientamento ai servizi è il paradigma che circoscrive quello che fai. L’architettura orientata ai servizi (SOA) è il tipo di architettura fondata sull’applicazione dell’orientamento ai servizi. Applichiamo l’orientamento ai servizi per favorire in un modo consistente le organizzazioni nel fornire prestazioni di business sostenibile, con maggiore agilità di impiego ed efficienza nei costi, adattandosi alle mutevoli esigenze aziendali”.
La logica del SOA, che tende a privilegiare il valore del business piuttosto che il primato tecnico, è dunque molto simile alla filosofia su cui si basano i microservizi, ma a livello tecnologico vi sono differenze molto marcate, in cui emergono le particolarità del SOA quale un’architettura di precedente generazione. Un punto fondamentale è costituito dal Enterprise Service Bus (ESB), la tecnologia che SOA utilizza per coordinare ed orchestrare i vari applicativi per svolgere le funzioni di business previste. I problemi emergono quando le applicazioni SOA diventano molto grandi, in quanto si rischia da un lato di ricadere in limiti “monolitici” che tendono ad sfumare i vantaggi ottenuti con il disaccoppiamento, per contro spostando tutto il peso dell’applicazione sulla parte relativa al business logic. Il SOA ha dunque il gran merito di aver aperto la strada alla separazione dei componenti fondamentali di un software, ma oggi inizia a pagare dazio in termini di funzionalità per le applicazioni moderne.
Tra le varie alternative possibili non va comunque accantonata la stessa architettura monolitica, che nel caso di software semplici e veloci da sviluppare, o caratterizzati da un breve ciclo di vita, rimane molto spesso l’opzione migliore, in quanto non sarebbe giustificabile lo sforzo di progettare e mantenere delle applicazioni come quelle partizionate sui microservizi. Esiste dunque una soglia di complessità e durata delle applicazioni, un break even da valutare con grande attenzione per capire dove finisca la convenienza del modello monolitico e inizi per davvero a rivelarsi vantaggiosa un’architettura a microservizi.