I Microservizi Fanno per Te? Scopriamolo Insieme.


Quando Quel Meetup a Reggio Emilia Ha Cambiato il Mio Modo di Pensare all’Architettura

Ricordo la prima volta che ho partecipato a un Fullstack Meetup a Reggio Emilia. Ero in piedi in un cerchio di forse 12 sviluppatori, quando un senior architect tutto d’un pezzo ha iniziato a sbraitare. “Ti bastano due righe di bash”, ha detto. “Due righe, e puoi deployare un monolita che serve milioni di utenti.”

Non aveva torto. Ma il disagio in quel cerchio era palpabile. Noi ingegneri abbiamo un tratto bellissimo, a volte tossico: bramiamo la complessità. Se una soluzione sembra troppo semplice, presumiamo che sia “da junior”. Ci convinciamo di dover gestire ogni possibile scenario, ogni problema di scaling ipotetico che non si è ancora verificato, ogni caso limite che vive solo nei nostri sogni di ansia architettonica.

Un anno e innumerevoli incidenti in produzione dopo, ho imparato una verità umiliante:

La Dura Verità

I microservizi non sono una destinazione, sono una scelta deliberata di problemi.

E la domanda che nessuno vuole fare è: Il tuo team è davvero abbastanza grande da giustificare il dolore che sto per descrivere?


Quando Amazon Ha Detto “In Realtà, Lascia Perdere”

Sai che il tuo settore ha un problema quando Amazon—l’azienda che ha letteralmente inventato la piattaforma cloud su cui girano i microservizi—pubblica un post sul blog intitolato “Scaling up the Prime Video audio/video monitoring service and reducing costs by 90%.”

Il loro team di monitoraggio aveva costruito il sistema moderno “perfetto”: serverless, disaccoppiato, distribuito. AWS Step Functions che orchestrano Lambda functions. Il tipo di diagramma architetturale che ti fa prendere upvote su Reddit.

Tranne che stava emorraggiando soldi e non poteva scalare.

Il problema? Ogni. Singolo. Hop. Di. Rete. Stavano serializzando i dati, memorizzandoli in S3, recuperandoli per la funzione successiva, ancora e ancora. Già solo l’overhead di orchestrazione li stava uccidendo. Così hanno fatto l’impensabile: hanno refactorizzato tornando a un monolita.

I risultati erano sbalorditivi:

  • Riduzione dei costi del 90% (non è un errore di battitura)
  • Enormi miglioramenti di scalabilità grazie all’elaborazione in memoria
  • Codice più semplice niente più ginnastica del layer di orchestrazione
Tip

Pensa a questo: Se Amazon — il cui modello di business consiste letteralmente nell’affittarti infrastruttura distribuita — sta consolidando i servizi, devi chiederti perché tu ti stia affrettando a frammentare i tuoi.


La Tassa Nascosta del Sogno Distribuito

Spesso parliamo del “Microservice Premium” — la tassa intrinseca pagata in complessità operativa e carico cognitivo. Questa tassa viene riscossa nei piccoli, dolorosi momenti della giornata di uno sviluppatore:

1. La Notte in cui RabbitMQ Mi Ha Fatto Mettere in Discussione le Mie Scelte di Carriera

Lascia che ti racconti di quella volta che ho passato un’intera serata a debuggare quello che si è rivelato essere… un timeout di keepalive.

Avevamo RabbitMQ in esecuzione usando @cloudamqp/amqp-client in Node.js. Tutto sembrava perfetto nei log — messaggi che fluivano, servizi che comunicavano magnificamente. Poi ho notato qualcosa di strano: la connessione cadeva e si riconnetteva ogni 2 minuti. Non “circa 2 minuti”. Non “tra 1-3 minuti”. Esattamente 120 secondi. Come un orologio.

Ecco cosa ho imparato, a faccia in giù nella documentazione di CloudAMQP:

Il Problema del Keepalive WebSocket:

  • La libreria client stava usando WebSocket sotto il cofano
  • I WebSocket richiedono pacchetti di keepalive periodici o vanno in timeout
  • Se non stai attivamente inviando/ricevendo messaggi, la connessione appare “idle” ai proxy intermedi
  • Il timeout di 2 minuti era il proxy che si arrendeva sulla nostra connessione “morta”
La Penalità di Latenza

Le chiamate a RabbitMQ aggiungono distanza fisica e hop di rete. Lo sviluppo sembra veloce localmente, ma la produzione perde prestazioni.

Codice → Rete → Coda → Servizio B (Latenza ↑)

E il divertimento non finisce qui:

  • La danza di riconnessione: Ogni disconnessione attivava un ciclo di riconnessione completo — ristabilire i canali, ridichiarare gli exchange, riassociare le code. Tutto quell’overhead, due volte al minuto.
  • L’inferno del configuration drift: Se il Servizio A dichiara un exchange come durable: true e il Servizio B dichiara lo stesso exchange con durable: false, RabbitMQ lancia:
    PRECONDITION_FAILED
    e blocca l’intera tua pipeline
Lezione Imparata

Una volta ho passato tre ore a capire perché un servizio non si avviasse, solo per scoprire che era un mismatch del flag auto-delete dell’exchange. Il messaggio di errore era inutile. La documentazione era scarsa. Stack Overflow non aveva niente. Mi sono sentito molto solo.

2. La Saga delle Dipendenze

La “Diversità Tecnologica” promessa dai microservizi spesso si trasforma in un incubo poliglotta.

Caso esemplare: La libreria node-canvas:

  • Installarla richiede dipendenze profonde a livello di sistema come Cairo, Pango e libjpeg
  • Se sviluppi su Mac ma deployai su un container Alpine Linux, i binari pre-compilati falliscono a causa del conflitto libc vs. musl
  • Sei costretto a installare Python, g++ e make nelle tue immagini di produzione, gonfiandole e aumentando i tempi di build da secondi a oltre 10 minuti
Caution

Questo non è “diversità tecnologica” — è l’inferno delle dipendenze con passi extra.

3. L’Incidente del Container Windows (Oppure: Perché Dovresti Sviluppare su Linux)

Se non hai mai dovuto eseguire Docker su Windows Server, congratulazioni. Sei benedetto.

Per il resto di noi che ci siamo passati: ricordi quella volta che hai provato a eliminare un’immagine Docker corrotta e Windows ha letteralmente detto “Accesso Negato” all’account Administrator?

Ecco la sequenza da incubo:

  1. La directory windowsfilter di Docker mantiene un file lock
  2. Anche il tuo antivirus mantiene un file lock (perché? chi lo sa)
  3. Il layer diventa corrotto
  4. Provi a eliminarlo: Accesso Negato
  5. Accedi come Administrator: Accesso Negato
  6. Piangi un po’ 😢
  7. Cerchi su Google per 45 minuti e trovi uno script PowerShell losco che prende forzatamente la proprietà dall’account SYSTEM
  8. Lo esegui, pregando di non brickare l’intera installazione Docker
  9. Funziona 🎉
  10. Inizi immediatamente a ricercare alternative Linux
Info

Vorrei esagerare. Ho ancora quello script PowerShell nei segnalibri.

4. Le Ops Non Sono Opzionali

Se adotti i microservizi, non puoi fare deployment manuali. La CI/CD diventa non negoziabile.

Ecco cosa succede quando provi a fare il deploy manuale di 12 microservizi:

  1. Ti connetti in SSH al primo server e fai il pull dell’ultima immagine Docker
  2. Il Servizio #1 si avvia con successo
  3. Passi al Servizio #2, ti rendi conto di aver dimenticato di aggiornare le variabili d’ambiente
  4. Il Servizio #2 crasha all’avvio
  5. Il Servizio #1 sta ora chiamando la vecchia versione dell’API del Servizio #2
  6. Gli utenti iniziano a segnalare errori
  7. Risolvi il Servizio #2, ma ora il Servizio #3 dipende da entrambi, e non sei sicuro quali combinazioni di versioni siano compatibili
  8. Sono le 23:00. Stai ancora facendo il deploy.

Noi usiamo Azure DevOps, e una volta configurati gli agent self-hosted, il miglioramento della qualità della vita è stato enorme. Ma c’è un trucco: se fai self-hosting degli agent, sei proprietario dell’ambiente di build. Ciò significa:

  • Gestire le versioni di Docker tra gli agent
  • Mantenere aggiornati i build tool (Node, .NET SDK, Python)
  • Debuggare perché l’Agent #3 builda con successo ma l’Agent #1 fallisce con esattamente lo stesso codice
  • Monitorare lo spazio su disco perché i layer di Docker riempiono i drive più velocemente di quanto ti aspetti

5. Logging: Il Buco Nero

Nei “vecchi tempi”, potevo fare SSH su un server e fare tail -f su un file di log. Semplice. Diretto. Efficace.

In un mondo di microservizi, ecco com’è fare il debug di una singola richiesta utente:

  • Step 1: L’utente segnala un errore alle 14:23:47
  • Step 2: Controllo i log dell’API Gateway… richiesta instradata all’Order Service
  • Step 3: SSH nel container dell’Order Service… il log dice che ha chiamato il Payment Service
  • Step 4: SSH nel Payment Service… nessun errore evidente, ma ha chiamato l’Inventory Service
  • Step 5: SSH nell’Inventory Service… niente di insolito, ma aspetta—i timestamp sono in UTC, l’Order Service era in CET, il Payment Service era in PST perché qualcuno aveva cambiato il timezone in quell’immagine Docker per “test”
  • Step 6: Passo 45 minuti a convertire mentalmente i timezone per correlare tre stream di log
  • Step 7: Finalmente trovo l’errore: un timeout di 500ms nell’Inventory Service che si verifica solo sotto carico

La brutale verità:

  • Senza logging strutturato (JSON), non puoi parsare i log programmaticamente
  • Senza correlation ID, non puoi tracciare le richieste tra i servizi
  • Senza logging centralizzato (Sentry, Datadog, ELK), stai giocando all’archeologia con file di testo

Se non investi in observability dal Giorno 1, stai volando alla cieca. E credimi, correlare manualmente i timestamp tra diversi stream di log alle 2 di notte è un tipo speciale di inferno.


Il Baratro dell’Orchestrazione: K8s vs. Rancher

Una volta che hai i container, devi eseguirli. Kubernetes (K8s) ha vinto la guerra, ma è una “bestia” con una curva di apprendimento verticale.

K8s vs. Rancher: Un Confronto Rapido

CategoriaK8s Gestito (AKS/EKS)SUSE Rancher
Control Plane✅ Gestito dal cloud provider⚠️ Gestito da te (Single Point of Failure)
UI Developer❌ Spesso lenta e disgiunta✅ Vista unificata “Cluster Explorer”
Multi-Cloud❌ Limitato al provider✅ “Single pane of glass” tra Azure, AWS e on-prem

La mia opinione: Se sei un team piccolo (< 20 dev), il K8s gestito (AKS/EKS) vale il costo. Se hai bisogno di multi-cloud o hai forte esperienza ops, Rancher ti dà più controllo—ma sei anche responsabile quando le cose si rompono.

Check di Realtà

La maggior parte degli sviluppatori (me compreso all’inizio) ammette di sapere solo abbastanza per “modificare qualche dannato file YAML”. Questo crea un pericoloso single-point-of-failure dove l’intero team è bloccato in attesa che l’unico “esperto K8s” torni dalle vacanze e spieghi perché il pod è bloccato in CrashLoopBackOff anche se funziona sulla mia macchina.


La Filosofia della “Tecnologia Noiosa”

L’essay “Choose Boring Technology” di Dan McKinley è una luce guida. Ogni azienda ha “innovation token” finiti.

La Scelta:

  • Noioso (Postgres, Monoliti, C#): Modalità di fallimento note; puoi cercare l’errore su Google e trovare la risposta
  • Eccitante (Service Mesh all’Avanguardia): “Incognite sconosciute” che ti costringono a debuggare il codice del vendor invece della tua business logic
Tip

Spendi i tuoi innovation token con saggezza. Non ne hai molti.


Il Verdetto: Inizia con il Monolita Modulare. Sul Serio.

Ecco il mio consiglio, guadagnato attraverso cicatrici di produzione:

Inizia con un Monolita Modulare. Non perché sei “cauto” o “junior”—perché sei intelligente.

Il Percorso Che Raccomando Davvero:

  1. Costruisci Prima un Monolita Modulare: Usa namespace rigidi, confini chiari e principi di domain-driven design in una singola unità deployabile
  2. Tratta i Confini Come API: Anche all’interno del tuo monolita, progetta le interfacce dei moduli come se un giorno potessero diventare chiamate di rete
  3. Investi Presto in Observability: Logging strutturato, correlation ID e metriche sono preziosi sia che tu sia distribuito o meno
  4. Deployalo. Shippalo. Facci soldi.

Poi, solo se colpisci uno di questi vincoli rigidi:

  • Hai oltre 100 sviluppatori e i team si bloccano a vicenda sui deployment
  • Hai bisogno di profili di scaling genuinamente diversi (il tuo video processing ha bisogno di macchine a 32 core mentre la tua API gira bene su istanze a 2 core)
  • Hai requisiti di compliance che impongono l’isolamento fisico dei servizi

…considera i microservizi.

Ma se percorri quella strada, abbraccia la complessità:

  • Ti stai iscrivendo per tutto ciò che ho descritto sopra: le sessioni di debug di RabbitMQ, gli incubi dei permessi dei file Docker, la complessità della pipeline di deployment
  • Hai bisogno di expertise DevOps dedicata — non “Bob che conosce Docker”
  • Hai bisogno di una piattaforma di logging/observability dal Giorno 1 — non “quando ci arriviamo”
  • Devi accettare che i tuoi sviluppatori junior passeranno il loro primo mese solo a capire come eseguire l’intero sistema localmente
La Dura Verità

I microservizi non rendono il tuo sistema più semplice. Scambiano la complessità del codice con la complessità operativa. Assicurati di essere pronto per quello scambio.


A volte, la decisione più “senior” che puoi prendere è scrivere due righe di bash, deployare un monolita, e tornare a casa in orario.

Tip

Credimi, il tuo io futuro, quello che riceve una chiamata perché i certificati del service mesh sono scaduti, ti ringrazierà.