DevOps24 Giugno 20269 min di lettura

Ho migrato un'app legacy su Kubernetes: ecco cosa è andato storto (e come l'ho risolto)

Ci sono progetti che ti insegnano più di dieci corsi online messi insieme. Questo è stato uno di quelli. Ho completato la migrazione di un'applicazione monolitica su Kubernetes per un istituto finanziario (chiamerò il cliente "FinCo" per riservatezza). L'app era in produzione da quasi dodici anni, girava su server fisici in un datacenter romano, e ogni settimana ci regalava almeno un downtime.

Quello che pensavo sarebbe stato un progetto di tre mesi si è trasformato in sette mesi di problem solving continuo. Racconto tutto qui, perché la comunità tecnica italiana ha bisogno di più post-mortem onesti e meno slide di marketing.


Il contesto: un monolite da domare

Un classico monolite Java EE, scritto tra il 2012 e il 2015 da un team che nel frattempo era quasi completamente cambiato. Nessuna documentazione aggiornata. Nessun test automatizzato degno di questo nome. Una base dati PostgreSQL con circa quattrocento tabelle, alcune con nomi come tabella_nuova_2, tabella_nuova_3, tabella_finale_davvero.

I downtime settimanali erano causati quasi sempre da memory leak accumulati — il "fix" era riavviare il server alle 3 di notte di sabato.


Errore 1: la containerizzazione non è "solo un Dockerfile"

Avevo stimato due settimane per la fase di containerizzazione. Ne ho impiegate sei.

Il problema non era scrivere il Dockerfile — quello era tutto sommato lineare. Il problema era che l'applicazione aveva dipendenze implicite che nessuno aveva documentato: path assoluti hardcodati nel codice, dipendenze su processi esterni lanciati tramite script shell, una libreria nativa compilata per una versione specifica di Red Hat che non coincideva con l'immagine base scelta.

Come l'ho scoperto

Il container partiva, sembrava stabile, e poi dopo venti minuti si rompeva in modi sempre diversi. Giorni di debug prima di capire che un processo figlio scriveva su un path che nel container non esisteva.

La lezione: prima di toccare Kubernetes, fai un audit completo delle dipendenze. Non solo le librerie nel pom.xml — tutto. File di configurazione letti all'avvio, processi di sistema attesi, path, permessi. Tutto.


Errore 2: i secrets in produzione

Ho messo le credenziali del database in un ConfigMap invece che in un Secret. Sembrava una scorciatoia ragionevole in fase di test. Il problema è che quella configurazione è finita in produzione.

Non è successo niente di grave — le credenziali erano accessibili solo all'interno del cluster. Ma ho violato un principio fondamentale.

La gestione corretta dei secrets

La soluzione: Kubernetes Secrets nativi per le credenziali di base, integrati con il secret manager del cloud provider per i dati più sensibili. Ogni pod accede ai secrets tramite variabili d'ambiente iniettate al momento dell'avvio, mai hardcoded nelle immagini. Ho anche aggiunto un controllo nel pipeline CI/CD che blocca il deploy se trova pattern simili a credenziali nel codice.


Errore 3: il deploy in produzione senza staging adeguato

Avevamo uno staging environment, tecnicamente. Era un namespace separato nello stesso cluster, con dati sintetici. Sembrava sufficiente. Non lo era.

Il primo deploy in produzione ha causato tre ore di disservizio. Il motivo: la configurazione del database di produzione aveva differenze rispetto a staging che non avevo mai verificato — versioni diverse di estensioni PostgreSQL, collation differente su alcune colonne, un trigger che in staging non esisteva.

Cosa è cambiato dopo

  • Lo staging gira su un cluster separato, non solo su un namespace diverso
  • Prima di ogni deploy importante, facciamo un restore anonimizzato dei dati di produzione in staging
  • Il rollout usa RollingUpdate con maxSurge: 1 e maxUnavailable: 0
  • Aggiunti readiness probe e liveness probe su tutti i container

Come l'abbiamo risolto: il processo finale

CI/CD con GitHub Actions: ogni push su main esegue test, costruisce l'immagine Docker, la pusha sul registry e aggiorna il deployment su staging automaticamente.

Rollout graduale in produzione: richiede approvazione manuale nel pipeline. Dopo l'approvazione monitoriamo le metriche per trenta minuti prima di considerare il deploy completato.

Osservabilità: Prometheus e Grafana per le metriche, alert configurati su latenza, error rate e utilizzo delle risorse. Prima di questa migrazione, FinCo scopriva i problemi quando li segnalava un utente. Ora li scopriamo noi per primi.


I risultati dopo 12 mesi

99.9%Uptime
-60%Costi infrastruttura
22 minTempo di deploy

Zero downtime non pianificati dalla messa in produzione definitiva. Il responsabile IT di FinCo, che all'inizio non aveva mai usato kubectl, oggi gestisce i deploy di routine in autonomia.


Cosa farei diversamente

  1. Fai l'audit prima di scrivere una riga di Dockerfile. Documenta ogni dipendenza, ogni assunzione sul filesystem, ogni processo esterno.
  2. Valuta seriamente se Kubernetes è la scelta giusta. Per una PMI senza competenze DevOps interne, un PaaS managed potrebbe dare il 70% dei benefici con il 20% della complessità.
  3. Investi nello staging prima di toccare la produzione. Un ambiente di staging che non rispecchia la produzione è un generatore di false sicurezze.

Stai affrontando una migrazione simile?

Ti aiuto a pianificare la migrazione evitando gli errori più costosi. Consulenza gratuita di 30 minuti.

Parliamone →