Monitoraggio delle Performance di Kubernetes: Strumenti e Tecniche per l'Ottimizzazione
Monitora le performance di Kubernetes con metriche utili, Prometheus, Grafana, kubectl e abitudini pratiche di ottimizzazione delle risorse.
Monitoraggio delle Performance di Kubernetes: Strumenti e Tecniche per l'Ottimizzazione
Il monitoraggio delle performance di Kubernetes non significa solo guardare i grafici della CPU. Un cluster può mostrare un basso utilizzo medio della CPU mentre gli utenti vedono richieste lente. Un pod può avere memoria sufficiente per la maggior parte della giornata e venire comunque terminato durante un job batch. Un nodo può sembrare sano finché la pressione del disco non inizia a sfrattare i pod. Un buon monitoraggio collega i segnali del cluster all'esperienza che le persone realmente apprezzano: il servizio è veloce, disponibile e prevedibile?
Il primo errore è iniziare con gli strumenti invece che con le domande. Prometheus, Grafana, metrics-server, kube-state-metrics e le piattaforme di monitoraggio cloud sono tutti utili, ma non decidono cosa conta. Lo decidi tu comprendendo il carico di lavoro. Un'API pubblica si preoccupa di latenza ed errori. Un worker di code si preoccupa di arretrato e tasso di elaborazione. Un job notturno si preoccupa del tempo di completamento e dei pod falliti. Un carico di lavoro simile a un database si preoccupa della latenza del disco e della pressione della memoria.
Per una rapida occhiata, kubectl top è ancora utile:
kubectl top nodes
kubectl top pods -A
kubectl top pod -n production api-7d9c8f7b9d-2x4mq --containers
Questi comandi dipendono da metrics-server. Forniscono l'uso recente di CPU e memoria, non una cronologia completa. Usali durante il triage, non come unico sistema di monitoraggio. Se un pod è stato riavviato dieci minuti fa perché ha esaurito la memoria, kubectl top potrebbe non mostrare il picco che lo ha causato.
Prometheus è la base comune per le metriche di Kubernetes perché raccoglie dati di serie temporali e funziona bene con il service discovery di Kubernetes. In una configurazione tipica, le metriche provengono da diverse fonti. Il kubelet espone le metriche delle risorse di pod e container. cAdvisor, integrato con kubelet, contribuisce con dati su CPU, memoria, filesystem e rete dei container. node-exporter riporta le metriche a livello di host. kube-state-metrics trasforma lo stato degli oggetti Kubernetes in metriche: repliche desiderate, repliche disponibili, fasi dei pod, condizioni dei nodi e altro ancora.
Grafana trasforma poi queste metriche in dashboard. Una buona dashboard non è un muro di indicatori. Dovrebbe rispondere rapidamente a domande specifiche: quale servizio è lento, quali pod sono limitati, quali nodi sono sotto pressione, quale Deployment non sta completando il rollout e se l'autoscaling sta tenendo il passo.
Inizia dal livello applicativo. Per i servizi rivolti agli utenti, i segnali più importanti sono il tasso di richieste, il tasso di errore e la latenza. Se hai SLO, rappresentali graficamente. Un grafico della CPU di un pod non ti dice se il checkout sta fallendo. Lo fanno le metriche applicative. Strumenta i servizi con librerie client di Prometheus, OpenTelemetry o il sistema di monitoraggio che la tua piattaforma già utilizza. Le metriche di Kubernetes spiegano perché il servizio è malato; le metriche applicative ti dicono che è malato.
Poi collega i sintomi applicativi alle risorse dei pod. L'uso della CPU è facile da interpretare male in Kubernetes. Un container con un limite di CPU può essere limitato anche quando la CPU media non sembra drammatica. La limitazione avviene quando il container tenta di utilizzare più tempo CPU di quanto il suo limite consenta nel periodo di scheduling. Per le app sensibili alla latenza, questo può causare richieste lente che appaiono casuali.
Una query PromQL utile per la limitazione è:
rate(container_cpu_cfs_throttled_periods_total{namespace="production", container!=""}[5m])
Un valore in aumento significa che il container viene limitato. Abbinalo all'uso della CPU e alla latenza delle richieste. Se i picchi di latenza coincidono con la limitazione, considera di aumentare o rimuovere il limite di CPU, aumentare le repliche o ottimizzare il percorso del codice. Alcuni team impostano richieste di CPU ma evitano i limiti di CPU per i servizi sensibili alla latenza, affidandosi invece a richieste, autoscaling e controlli di capacità del nodo. Questo può essere ragionevole, ma richiede disciplina a livello di cluster in modo che i carichi di lavoro rumorosi non affamino gli altri.
La memoria si comporta diversamente. La CPU può essere limitata; la memoria non può essere rallentata allo stesso modo. Se un container supera il suo limite di memoria, può essere OOMKilled. Cerca i motivi del riavvio:
kubectl describe pod -n production api-7d9c8f7b9d-2x4mq
kubectl get pod -n production api-7d9c8f7b9d-2x4mq -o jsonpath='{.status.containerStatuses[*].lastState}'
In Prometheus, monitora la memoria del working set e confrontala con i limiti:
container_memory_working_set_bytes{namespace="production", container!=""}
Non ottimizzare la memoria basandoti su un'unica ora tranquilla. Considera il traffico di picco, le finestre batch, i deployment e il comportamento del garbage collector. I servizi Java, Go, Node.js e Python hanno profili di memoria diversi. Un limite che sembra generoso durante il traffico normale potrebbe essere troppo stretto durante l'avvio, il riscaldamento della cache o una richiesta di grandi dimensioni.
Le richieste di risorse sono importanti perché lo scheduler le usa per posizionare i pod. Se le richieste sono troppo basse, Kubernetes potrebbe impacchettare troppi pod occupati sullo stesso nodo. Tutto sembra efficiente finché quei pod non diventano occupati contemporaneamente. Se le richieste sono troppo alte, il cluster spreca capacità e l'autoscaling potrebbe aggiungere nodi prima del necessario. La richiesta migliore è solitamente basata sull'uso osservato più un margine, non su un valore copiato da un altro servizio.
Il Vertical Pod Autoscaler può aiutare raccomandando richieste basate sull'uso storico. Molti team eseguono VPA prima in modalità di raccomandazione perché gli aggiornamenti automatici possono riavviare i pod a seconda della configurazione e del tipo di carico di lavoro. Tratta le raccomandazioni come input, non come legge. Un servizio con picchi rari ma importanti potrebbe aver bisogno di più margine di quanto suggerisca la sua media storica.
L'Horizontal Pod Autoscaler è utile quando più repliche migliorano effettivamente il throughput. Funziona bene per servizi web stateless e worker che possono condividere il carico. Non risolve un collo di bottiglia single-threaded, un blocco del database o una dipendenza a valle già saturata.
Un HPA di base potrebbe assomigliare a questo:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Monitora il comportamento dell'HPA, non solo il numero di repliche. Se scala continuamente su e giù, regola le finestre di stabilizzazione, i target o la metrica. Se raggiunge maxReplicas e la latenza è ancora pessima, il problema potrebbe essere la capacità, il codice o una dipendenza. Se non scala mai mentre i pod sono chiaramente sovraccarichi, verifica la disponibilità delle metriche e se le richieste sono impostate. I target di utilizzo della CPU dipendono dalle richieste di CPU; richieste mancanti o irrealistiche possono rendere fuorviante l'autoscaling.
La salute dei nodi è il livello successivo. Un problema di pod che appare su molti servizi su un nodo è solitamente un problema del nodo. Monitora la saturazione della CPU, il carico medio, la memoria disponibile, la pressione del disco, l'uso degli inode, la latenza del filesystem, gli errori di rete e la salute del kubelet. Condizioni del nodo come MemoryPressure, DiskPressure e PIDPressure dovrebbero essere visibili in dashboard e alert.
Usa kubectl describe node quando un nodo sembra sospetto:
kubectl describe node worker-12
Osserva le condizioni, le risorse allocate, gli eventi e i pod schedulati sul nodo. Un nodo può essere sovraccaricato da limiti, richieste o uso effettivo. La sezione delle risorse allocate ti aiuta a vedere se le ipotesi di scheduling corrispondono alla realtà.
Il monitoraggio del control plane è importante anche se i tuoi pod applicativi sembrano a posto. La latenza del server API può rallentare deployment, autoscaling e controller. La latenza di etcd o i problemi del disco possono far sembrare l'intero cluster lento. Problemi del controller manager e dello scheduler possono ritardare il posizionamento dei pod o la riconciliazione. In Kubernetes gestito, potresti non vedere ogni componente del control plane, ma i provider cloud solitamente espongono alcune metriche di salute e latenza dell'API.
Gli eventi sono utili durante gli incidenti, ma non sono un archivio di metriche a lungo termine. Tuttavia, spesso spiegano cosa è appena successo:
kubectl get events -A --sort-by=.lastTimestamp
Cerca scheduling falliti, errori di pull delle immagini, fallimenti dei probe, sfratti e messaggi di back-off. Gli eventi possono essere rumorosi, quindi filtra per namespace o oggetto coinvolto quando necessario.
I probe meritano un monitoraggio attento. I liveness probe troppo aggressivi possono riavviare un'app lenta ma in ripresa e peggiorare un incidente. I readiness probe che falliscono correttamente possono proteggere gli utenti rimuovendo i pod difettosi dal servizio. Tieni traccia dei fallimenti dei probe e correlali con la limitazione della CPU, le pause del GC, i timeout a valle e i deploy.
Per carichi di lavoro con uso intensivo di storage, CPU e memoria del container non sono sufficienti. Monitora la latenza dei volumi persistenti, il throughput del disco, la profondità della coda e la pienezza del filesystem. Un pod in attesa di storage lento potrebbe mostrare una CPU bassa perché è bloccato. Se un database o una coda viene eseguito su Kubernetes, le metriche di storage fanno parte delle performance applicative, non di dettagli infrastrutturali.
Un percorso pratico di risoluzione dei problemi inizia ampio e si restringe. Primo, conferma il sintomo visibile all'utente: latenza, errori, job falliti o arretrato. Secondo, identifica l'ambito: un pod, un Deployment, un nodo, un namespace o l'intero cluster. Terzo, verifica le modifiche recenti: deployment, aggiornamenti di configurazione, attività dell'autoscaler, rotazioni dei nodi o picchi di traffico. Quarto, ispeziona il comportamento delle risorse del pod: limitazione della CPU, pressione della memoria, riavvii e fallimenti dei probe. Quinto, ispeziona la salute del nodo e delle dipendenze.
L'alerting dovrebbe evitare di svegliare le persone per rumore innocuo. Avvisa prima sull'impatto per l'utente: alto tasso di errore, alta latenza, scadenza di job mancata, età della coda in crescita. Poi avvisa su forti indicatori anticipatori: OOMKill frequenti, limitazione sostenuta della CPU su servizi sensibili alla latenza, pod non disponibili al di sotto delle repliche desiderate, pressione del nodo, pod persistentemente in attesa e HPA bloccato al massimo delle repliche mentre le metriche del servizio sono pessime.
L'obiettivo non è l'utilizzo perfetto. Un cluster che funziona al 95% di utilizzo delle risorse tutto il giorno può sembrare efficiente finché un nodo non fallisce e non c'è spazio per rischedulare i pod. Lascia capacità per rollout, tentativi, picchi di traffico e fallimenti. L'ottimizzazione dovrebbe ridurre gli sprechi senza rimuovere il buffer che mantiene piccoli gli incidenti.
Un buon monitoraggio delle performance di Kubernetes sembra pratico. Puoi aprire una dashboard e vedere la salute del servizio, la salute dei pod, la salute dei nodi e il comportamento di scaling senza cercare in venti schede. Puoi rispondere se un rallentamento è dovuto a codice, limiti di risorse, pressione del nodo, storage, rete o control plane. E quando modifichi richieste, limiti o autoscaling, puoi vedere se la modifica ha aiutato invece di indovinare.
Le viste a livello di namespace sono utili quando molti team condividono un cluster. Un singolo team potrebbe non vedere la saturazione a livello di nodo in arrivo se guarda solo i propri Deployment. I team della piattaforma dovrebbero esporre dashboard che mostrino le richieste di CPU e memoria del namespace, l'uso effettivo, il conteggio dei pod, i riavvii e la limitazione. Questo rende le conversazioni sulla capacità meno emotive. Invece di dire che un team sta usando "troppo", puoi mostrare tendenze delle richieste, uso di picco e sprechi.
L'ottimizzazione dei costi dovrebbe venire dopo i segnali di affidabilità, non prima. Se un servizio non ha mai avuto richieste ottimizzate, potresti trovare risparmi facili. Ma tagliare le richieste in modo aggressivo può creare pressione di scheduling e problemi di vicini rumorosi. Un buon processo modifica una classe di carico di lavoro alla volta, osserva latenza e riavvii e lascia note di rollback. Tratta l'ottimizzazione delle risorse come codice di produzione: piccole modifiche, risultati misurati.
I deployment stessi possono creare incidenti di performance. Un rollout che sostituisce troppi pod contemporaneamente può sovraccaricare cache fredde, pool di connessioni o servizi a valle. Monitora la durata del rollout, le repliche non disponibili e la latenza applicativa durante i deploy. Ottimizza maxSurge e maxUnavailable in base a come si comporta il servizio durante l'avvio. Un servizio con riscaldamento lento potrebbe aver bisogno di un rollout conservativo anche se le performance a regime sono buone.
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
Quell'impostazione non è universalmente migliore, ma mostra il compromesso: rollout più lento, più protezione contro cali di capacità. Per un servizio stateless che si avvia istantaneamente, potresti scegliere un rollout più veloce. Per un servizio JVM che riscalda cache e apre molte connessioni a valle, più lento potrebbe essere più sicuro.
Tieni d'occhio la cardinalità nelle metriche. Le etichette di Kubernetes sono tentatrici, ma etichette ad alta cardinalità come UID del pod, ID della richiesta o ID utente possono rendere Prometheus costoso e lento. Usa etichette che ti aiutano ad aggregare: namespace, carico di lavoro, pod, container, nodo, codice di stato, pattern di route. Evita etichette che creano una nuova serie temporale per ogni utente o ogni richiesta. Il monitoraggio non dovrebbe diventare la cosa che danneggia le performance del cluster.
Log e trace completano il quadro. Le metriche ti dicono che la latenza è aumentata; i trace possono mostrare quale chiamata a valle è diventata lenta; i log possono mostrare l'errore esatto o il timeout. OpenTelemetry è comunemente usato per collegare questi segnali, ma lo strumento conta meno della correlazione. Usa nomi di servizio, namespace, versioni e ID di trace coerenti in modo da poter passare da un alert ai log pertinenti senza indovinare.
Per sistemi batch e worker, monitora l'età dell'arretrato piuttosto che solo la CPU del pod. Un worker di coda può essere sano a livello di pod mentre resta indietro perché il lavoro in arrivo supera la capacità di elaborazione. Metriche come l'età del messaggio più vecchio, i job completati al minuto, i tentativi e i conteggi di dead-letter spesso contano più dell'utilizzo del container. L'HPA può scalare da metriche personalizzate o esterne quando la CPU è il segnale sbagliato.
Rivedi le dashboard dopo gli incidenti. Se i responder hanno dovuto eseguire cinque comandi manuali per rispondere alla stessa domanda, quella domanda appartiene a una dashboard o a un runbook. Il monitoraggio migliora con l'uso. L'obiettivo non è prevedere ogni fallimento; è rendere la prossima indagine più breve e meno dipendente dalla memoria di una singola persona.