Architetture
a microservizi
Architettura dei Sistemi Software
27 maggio 2016
Paolo D’Incau
[email protected]
www.xpeppers.com
Monolith
In un architettura classica (monolitica), tutte le
funzionalità del sistema sono racchiuse in una
unica entità che giorno dopo giorno tende a
diventare sempre più grande e complessa da
mantenere.
Problematiche
Un sistema monolitico tende ad essere fortemente
accoppiato (e.g. linguaggio, features, deploy).
Con il tempo diventa difficile capire dove una
determinata funzionalità è implementata e
spesso si introducono regressioni o duplicazioni
quando si aggiunge qualcosa di nuovo.
Microservizi
In un’architettura basata sui microservizi il
sistema è suddivido in svariate componenti di
dimensioni ridotte che hanno un ciclo di sviluppo
e rilascio indipendente.
Definizione
Un microservizio dovrebbe contenere tutto
quello che serve a garantire una funzionalità
autocontenuta del sistema (bounded context).
Ogni microservizio può comunicare con altri
microservizi al fine di ottenere informazioni
necessarie al proprio funzionamento.
Bounded Context
Rappresenta una specifica responsabilità definita da
dei confini ben definiti. Ogni context ha delle
interfacce di comunicazione con l’esterno.
Il concetto è introdotto da Eric Evans in “Domain
Driven Design”, libro che propone un paradigma
differente di affrontare lo sviluppo di un determinato
software.
Bounded Context
Utenti
Ordini
Magazzino
Massimizzare Cohesion
Tutto ciò che è pertinente ad una determinata
funzionalità deve stare “vicino” (come affermato
nel Single Responsibility Principle).
In questo modo riusciamo a trovare in modo facile
il codice relativo, ed evitiamo di avere logica
simile o correlata disseminata nella codebase.
Minimizzare Coupling
Quando il codice è accoppiato, un cambiamento in
un determinato punto causa dei forzati cambiamenti
in altri punti non necessariamente necessari.
Riducendo il coupling si riesce a garantire che il
deploy di una componente non necessiti quello di
un’altra.
Benefici
Andando ad adottare un approccio basato sui
microservizi possiamo ottenere:
•
•
•
•
•
eterogeneità tecnologica
resilienza
scalabilità
facilità/velocità di deploy
migliore organizzazione del team
Eterogeneità tecnologica
Un approccio a microservizi ci permette di
sperimentare e di usare lo strumento giusto per
risolvere il problema che abbiamo davanti.
Non esiste la paura di provare una nuova
tecnologia perché una eventuale riscrittura
sarebbe contingentata a poche righe di codice.
Resilienza
E’ un dato di fatto che il software fallisce. Suddividendo
la nostra architettura in microservizi possiamo
garantire che il fallimento di una singola componente
non pregiudichi il funzionamento dell’intero sistema.
•
•
•
bulkhead
circuit breaker
distribuzione
Scalabilità
A differenza di un’architettura monolitica,
un’architettura a microservizi permette di far scalare
solo la parte dell’applicativo che ne ha necessità.
Utilizzando strumenti come AWS è possibile scalare
i nostri servizi in base a delle metriche di utilizzo
del sistema.
Facilità/Velocità di deploy
In una architettura monolitica, il più piccolo
cambiamento alla codebase comporta il dover
rilasciare tutta la codebase.
L’approccio a microservizi permette di poter
rilasciare invece solo il servizio che è variato senza
andare ad impattare sul resto dell’applicativo.
Migliore organizzazione del team
Ogni microservizio è a carico di un team
crossfunzionale (frontend, backend, db, operation) che
lavora per ottenere una determinata funzionalità.
I team hanno libertà decisionale sullo stack e si
assumono la responsabilità end-to-end.
Idealmente vale la regola del “Two-Pizza Team”.
Svantaggi
Non dobbiamo pensare che i microservizi siano una
soluzione “silver bullet”.
Problematiche:
•
•
•
Necessita una certa maturità del team
Aumento della complessità
Problematiche relative alla gestione dei sistemi distribuiti
Complessità
All’aumentare del disaccoppiamento aumenta la
complessità.
Maggior numero di SCM, conoscenza divisa su
più team, diverse tecnologie e modi di rilascio.
Distribuzione
Si deve riuscire a garantire la comunicazione interservizio, o almeno creare dei meccanismi che
minimizzino eventuali problemi.
Non è sempre facile capire cosa è andato storto e
soprattutto dove (e.g. monitoring, logging).
Tenere in considerazione il CAP theorem.
Esempi vincenti
Realtà che hanno abbracciato un approccio a
microservizi e che ne sponsorizzano
l'applicazione:
•
•
•
Netflix
Spotify (http://bit.ly/1mOSHLk)
Zalando (http://bit.ly/20XSW9b)
Esempio reale: applicazione monolitica
•
caricamento documenti testuali
• diversi tipi di documenti
• tramite diversi endpoint (e.g. queue, web)
• validazione del contenuto
•
REST api
• permettono all’utente di eseguire comandi sul
sistema
• consumate da un’interfaccia web (AngularJS)
• consumate da applicazioni mobile
Esempio reale: applicazione monolitica
•
amministrazione
• creazione utenti
• profilazione utenti
• interfaccia web (AngularJS)
•
notifica verso servizi esterni
• store remoto
• applicazione di marcatura digitale
Architettura Monolith
Utenti
Validazione
API
Marca
Remote Store
Input
Estrazione Amministrazione
Utente?
Utenti
Validazione
Utente?
API
Marca
Remote Store
Input
Troppe Richieste?
Se ad ogni documento caricato o in fase di validazione
dello stesso andiamo a richiedere l’utente che sta
caricando rischiamo di stressate troppo il servizio estratto.
Soluzioni:
•
•
Scalare orizzontalmente
Applicare politiche di caching
Troppe Richieste: scalare
Utenti
Utente?
Validazione
Utenti
L.
B.
API
Utente?
Utenti
Marca
Remote Store
Input
Troppe Richieste: caching
Utente?
Utenti
C
A
C
H
E
Validazione
API
Utente?
Marca
Remote Store
Input
Scalare vs Caching
Scalare è molto semplice quando abbiamo una
infrastruttura cloud (possiamo scalare in modo
programmato a seconda del carico misurato).
Il meccanismo di caching è molto performante
perché evita chiamate inutili a servizi o
database.
Estrazione Marcatura
Validazione
Utenti
esegui
Marca
API
Remote Store
Input
E se servizio di marca non risponde?
Se il servizio di marca non è attivo per un qualsiasi
disservizio potremmo avere migliaia di richieste che
falliscono solo dopo un determinato timeout, andando a
generare una grandissima latenza tra i nostri vari servizi.
Soluzione:
•
Rendere il servizio di marca asincrono
Asincronia ed Eventual Consistency
Nel nostro caso, il processo di marcatura può
essere eseguito in modo asincrono e adottare
politiche di eventual consistency.
Una possibile soluzioni consiste nel mettere il dato
in una coda e fornire una dashboard che permetta
di risottomettere il documento alla marcatura.
Le richieste alle API cosa fanno?
Spesso ci si rende conto nella maggior parte
delle nostre applicazioni, gli utenti vanno a fare
due tipi di operazioni:
•
•
Modifiche allo stato del sistema (poche)
Letture sullo stato del sistema (molte)
CQRS
Command-Query Responsibility Segregation (CQRS) è
un pattern che ci permette di separare logicamente la
nostra applicazione in:
•
•
comandi che vengono validati e applicati ai nostri
modelli e che modificano il loro stato
query che vanno a interrogare delle proiezioni del
sistema ottimizzate alla visualizzazione
CQRS
Command
Bus
Command
Handler
Event
Bus
User
Inteface
Event
Handler
Queries
Domain
Storage
Domain
Storage
CQRS: Benefici
Questo separazione di responsabilità ci garantisce
diversi vantaggi:
•
•
•
comandi e query posso risiedere su servizi/
macchine diverse —> scalabilità
possiamo usare diverse tecnologie (e.g. database per
le diverse parti)
abbandonare design “database driven”
Logging e Monitoring
Qualsiasi applicazione software necessita di
meccanismi di logging per storicizzare cosa è accaduto
nel sistema.
Allo stesso modo è importante avere dei meccanismi
di monitoring che verifichino che le varie componenti
della nostra architettura funzionino in modo corretto.
Logging e Monitoring
Passare da un’architettura monolitica a un’architettura a
microservizi significa andare ad aggiungere uno strato di
complessità.
Servono strumenti che ci permettano di aggregare ed
analizzare i dati provenienti dalle diverse fonti:
• ELK (Elesticsearch + Logstash + Kibana)
• SENSU
Logging e Monitoring
Kibana
Logs
Logstash
ES
Logs
Esempio: dashboard
Logging e Monitoring
Va sempre tenuto in considerazione che i log e
le metriche di monitoraggio non comunicano
solo informazioni utili agli sviluppatori.
Affrontare queste tematiche usando un’ottica di
collaborazione ci permette di estrarre dati utili
in ottica di business.
Possibili Metriche
Ad esempio nel nostro applicativo potremmo essere
interessati a:
•
•
•
•
•
numero di documenti caricati
distribuzione del carico nelle ore/giornate
errori più comuni di validazione
documenti marcati con errore
etc, etc…
Conclusioni I
Utilizzare i microservizi ci permette di andare a
costruire il software e il modo in cui lavorano i
nostri team attorno a concetti di business e non
attorno a tecnologie.
Possiamo inoltre lavorare il software in modo
che sia rilasciabile in modo indipendente,
riducendo dunque il “time to market”.
Conclusioni II
Un’architettura a microservizi, sebbene sia più
complessa da gestire rispetto ad un monolito
permette di isolare in modo migliore i possibili
fallimenti e di scalare in modo più semplice.
Si devono introdurre dei meccanismi di logging
e monitoring più sofisticati al fine di garantire
che il sistema si comporti nel modo corretto.
Letture consigliate
Recommended
reading
http://amzn.to/1UJhPkz
http://bit.ly/1sNK07n