Articoli

Microservizi: il lavoro che non avevo messo in conto

Una conversazione a un meetup, un timeout RabbitMQ e cosa mi hanno insegnato sulle architetture distribuite.

Durante un incontro di Reggio Emilia Fullstack, qualcuno sosteneva che per fare il deployment di un monolite potessero bastare un paio di righe di Bash. Ricordo il disagio che aveva suscitato. Eravamo lì a parlare di architettura, e qualcuno ci stava suggerendo che forse la stavamo complicando più del necessario.

Quella conversazione mi è rimasta in testa. Anche le serate passate con RabbitMQ, build di container e Azure DevOps. Mi piace ancora lavorare su questi sistemi. Oggi, però, scelgo con più attenzione le complicazioni da affrontare.

La connessione RabbitMQ che non voleva restare connessa

Ho passato una serata a seguire una connessione RabbitMQ che continuava a disconnettersi e riconnettersi. I messaggi passavano, i log sembravano rassicuranti, poi la connessione cadeva di nuovo. Proprio quella regolarità era l’indizio.

Il client utilizzava WebSocket e un timeout di inattività del proxy contribuiva al problema. Ero partito dalla libreria client; dovevo guardare anche il percorso della connessione e cosa la teneva attiva.

In un altro caso, servizi diversi dichiaravano lo stesso exchange con impostazioni incompatibili. Parametri come durable e autoDelete costituiscono un contratto tra produttori, consumatori e broker. Una discrepanza può far fallire la dichiarazione con PRECONDITION_FAILED e chiudere il canale.

Nessuno dei due problemi sembrava particolarmente interessante su un diagramma. Entrambi contavano quando l’applicazione doveva funzionare. Ora dedico più attenzione fin dall’inizio al recupero delle connessioni e alla coerenza della configurazione del broker.

L’ambiente di build fa parte del sistema

Le dipendenze native complicano deployment Node.js altrimenti familiari. Librerie come node-canvas dipendono da librerie di sistema, e un binario compilato per un ambiente potrebbe non funzionare in un altro.

Per le immagini container servono scelte consapevoli su sistema operativo, dipendenze native e strumenti di compilazione. Le build multi-stage possono separare gli strumenti dal runtime, ma la compilazione deve comunque essere riproducibile.

Lavorare con container Windows mi ha insegnato anche che i problemi alle immagini e al filesystem possono richiedere verifiche specifiche della piattaforma. Le differenze tra sviluppo e produzione vanno comprese e testate prima del rilascio.

I rilasci richiedono coordinamento

Servizi indipendenti non eliminano le dipendenze tra i rilasci. Una modifica a un’API, una variabile d’ambiente mancante o versioni incompatibili possono interrompere un flusso utente.

Ho lavorato con Azure DevOps e agenti self-hosted. Offrono controllo sull’ambiente di build, insieme alla responsabilità di mantenerlo:

  • Mantenere coerenti le versioni dei runtime e degli strumenti.
  • Aggiornare gli strumenti container e le dipendenze native.
  • Monitorare lo spazio su disco e rimuovere in sicurezza gli artefatti obsoleti.
  • Rendere ripetibili configurazione e passaggi di rilascio.

La pipeline contribuisce alla manutenibilità dell’applicazione e va progettata insieme ai servizi.

Ai log serve un contesto comune

Un errore che attraversa più servizi è difficile da analizzare se ciascuno produce testo scollegato dagli altri e usa un fuso orario diverso.

Log strutturati, timestamp coerenti e correlation ID consentono di seguire una richiesta. Una raccolta centralizzata permette di cercare questi dati nello stesso posto. Conta soprattutto che rispondano alle domande pratiche: quale richiesta è fallita, quale percorso ha seguito e quale dipendenza era coinvolta.

Definire i confini prima di distribuire

Per molte applicazioni parto da un monolite modulare: confini espliciti tra moduli all’interno di un solo deployment. Anche questi confini richiedono interfacce chiare e test.

Separare i servizi diventa utile per esigenze concrete, come rilasci indipendenti, requisiti di risorse diversi o vincoli di isolamento. Servono anche un piano per la configurazione, la gestione dei guasti nella messaggistica, l’osservabilità e l’operatività.

Ripenso ancora a quel meetup quando guardo un diagramma. Prima di aggiungere un altro servizio, voglio saper spiegare quale problema risolve e chi dovrà occuparsene.