Microservizi
Tutti i microservizi che compongono la piattaforma devo rispettare i seguenti standard e regole generali
Tutti i servizi devono rispettare i 12factor, descritti in modo anche piĂš dettagliato nell'articolo An illustrated guide to 12 Factor Apps
Packaging
Obiettivo
Ogni microservizio deve poter essere eseguito facilmente in locale, anche da chi non conosce a fondo il progetto. Lâobiettivo del packaging è:
facilitare lâavvio dellâapplicativo;
semplificare lo sviluppo e il debugging;
rendere accessibile un ambiente funzionante con un semplice
docker-compose up.
Struttura del repository
Nella root del repository devono essere presenti i seguenti file:
Dockerfile
Build dellâimmagine base del servizio
docker-compose.yml
Definisce i container minimi per lâesecuzione in produzione o ambienti CI
docker-compose.dev.yml
Contiene configurazioni utili per lo sviluppo locale (es. build, mount volumi, strumenti debug)
.dockerignore
(opzionale) Esclude file e cartelle non rilevanti dalla build
Principi e buone pratiche
Il file
docker-compose.ymldeve essere autosufficiente: chi lo scarica deve poter eseguire il servizio senza dover clonare lâintero repository.Le immagini Docker utilizzate devono essere:
disponibili su un registry pubblico (es. DockerHub, GitHub Container Registry ecc.);
oppure facilmente costruibili in locale tramite override.
â ď¸ Non includere la chiave
build:neldocker-compose.ymlprincipale, per evitare conflitti nei contesti di esecuzione remota (es. CI/CD).
Sviluppo locale
Per lo sviluppo in locale:
Copia o rinomina il file
docker-compose.dev.ymlindocker-compose.override.ymlEsegui:
Docker gestirĂ automaticamente il merge tra
docker-compose.ymledocker-compose.override.yml(vedi: Docker Docs â Multiple Compose Files).
â In questo modo, chiunque può avviare lâambiente di sviluppo senza modificare i file originali.
Esempio di uso dei file
docker-compose.yml (semplificato)
docker-compose.dev.yml
Dopo aver rinominato docker-compose.dev.yml in docker-compose.override.yml, il comando:
...utilizzerĂ in automatico lâimmagine build locale con i volumi montati per lo sviluppo.
Terminazione Pulita
Obiettivo
Ogni microservizio deve essere in grado di gestire correttamente la terminazione, in modo da:
garantire la consistenza dei dati;
evitare la perdita di eventi o operazioni incomplete;
chiudere le risorse in uso in maniera sicura (DB, Kafka, file, socket...).
La gestione corretta della terminazione è essenziale sia in ambiente Docker che in Kubernetes, dove il segnale di terminazione (SIGTERM) viene inviato prima di un restart o di un downscaling.
Segnali da gestire
SIGTERM
Richiesta di terminazione gentile
Il servizio avvia lo shutdown controllato
SIGKILL
Terminazione forzata e immediata
Non gestibile dal codice; da evitare
â Il servizio DEVE catturare
SIGTERMe avviare una sequenza di terminazione controllata.
Comportamento atteso
Alla ricezione di SIGTERM, il servizio deve:
smettere di accettare nuove richieste o eventi;
completare le operazioni critiche in corso;
liberare risorse (connessioni, file, lock...);
loggare la terminazione a livello
INFO;uscire con exit code 0 entro un tempo ragionevole.
Checklist â Terminazione Pulita
Cattura SIGTERM
Il servizio intercetta correttamente il segnale SIGTERM
â
Terminazione controllata
Avvia una sequenza di chiusura ordinata
â
Timeout ragionevole
Si chiude in max 10s (o valore configurato)
â
Operazioni critiche completate
Nessuna perdita o corruzione dati
â
Chiusura delle risorse
Socket, consumer, file, connessioni DB...
â
Log di terminazione
Esempio: "SIGTERM ricevuto, shutdown in corso..."
â
Nessuna nuova richiesta
I listener vengono disattivati
â
Exit code 0
Il processo si chiude correttamente
â
Esempio Python â uvicorn + asyncio
uvicorn + asyncioEsempio Go â signal.NotifyContext
signal.NotifyContextScript di test automatico
Usare questo script per validare la terminazione pulita in locale o integrarlo nella CI.
Se si usa
docker-compose, si può integrare il test nel CI lanciando:
Configurazione
Principi
La configurazione dei microservizi deve avvenire:
principalmente tramite variabili dâambiente;
opzionalmente tramite:
file
.env(per ambienti locali);parametri CLI (per job schedulati o debug interattivo).
Best practice
Variabili dâambiente
Default per tutti i deployment (prod/dev/test)
.env file
Sviluppo locale o test manuali
CLI parametri
Script cron, job schedulati, test/debug
â Le configurazioni non devono essere hardcoded nel codice. Utilizzare sempre default sicuri e sovrascrivibili.
Healthcheck
Obiettivo
Permettere al sistema di orchestrazione (Docker/Kubernetes) di verificare che il servizio sia vivo e funzionante.
HTTP microservizi
Deve esporre un endpoint
/status:200 OKse tutto è funzionante;codice diverso in caso di errore.
Altri microservizi
Se il servizio non è HTTP, lâhealthcheck può essere:
la presenza di un file di stato;
la verifica di un processo in esecuzione.
â Lâhealthcheck DEVE essere incluso nel
Dockerfile.
Logging
Principi generali
Tutti i microservizi devono implementare un sistema di log coerente, strutturato e facilmente aggregabile.
Riferimento normativo: Allegato 4 delle Linee Guida di InteroperabilitĂ AgID.
I log devono supportare almeno i livelli:
DEBUG,INFO,ERROR. Una gestione completa prevede sei livelli (vedi sotto).Ogni log di errore o anomalia deve essere su una singola riga, facilmente parsabile (plaintext
key=valueo JSON).Evitare log verbosi e non strutturati: ostacolano il monitoraggio e la correlazione cross-microservizio.
â ď¸ Non mischiare log di tipo HTTP e stacktrace multilinea.
Scrivere i log a singola riga su
stdout.Scrivere stacktrace (se necessario) separatamente su
stderr.
Formato e contenuto dei log
Preferibile lâuso di log in formato JSON puro, ma solo se interamente strutturato.
Ă vietato:
Mischiare testo libero e JSON nello stesso log.
Includere payload JSON grezzi come stringa in un campo JSON (es. loggare interamente lâevento Kafka).
Campi richiesti in ogni log rilevante
level
â
Definisce il tipo di log (INFO, ERROR, DEBUG, CRITICAL, WARNING, ecc.)
environment
â
Definisce l'ambiente in cui il microservizio è in esecuzione
event_id
â ď¸
Identificatore univoco dellâevento (se applicabile)
event_type
â
Tipo di evento o operazione ricevuta
call_type
â ď¸
Tipo chiamata: HTTP, CLI, EVENT
topic
â ď¸
Kafka topic da cui è stato letto lâevento (se applicabile)
log_message
â
Messaggio del log (errore o info)
client_ip
â ď¸
IP della chiamata in ingresso. Usare X-Forwarded-For quando presente
tenant
â
identificativo univoco dell'ente a partire dal quale è stato generato il log
provider, application, service
â
Contesto operativo (se disponibili)
user_id
â
Se presente, deve essere un riferimento anonimo (es. ID utente). Mai dati personali in chiaro
Allâavvio, ogni servizio DEVE loggare la versione in esecuzione e lâambiente (es. local, boat-qa, boat-prod).
Livelli di log
CRITICAL
Fallimento grave, con perdita di dati o che comporta lo l'uscita dal flusso di esecuzione (shutdown) del servizio per impossibilità a proseguire o perchÊ è piÚ safe non proseguire.
No
ERROR
Anomalia che non è possibile gestire e che avrà effetti sul risultato. A ERROR, il rate dei log che si alza dovrebbe essere indice che ci sono problemi per i quali è importante attrarre l'attenzione degli amministratori. Attenzione a non confondere errori che si verificano su sistemi esterni e che non sono nostri errori. Date sempre per scontato che una riga di errore prodotta dovrebbe sempre essere letta da qualcuno, altrimenti meglio esporre un livello di warning o non loggare proprio. Una anomalia dovrebbe sempre dare origine a una e una sola riga di log.
Si
WARNING
Anomalia che non avrebbe dobuto presentarsi ma che è stata gestita correttamente per non comportare errori nel nostro servizio. Ad esempio ho ricevuto un evento con una versione negativa, ma l'ho ignorato. Oppure non sono riuscito a collegarmi al db la prima volta, ma solo dopo 2 tentativi.
Si
INFO
Comunica un cambiamento di stato del servizio. Un evento significativo produce una riga di log per ogni evento o chiamata ricevuta dall'applicativo. Se una transazione o un evento gestito presenta errori NON si deve produrre la riga di INFO e la riga di ERROR, ma solo la seconda.
Si
DEBUG
Questo è il livello in cui comunicare informazioni diagnostiche, non necessarie se non si sta investigando un errore specifico. Devono essere informazioni utili a comprendere errori, non semplicemente a comprendere il flusso interno del software, per il quale esiste il livello apposito (TRACE). Una informazione utile è dare tutto il contesto che permette di comprendere perchÊ il servizio si sta comportando in un certo modo.
No
TRACE
Questo livello è inteso per tracciare il flusso interno del servizio, in modo molto dettagliato. Per esempio si può inserire un log a questo livello per capire in quale ramo del codice siamo finiti con l'esecuzione, oppure si può inserire una riga all'inizio e una alla fine di certe porzioni di codice rilevanti.
No
â Ogni anomalia deve generare una e una sola riga di log a INFO, i dettagli vanno a DEBUG.
Esempi di log ben formattati
JSON strutturato
Plaintext key=value
Checklist di qualitĂ per log ERROR
ERRORUsare questa checklist in review e PR:
Monitoring errori
Obiettivo
Raccogliere centralmente gli errori per individuare rapidamente problemi in produzione.
Requisiti
Ogni microservizio DEVE integrare Sentry.
Una volta configurato:
abilitare lâintegrazione con Sentry;
definire regole di alert personalizzate, se quelle standard non bastano.
đŹ Usare
SENTRY_DSNcome variabile dâambiente per collegare il servizio a Sentry.
Monitoring metriche
Obiettivo
Esportare metriche di stato e performance per il monitoraggio continuo.
Endpoint Prometheus
Se HTTP, il microservizio DEVE esporre un endpoint
/metricsin formato Prometheus.
Cosa monitorare
Error counters
â
Numero errori critici/intermittenti
Response histograms
â
Tempi di risposta di servizi esterni
Status code metrics
â ď¸
Solo se utili (es. 500 frequenti) â altrimenti usiamo Traefik
404 o bot traffic
â
NON deve essere esposto come metrica â produce rumore inutile
Cron
Principio
Non reinventare un sistema di schedulazione dentro lâapplicazione. Usare invece lâorchestratore.
Linee guida
NON implementare schedulatori interni (es. cron manuale, loop time-based).
Il servizio deve essere idempotente: rieseguibile sugli stessi dati senza effetti collaterali.
Esecuzione corretta
Implementare task cron come comandi CLI, eseguibili con la stessa immagine Docker:
Il job verrĂ poi schedulato tramite clustered cron (es. crazy-max/cronjob).
Parametri e file di configurazione
Principio
Niente overengineering: Docker impone giĂ un mapping tra esterno e interno.
Best practice
Path interni
Usa path fissi tipo /data, senza preoccuparsi del mapping esterno
Configs
Usare Docker Swarm Configs per file di configurazione
Secrets
Usare Docker Swarm Secrets per credenziali e dati sensibili
NO volumes
Evitare lâuso di volume mapping per la configurazione
â Questo approccio garantisce sicurezza, semplicitĂ e prevedibilitĂ .
Continuous Integration
Obiettivo
Standardizzare la CI tra i progetti, garantendo efficienza, velocitĂ e qualitĂ .
Requisiti
Ogni progetto DEVE includere la CI condivisa:
Eventuali step personalizzati (es. test, lint, coverage) vanno inseriti dopo lo
include.Devono riutilizzare la build giĂ fatta tramite artifact, evitando rebuild inutili.
Performance CI
Durata massima della CI: 15 minuti
Durata consigliata: ⤠5 minuti
⥠Ottimizzare usando:
build multi-stage Docker;
test selettivi.
Test publiccode.yml
publiccode.ymlPer progetti pubblicati su Developers Italia:
Last updated
Was this helpful?