Il primo deploy di un’applicazione su Kubernetes in un ambiente di produzione è un’esperienza che ogni SysAdmin e DevOps Engineer ricorda. Spesso, è un battesimo del fuoco. La teoria è una cosa, la realtà del campo, con le sue pressioni e le sue sfide inattese, è tutt’altra. Questa è la storia di un venerdì pomeriggio che ha messo a dura prova le nostre certezze, un racconto di errori comuni e lezioni apprese a caro prezzo, ma fondamentali per chiunque si avventuri nel mondo di Kubernetes in un contesto enterprise.
Quando si parla di Kubernetes in produzione, il benessere del cluster non è un optional. Un downtime può significare perdite economiche ingenti, danni alla reputazione e notti insonni. Secondo un report del Cloud Native Computing Foundation (CNCF) del 2025, il 38% delle aziende che adottano Kubernetes in produzione ha subito almeno un downtime significativo nel primo anno a causa di errori di configurazione o mancanza di esperienza. La nostra storia rientra perfettamente in questa statistica. L’obiettivo qui non è spaventare, ma preparare. Condividere le criticità che abbiamo affrontato può aiutare altri a evitarle, trasformando un potenziale disastro in un’opportunità di apprendimento.
Prerequisiti / Ambiente di test
Questa guida si basa sull’esperienza maturata su un cluster Kubernetes in produzione, versione 1.29, con 5 nodi worker, gestito con Kubeadm su Ubuntu Server 22.04. L’applicazione era un set di microservizi Java Spring Boot, precedentemente containerizzati con Docker Compose. Il monitoraggio iniziale era affidato a Prometheus e Grafana, ma come vedremo, non era sufficientemente granulare per rilevare i problemi iniziali.
Il contesto: migrazione da Docker Compose a Kubernetes in 3 settimane
La decisione di migrare l’applicazione da Docker Compose a Kubernetes è stata dettata da esigenze di scalabilità e resilienza. L’ambiente legacy faticava a gestire i picchi di traffico e la gestione manuale dei container stava diventando un collo di bottiglia. La deadline era ambiziosa: tre settimane per la containerizzazione, la creazione dei manifest e il deploy in produzione. Un tempo irrealistico, ma imposto dall’alto. Questa fretta ha contribuito a farci saltare alcune best practice fondamentali, come la configurazione approfondita delle probe e la definizione rigorosa dei resource limit. Il team era composto da sviluppatori con una conoscenza di base di Docker e SysAdmin con esperienza su VM e networking, ma nuovi al mondo delle orchestratrici container.
Ore 0-2: deploy andato bene, tutto sembra funzionare
Il venerdì pomeriggio del deploy era iniziato con un cauto ottimismo. I manifest YAML erano stati applicati, i pod erano saliti, i servizi erano accessibili. I test iniziali, eseguiti dal team di QA, avevano dato esito positivo. Il sistema sembrava rispondere bene. La dashboard di Grafana mostrava CPU e memoria nella norma. Ricordo ancora il sospiro di sollievo collettivo. Avevamo superato il primo ostacolo. Il CEO, informato del successo, si preparava per la demo del lunedì mattina. Questa falsa sensazione di sicurezza è, a posteriori, uno dei pericoli maggiori nei deploy complessi: la mancanza di problemi immediati non significa assenza di problemi latenti.
Ore 2-6: CrashLoopBackOff su metà dei pod, causa ignota
Dopo circa due ore, i primi segnali di allarme. Alcuni servizi iniziavano a mostrare latenze anomale. Consultando kubectl get pods, abbiamo notato che circa la metà dei pod era in stato CrashLoopBackOff. Un errore generico, frustrante perché non puntava immediatamente a una causa specifica. I log dei pod, accessibili con kubectl logs , non mostravano errori applicativi evidenti, solo riavvii continui. Il team era nel panico. La demo del CEO si avvicinava, e la situazione peggiorava di minuto in minuto. La ricerca della causa era come cercare un ago in un pagliaio, con la pressione che aumentava.
Per diagnosticare i CrashLoopBackOff, il primo passo è sempre esaminare gli eventi del pod:
kubectl describe pod <nome-del-pod>
Questo comando mostra una panoramica dettagliata dello stato del pod, inclusi gli eventi recenti. Nel nostro caso, gli eventi indicavano riavvii continui senza una causa chiara, ma la chiave era leggerli con attenzione.
L’errore: liveness probe con timeout troppo basso su applicazione Java
Analizzando gli eventi e i log, abbiamo finalmente individuato il problema: le liveness probe. Erano configurate per controllare la salute del container troppo presto. Le nostre applicazioni Java, al primo avvio, impiegavano circa 45-60 secondi per inizializzare tutti i bean di Spring Boot e diventare effettivamente ‘ready’. La liveness probe, invece, era configurata con un initialDelaySeconds di soli 10 secondi. Kubernetes, vedendo che l’applicazione non rispondeva entro quel lasso di tempo, la dichiarava non healthy e la riavviava. Un ciclo infinito di riavvii, appunto, un CrashLoopBackOff.
Questo è un errore incredibilmente comune, specialmente con applicazioni che richiedono un certo tempo di startup. La soluzione è stata estendere l’initialDelaySeconds e anche il timeoutSeconds per dare all’applicazione il tempo necessario per avviarsi correttamente. La configurazione corretta per la nostra applicazione Java, dopo il fix, era la seguente:
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 60 # Esteso per dare tempo all'app di avviarsi
periodSeconds: 10
failureThreshold: 3
Il secondo problema: nessun resource limit configurato
Mentre eravamo impegnati a risolvere il problema delle liveness probe, è emerso un secondo, altrettanto grave, problema. I pod superstiti, quelli che non erano in CrashLoopBackOff, iniziavano a mostrare comportamenti anomali. Le latenze aumentavano, e alcuni servizi si bloccavano del tutto. Il monitoraggio del nodo mostrava un consumo di RAM alle stelle. Il problema? Nessun resource limit era stato configurato per i pod. Senza limiti, i container possono consumare tutte le risorse disponibili sul nodo, soffocando gli altri pod e il sistema operativo stesso. Questo porta a una situazione di OOMKilled (Out Of Memory Killed).
Ore 6-24: OOMKilled a cascata su tutto il nodo
Il risultato dell’assenza di resource limit è stato disastroso. Il kernel Linux, per proteggere il sistema dalla saturazione della memoria, ha iniziato a uccidere i processi che consumavano più RAM, inclusi i container Kubernetes. Questo ha innescato un effetto domino: i pod venivano terminati in modo imprevedibile, non per un errore applicativo, ma per esaurimento delle risorse fisiche del nodo. La dashboard di Grafana si è trasformata in un’ecatombe di metriche rosse. Eravamo in piena crisi.
Per verificare se un pod è stato terminato a causa di OOMKilled, si può esaminare lo stato precedente del container:
kubectl get pod <nome-del-pod> -o json | jq '.status.containerStatuses[].lastState'
Questo comando mostra lo stato del container prima dell’ultimo riavvio. Se reason è OOMKilled, abbiamo trovato il colpevole. La soluzione è stata definire requests e limits per CPU e memoria in tutti i manifest dei deployment. Questo garantisce che ogni pod abbia una quantità minima di risorse garantita (requests) e non superi una quantità massima (limits), prevenendo la saturazione del nodo.
Come abbiamo recuperato e stabilizzato il cluster
La notte è stata lunga. Abbiamo applicato le seguenti modifiche:
- Correzione Liveness Probes: Aumento significativo dell’
initialDelaySecondse deltimeoutSecondsper tutti i servizi Java. Questo ha permesso ai pod di avviarsi completamente prima di essere controllati. - Aggiunta Resource Limits: Abbiamo definito
requestselimitsper CPU e memoria per ogni container. Questo è stato un processo iterativo, iniziato con valori conservativi e poi affinato tramite monitoring. - Rollback Parziali: Per i servizi più critici, dove la situazione era irrecuperabile, abbiamo eseguito dei rollback a versioni precedenti dei manifest che non includevano i pod problematici, o che non erano ancora stati migrati.
- Monitoraggio Migliorato: Abbiamo configurato alert più aggressivi su Prometheus e Grafana per
CrashLoopBackOff,OOMKillede consumo eccessivo di risorse. - Comunicazione Costante: Mantenere informato il management è stato cruciale per gestire le aspettative e guadagnare tempo prezioso per la risoluzione.
Entro la mattina di sabato, il cluster era stabile e tutti i servizi erano tornati operativi. La demo del CEO è stata salvata, ma la lezione è rimasta impressa.
5 cose da fare PRIMA del primo deploy in produzione
Per evitare il nostro calvario, ecco 5 punti chiave da implementare prima di spingere il primo workload su Kubernetes in produzione:
- Testare a Fondo le Liveness e Readiness Probe: Non dare per scontato che le probe funzionino. Testale in un ambiente di staging che replichi la produzione. Considera i tempi di avvio reali delle tue applicazioni. Un report del CNCF del 2025 indica che il 25% dei problemi di disponibilità in Kubernetes è correlato a probe mal configurate.
- Definire Resource Requests e Limits Rigorosi: Non deployare mai un container senza aver configurato
resources.requestseresources.limitsper CPU e memoria. Questo è fondamentale per la stabilità del cluster e per prevenireOOMKilled. Inizia con stime e affina tramite monitoring e stress test. - Implementare un Monitoring e Alerting Robusto: Non basta avere Prometheus e Grafana. Configura alert specifici per stati come
CrashLoopBackOff,OOMKilled,ImagePullBackOffe per soglie critiche di CPU/memoria a livello di pod e nodo. - Avere un Piano di Rollback Chiaro e Testato: Cosa succede se il deploy va male? Hai un modo rapido e affidabile per tornare alla versione precedente? I deploy falliscono, e un piano di rollback è la tua ultima linea di difesa.
- Formazione del Team: Assicurati che il tuo team abbia una comprensione solida non solo di come funzionano i comandi
kubectl, ma anche delle implicazioni del ciclo di vita dei pod, della gestione delle risorse e delle strategie di deploy. La mancanza di formazione è una delle principali cause di incidenti in ambienti complessi.
Errori comuni e troubleshooting
Durante la fase di troubleshooting, abbiamo incontrato altri errori comuni che vale la pena menzionare:
- ImagePullBackOff: Spesso indica un errore nel nome dell’immagine, nel tag o problemi di autenticazione con il registry.
kubectl describe podè il tuo migliore amico qui. - Pending Pods: I pod rimangono in stato
Pendingse il scheduler non riesce a trovare un nodo su cui allocarli, spesso per mancanza di risorse (CPU/Memoria) o per incompatibilità connodeSelector/taints and tolerations. - CrashLoopBackOff (non da probe): Se non è la liveness probe, potrebbe essere un errore nell’applicazione stessa (es. errore di connessione al database, file di configurazione mancanti). Controlla sempre i log dell’applicazione.
FAQ — Domande Frequenti
Come posso testare le liveness probe senza deployare in produzione?
Puoi testare le liveness probe in un ambiente di staging o di sviluppo. Utilizza strumenti come curl o wget all’interno del container per simulare il comportamento della probe e verificare i tempi di risposta. È fondamentale replicare le condizioni di carico della produzione per avere risultati realistici.
Qual è la differenza tra resource requests e resource limits?
Resource requests sono la quantità minima di CPU e memoria che un pod richiede per funzionare. Il scheduler Kubernetes garantisce che queste risorse siano disponibili prima di allocare il pod. Resource limits sono la quantità massima di CPU e memoria che un pod può consumare. Se un pod supera i suoi limiti di memoria, viene terminato (OOMKilled). Se supera i limiti di CPU, viene throttled, ma non terminato.
Come posso implementare un piano di rollback efficace?
Un piano di rollback efficace include l’uso di Deployment strategici, che permettono di specificare revisionHistoryLimit per mantenere versioni precedenti del tuo deployment. In caso di problemi, kubectl rollout undo deployment/ ti permette di tornare rapidamente alla versione precedente. Assicurati di testare questa procedura.
Quali strumenti di monitoring sono essenziali per Kubernetes?
Prometheus per la raccolta di metriche, Grafana per la visualizzazione delle dashboard e Alertmanager per la gestione degli alert sono lo stack standard. Strumenti come Loki per la raccolta log e Jaeger per il distributed tracing sono altrettanto utili in ambienti complessi per una diagnostica approfondita.
Conclusioni con takeaway operativi
Il nostro venerdì nero con Kubernetes è stato un’esperienza dolorosa ma incredibilmente istruttiva. Ha rafforzato la consapevolezza che, in ambienti complessi, la fretta è il peggior nemico e che ogni dettaglio di configurazione conta. I takeaway operativi sono chiari: non sottovalutare mai l’importanza di una configurazione accurata delle probe, di una gestione rigorosa delle risorse e di un robusto sistema di monitoring. La preparazione è la chiave per trasformare un potenziale disastro in un successo.
Leggi anche: Kubernetes Networking: DNS, Services, Ingress — Guida Pratica Sysadmin (2026)
Leggi anche: Hardening SSH Linux: Guida Completa 2026 (10 Impostazioni Critiche)
Leggi anche: CI/CD Pipeline Sicura: Checklist 12 Punti per GitLab e GitHub Actions (2026)
Aggiornato: luglio 2026