Risoluzione dei Colli di Bottiglia Comuni nelle Performance di Kubernetes

Impara a diagnosticare e risolvere sistematicamente i comuni colli di bottiglia delle performance di Kubernetes, inclusi throttling della CPU, OOMKill della memoria e ritardi di scheduling. Questa guida fornisce comandi pratici e best practice per ottimizzare le richieste di risorse, ottimizzare lo scaling HPA e identificare i vincoli sottostanti del cluster per garantire prestazioni ottimali dell'applicazione.

Risoluzione dei Colli di Bottiglia Comuni nelle Performance di Kubernetes

I problemi di performance di Kubernetes raramente si annunciano come "Kubernetes è lento". Vedi un rollout che si blocca, un'API che improvvisamente restituisce errori 5xx, una coda che smette di svuotarsi, o pod che sembrano sani mentre gli utenti si lamentano della latenza. Il cluster è solo una parte di questa storia, ma è la parte che puoi ispezionare con un insieme coerente di comandi.

Il trucco è evitare di saltare direttamente alla dimensione del nodo o al numero di repliche. Prima decidi che tipo di collo di bottiglia hai: throttling della CPU, pressione della memoria, ritardo di scheduling, scaling lento, latenza di rete, latenza di storage o un'applicazione che sta semplicemente facendo più lavoro del previsto.

Fase 1: Identificare i Sintomi

Prima di immergerti in componenti specifici, definisci chiaramente il degrado delle performance osservato. I sintomi comuni spesso rientrano in una di queste categorie:

  • Distribuzioni/Aggiornamenti Lenti: La creazione dei pod richiede un tempo eccessivo o gli aggiornamenti progressivi si bloccano.
  • Applicazioni Non Reattive: I pod sono in esecuzione ma non rispondono al traffico a livello di applicazione (es. alta latenza, errori 5xx).
  • Picchi Elevati di Risorse: Picchi inspiegabili di utilizzo di CPU o memoria su nodi o distribuzioni specifiche.
  • Ritardi di Scheduling: I nuovi pod rimangono nello stato Pending a tempo indeterminato.

Fase 2: Diagnosticare i Vincoli delle Risorse (CPU e Memoria)

La cattiva gestione delle risorse è la causa più frequente dei problemi di performance di Kubernetes. Richieste e limiti impostati in modo errato portano a throttling o OOMKill.

1. Verifica dell'Utilizzo e dei Limiti delle Risorse

Inizia ispezionando le allocazioni delle risorse per l'applicazione interessata usando kubectl describe e kubectl top.

Controllo Attuabile: Confronta le requests e i limits con l'utilizzo effettivo riportato dai server di metriche.

# Ottieni l'utilizzo delle risorse per tutti i pod in un namespace
kubectl top pods -n <namespace>

# Esamina le richieste/limiti delle risorse per un pod specifico
kubectl describe pod <pod-name> -n <namespace>

Ispeziona anche il carico di lavoro proprietario per capire se il problema riguarda un pod o l'intero Deployment:

kubectl get deploy <deployment-name> -n <namespace> -o yaml
kubectl get pods -n <namespace> -l app=<label> -o wide

Se solo un pod è lento e si trova su un nodo diverso dagli altri, è più probabile una pressione a livello di nodo. Se ogni replica è lenta, le impostazioni delle risorse, le dipendenze a valle o il comportamento dell'applicazione meritano più attenzione.

2. Throttling della CPU

Se l'utilizzo della CPU di un container raggiunge ripetutamente il suo limite definito, il kernel lo limiterà, portando a gravi picchi di latenza anche se il nodo stesso ha capacità disponibile. Questo viene spesso scambiato per una carenza generale di CPU.

Suggerimento per la Diagnosi: Cerca risposte ad alta latenza, anche quando kubectl top non mostra un utilizzo della CPU al 100% sul nodo. Il throttling avviene per container.

Per una conferma più approfondita, usa il tuo sistema di metriche se espone le metriche di throttling della CPU del container. Nelle configurazioni basate su Prometheus, i team spesso monitorano metriche come i periodi di CPU limitati insieme alla latenza delle richieste. L'utilizzo grezzo della CPU da solo può nascondere il throttling perché un container può essere limitato prima ancora che sembri utilizzare un core completo del nodo.

Risoluzione:

  • Aumenta il limit della CPU se il carico di lavoro richiede legittimamente più potenza di elaborazione.
  • Se l'applicazione è in busy-waiting, ottimizza il codice dell'applicazione piuttosto che aumentare semplicemente i limiti.
  • Considera la rimozione dei limiti della CPU per alcuni servizi sensibili alla latenza mantenendo le richieste di CPU, se questo è in linea con la tua policy di piattaforma. Ciò evita un throttling rigido pur fornendo allo scheduler informazioni utili per il posizionamento.

3. Pressione della Memoria e OOMKill

Se un container supera il suo limite di memoria, Kubernetes avvia un kill Out-Of-Memory (OOM), riavviando il container ripetutamente.

Diagnosi: Controlla lo stato del pod per riavvii frequenti (controlla la colonna RESTARTS in kubectl get pods) ed esamina i log per eventi OOMKilled.

# Controlla gli eventi recenti per OOMKill
kubectl get events --field-selector involvedObject.name=<pod-name> -n <namespace>

Risoluzione:

  • Se gli OOMKill sono frequenti, aumenta immediatamente il limit della memoria.
  • Per correzioni a lungo termine, profila l'applicazione per trovare e correggere le perdite di memoria o ridurre la dimensione dell'heap.

La memoria si comporta diversamente dalla CPU. La CPU può essere limitata e il processo continua a funzionare lentamente. Le violazioni del limite di memoria di solito finiscono con l'uccisione del processo. Questo fa sì che i problemi di memoria sembrino incidenti di affidabilità: riavvii, connessioni interrotte, cache fredde e richieste in-flight fallite.

Best Practice: Imposta le Richieste con Saggezza. Assicurati che le requests di risorse siano impostate ragionevolmente vicine all'utilizzo minimo previsto. Se le requests sono troppo basse, lo scheduler potrebbe sovraccaricare il nodo, portando a contesa quando tutti i pod raggiungono le loro richieste simultaneamente.

Fase 3: Indagare sui Colli di Bottiglia di Scheduling

Quando i pod rimangono nello stato Pending, il problema risiede nell'incapacità dello scheduler di trovare un nodo adatto.

1. Analisi dei Pod in Attesa

Usa kubectl describe pod su un pod in attesa per leggere la sezione Events. Questa sezione di solito contiene una spiegazione chiara per il fallimento dello scheduling.

Messaggi Comuni dello Scheduler:

  • 0/3 nodes are available: 3 Insufficient cpu. (Problema di capacità del nodo)
  • 0/3 nodes are available: 3 node(s) had taint {dedicated: infra}, that the pod didn't tolerate. (Disallineamento Taints/Tolerations)
  • 0/3 nodes are available: 1 node(s) had taint {NoSchedule: true}, that the pod didn't tolerate. (Pressione o manutenzione del nodo)

2. Saturazione delle Risorse del Cluster

Se lo scheduling è ritardato a causa della mancanza di CPU/Memoria, il cluster non ha capacità aggregata sufficiente.

Risoluzione:

  • Aggiungi più nodi al cluster.
  • Verifica che l'utilizzo del nodo non sia artificialmente alto a causa di richieste di risorse configurate male (vedi Fase 2).
  • Usa Cluster Autoscaler (CA) se eseguito su provider cloud per aggiungere dinamicamente nodi quando i pod in attesa si accumulano.

Se Cluster Autoscaler è abilitato ma i nodi non vengono aggiunti, leggi i suoi log prima di presumere che il provider cloud sia rotto. L'Autoscaler potrebbe rifiutarsi di aggiungere nodi perché i gruppi di nodi hanno raggiunto la loro dimensione massima, il pod in attesa ha vincoli che nessun gruppo di nodi può soddisfare o le quote impediscono nuove istanze.

Fase 4: Problemi di Performance nei Meccanismi di Scaling

Lo scaling automatico dovrebbe reagire rapidamente, ma configurazioni errate negli Horizontal Pod Autoscaler (HPA) o Vertical Pod Autoscaler (VPA) possono causare problemi.

1. Ritardo dell'Horizontal Pod Autoscaler (HPA)

HPA si basa sul Metrics Server per riportare l'utilizzo accurato di CPU/Memoria o metriche personalizzate.

Passaggi di Diagnosi:

  1. Verifica la Salute del Metrics Server: Assicurati che il Metrics Server sia in esecuzione e accessibile.
    kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes"
    
  2. Controlla lo Stato dell'HPA: Ispeziona la configurazione dell'HPA e gli eventi recenti.
    kubectl describe hpa <hpa-name> -n <namespace>
    
    Cerca messaggi che indicano se l'origine della metrica non è disponibile o se il ciclo decisionale di scaling funziona.

Colli di Bottiglia: Se vengono utilizzate metriche personalizzate, assicurati che il provider di metriche esterne funzioni correttamente e riporti i dati abbastanza spesso affinché l'HPA possa prendere decisioni utili.

HPA è reattivo. Non sa che sta arrivando un picco di traffico a meno che la tua metrica non lo rifletta. Per carichi di lavoro con picchi improvvisi, potresti aver bisogno di repliche minime più alte, metriche personalizzate più veloci, scaling basato su coda o pre-scaling prima di eventi noti.

2. Interazioni del Vertical Pod Autoscaler (VPA)

Mentre VPA regola automaticamente le richieste di risorse, può causare instabilità delle performance durante la sua fase di regolazione se riavvia o ridimensiona frequentemente i pod, specialmente per applicazioni stateful che non tollerano i riavvii.

Raccomandazione: Usa VPA in modalità Recommender prima, o usa updateMode: "Off" per osservare solo le raccomandazioni senza applicazione automatica, mitigando interruzioni di ridimensionamento non necessarie.

Fase 5: Performance di Rete e Storage

Quando le risorse di calcolo sembrano a posto, la rete o lo storage persistente potrebbero essere il punto di strozzatura.

1. Problemi CNI (Container Network Interface)

Se la comunicazione tra pod (specialmente attraverso i nodi) è lenta o fallisce in modo intermittente, il plugin CNI potrebbe essere sovraccarico o configurato male.

Risoluzione dei Problemi:

  • Controlla i log dei pod del daemonset CNI (es. Calico, Flannel).
  • Testa la connettività di base usando ping o curl tra pod su nodi diversi.

2. Latenza del Volume Persistente (PV)

Le applicazioni che dipendono fortemente da I/O su disco (database, sistemi di logging) soffriranno se la latenza del Volume Persistente sottostante è alta.

Controllo Attuabile: Conferma il tipo di provisioner (es. AWS EBS gp3 vs. io1) e verifica che il volume soddisfi le specifiche IOPS/throughput richieste.

Avvertenza sullo Storage: Non eseguire mai database ad alto throughput direttamente su volumi hostPath standard senza comprendere le caratteristiche di performance del disco sottostante. Utilizza soluzioni di storage cloud gestite o provisioner di storage locale ad alte prestazioni per carichi di lavoro impegnativi.

Colli di Bottiglia a Livello di Nodo

A volte tutti i pod su un nodo diventano più lenti contemporaneamente. Questo è il tuo segnale per smettere di fissare un Deployment e ispezionare il nodo.

kubectl describe node <node-name>
kubectl top node <node-name>
kubectl get pods --all-namespaces -o wide | grep <node-name>

Cerca condizioni MemoryPressure, DiskPressure e PIDPressure. La pressione del disco è facile da trascurare perché il sintomo dell'applicazione potrebbe essere un avvio lento, fallimenti nel pull delle immagini o sfratti piuttosto che un ovvio errore del disco.

Sul nodo stesso, se hai accesso, controlla:

df -h
iostat -x 1
free -h
journalctl -u kubelet --since "30 minutes ago"

I servizi Kubernetes gestiti possono limitare l'accesso diretto al nodo, ma la stessa idea vale comunque: usa le metriche del provider, gli eventi di kubelet e le condizioni del nodo per decidere se il nodo è il collo di bottiglia condiviso.

Pressione del Control Plane e dell'API

La maggior parte della latenza dell'applicazione non è causata dal server API di Kubernetes. La tua richiesta web di solito non chiama il server API ad ogni richiesta utente. Ma la pressione del control plane può danneggiare le performance operative: rollout lenti, scheduling ritardato, aggiornamenti degli endpoint lenti o controller che restano indietro.

I sintomi includono:

  • I comandi kubectl sono lenti in tutto il cluster.
  • I Deployment impiegano più tempo del solito per creare pod.
  • I controller sono in ritardo rispetto allo stato desiderato.
  • Gli eventi mostrano timeout API ripetuti.

Controlla se il problema riguarda il traffico normale dell'applicazione o le operazioni del cluster. Se solo i rollout e lo scheduling sono lenti, guarda la salute del server API, il comportamento del controller manager, i webhook di ammissione e la salute di etcd nei cluster in cui gestisci il control plane.

I webhook di ammissione meritano un'attenzione speciale. Un webhook lento o non disponibile può ritardare la creazione dei pod anche quando i nodi hanno molta capacità. Se un rollout si blocca al momento della creazione e gli eventi menzionano chiamate al webhook, indaga sul servizio webhook prima di ridimensionare i nodi.

Un Ordine Pratico per la Risoluzione dei Problemi

Inizia con il sintomo visibile all'utente:

  • Richieste HTTP lente: confronta la latenza dell'app, il throttling della CPU, i riavvii della memoria, la latenza a valle e il percorso di rete.
  • Avvio lento del pod: controlla il tempo di pull dell'immagine, gli eventi di scheduling, il tempo di attach del volume e i container init.
  • Pod in attesa: controlla le richieste, la capacità del nodo, i taint, l'affinità, le quote e i limiti dell'autoscaler.
  • Picchi periodici di latenza: controlla il throttling della CPU, la garbage collection, i vicini rumorosi, la latenza dello storage e la tempistica dello scaling HPA.
  • Riavvii casuali: controlla OOMKilled, i probe di liveness, la pressione del nodo e i log dell'applicazione dal container precedente.

Poi prova o elimina un livello alla volta. Ad esempio, se i picchi di latenza coincidono esattamente con il throttling della CPU, hai un forte indizio. Se i picchi di latenza si verificano mentre CPU, memoria, rete e storage sembrano tutti calmi, il collo di bottiglia potrebbe essere all'interno dell'applicazione o di un servizio a valle al di fuori di Kubernetes.

Ottimizzazione di Richieste e Limiti Senza Supposizioni

Impostazioni errate delle risorse creano molti problemi di performance:

  • Richieste troppo basse: lo scheduler impacchetta troppi pod occupati sullo stesso nodo.
  • Richieste troppo alte: i pod rimangono in attesa anche se l'utilizzo effettivo è modesto.
  • Limiti CPU troppo bassi: le app sensibili alla latenza vengono limitate.
  • Limiti memoria troppo bassi: i container vengono uccisi invece di rallentare.
  • Nessuna richiesta: lo scheduling diventa meno prevedibile e i carichi di lavoro critici possono competere male con i vicini rumorosi.

Usa le metriche di produzione recenti come punto di partenza, poi lascia un margine per i picchi normali. Per carichi di lavoro Java, Node.js, Go, Python e simili a database, il comportamento della memoria può essere molto diverso, quindi evita di copiare i limiti da un servizio all'altro solo perché la dimensione dell'immagine del container sembra simile.

Prossimi Passi

Le migliori indagini sulle performance di Kubernetes sono noiose in senso positivo: definisci il sintomo, controlla il pod, controlla il nodo, controlla lo scaling, poi controlla la rete e lo storage. kubectl describe e kubectl top sono solo l'inizio, ma di solito ti dicono quale direzione vale la pena seguire.

  1. Implementa Resource Quotas robuste per impedire ai vicini rumorosi di affamare le applicazioni critiche.
  2. Rivedi regolarmente i conteggi di riavvio dei pod per individuare precocemente OOM sottili o comportamenti di applicazione falliti.
  3. Utilizza dashboard Prometheus/Grafana specificamente per tracciare le metriche di throttling della CPU, non solo l'utilizzo grezzo.