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
Quello che ci si aspetta è che si possa usare il docker-compose.yml scaricandolo direttamente sul proprio pc, possibilmente senza necessità di clonare tutto il repository.
Dockerfile
e docker-compose.yml
devono essere presenti nella root del repo (.dockerignore
se serve) per facilitare l'avvio in locale dell'applicativo e farne saggiare l'utilizzo; Perché sia pienamente usabile è necessario che le immagini facciano riferimento al registry pubblico (nostro o meno).
usare un docker-compose.dev.yml
per aggiungere tool utili per lavorare in locale durante lo sviluppo.
Per esempio la chiave "build" per le immagini create deve essere presente nel file ".dev" e non nel file compose principale.
In questo modo scaricando il repository e rinominando il file .dev in docker-compose.override.yml
ci si mette nelle condizioni ideali per sviluppare il servizio e un docker-compose up
comporterà automaticamente l'uso in cascata dei file docker-compose.yml
e docker-compose.override.yml.
Per ulteriori dettagli consultare il manuale di Docker: Merge compose files.
il servizio deve gestire correttamente i segnali inviati da docker SIGTERM e SIGKILL per fare uno shutdown pulito e senza interrompere l'esecuzione in fasi critiche per la consistenza dei dati.
la configurazione *deve avvenire mediante variabili d'ambiente
la configurazione può essere fatta anche tramite un file di environment (.env
) o parametri da cli
se http, deve essere presente un /status
entrypoint che risponde 200 se va tutto bene, uno status code diverso in caso contrario
se non http, l'healthcheck può essere un controllo su un file o su un processo
l'health check, se disponibile, deve essere inserito nel Dockerfile
Vanno tenute sempre in considerazione le raccomandazioni implementative di Agid, espresse nell'Allegato 4 alle Linee Guida di Interoperabilità.
Il log deve supportare almeno tre livelli: debug
, info
e error
.
debug:
deve essere comprensibile il flusso logico dell'applicativo
info:
ci si aspetta di leggere 1 riga per ogni evento o chiamata ricevuta dall'applicativo, che non ha presentato errori. 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.
error:
non si deve vedere nulla se non ci sono errori. 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.
Non mi schiare log http e log multiriga (stacktrace o similari): se si è costretti, per esempio inviare lo stacktrace a stderr
e riservare stdout
ai log a singola riga
Se possibile implementare log in formato json, ma solo se si può produrre solo del JSON, mischiare testo semplice e JSON non è di grande utilità.
Sul contenuto:
DEVE esserci l'istante della comunicazione in formato UTC (RFC 3339) e con separatori Z e T in maiuscolo.
DEVE contenere la URI o l'identificazione dell'operazione che si sta eseguendo
Dovrebbe contenere la tipologia della chiamata, ad esempio il verbo HTTP o se si tratta di una chiamata fatta da CLI o in un azione batch.
DEVE contenere l'esito della chiamata
Se è una chiamata fatta da un client DEVE essere inserito l'IP sorgente della chiamata (attenzione a rispettare eventuali header X-FORWARDED-XXX)
Se è una chiamata fatta per conto di un essere umano è possibile inserire un riferimento all'utente, ma NON DEVE contenere dati personali in chiaro (il codice fiscale ad esempio deve essere troncato al mssimo alle prime 7 lettere). Per avere un riferimento certo inserire lo userid
della piattaforma.
Se disponibile DEVE inserire l'identificatore unico della chiamata o dell'evento (event_id
).
DEVE contenere l'environment in cui sta girando il microservizio: boat-qa, boat-prod, local
, etc...
Il microservizio DEVE integrare Sentry per il monitoraggio centralizzato degli errori.
Una volta operativa l'integrazione è necessario:
abilitare l'integrazione con sentry sul repo gitlab
inserire regole di allerta per quel microservizio, se le regole esistenti non sono sufficienti
se http, deve esistere l'entrypoint /metrics
che espone metriche in formato prometheus.
un servizio può mostrare metriche sulle chiamate (status code, timing), anche se solitamente le possiamo prendere altrove (traefik), - un servizio deve mostrare metriche specifiche dell'app, in particolare delle condizioni di errore (counters) e dei tempi di risposta di eventuali servizi esterni richiamati da questo (histograms).
il servizio NON deve mostrare metriche che derivano ad esempio da errori 404, perché i bot che scansionano tutte le URL provocano infinite varianti che comportano la creazione di infinite metriche di errore
sui sistemi di deploy e orchestrazione che usiamo ci sono sempre dei modi di eseguire task stile cron, inutile reimplementare quella logica dentro la propria App.
è indispensabile invece rendere il proprio software idempotente: deve essere rieseguibile più volte sugli stessi dati senza fare danno.
il modo più semplice per implementare una chiamata a tempo è implementare un comando da cli che possa essere eseguito con la stessa immagine con cui gira l'applicativo.Questo applicativo viene poi schedulato nel nostro ambiente di produzione istruendo il nostro clustered cron. Per istruzioni sulla configurazione si rimanda alla documentazione di crazymax-cronjob.
in docker c'e' sempre un mapping tra esterno e interno, inutile parametrizzare questi aspetti in modo particolarmente flessibile: il controllo sul filesystem interno è sempre in mano allo sviluppatore del microservizio, quindi si può sempre usare un path interno del tipo /data
senza preoccuparsi tanto del fatto che all'esterno quel path diventerà qualcosa come /srv/data/qualcosa/altro/data.
Per le configurazioni e' importante usare sempre lo strumento configs
di docker swarm, senza ricorrere al volume mapping.
Analogamente per le credenziali o le parti segrete DEVE essere usato lo strumento dei secrets.
Ogni progetto può inserire nella CI attività di linting del codice e enforcemente dei coding styles
Ogni progetto DEVE includere la CI condivisa, nel seguente modo:
Eventuali step di test devono essere inclusi dopo questo snippet di codice e devono sfruttare la fase di build già eseguite, per esempio se viene fatta la build di un binario la si può sfruttare negli step successivi mediante un artifact.
La durata della CI non DEVE mai superare i 15 minuti e non dovrebbe mai superare i 5: questo perché soprattutto in condizioni di emergenza, fare un hotfix non può richiedere troppo tempo, è necessario che ogni step sia ben ottimizzato per ottenere questo scopo, per esempio sfruttando le build multi-stage di docker o la cache di Gitlab CI.
in caso di pubblicazione su Developers Italia, la correttezza del public-code.yml
deve essere testato con: