Restart & Pod Lifecycle — Troubleshooting
Restart & Pod Lifecycle — Troubleshooting
Query PromQL operative per identificare pod che restartano, container in CrashLoopBackOff e motivi di terminazione, con l’output arricchito da namespace, pod, container e node. Utile in emergency call quando i pod applicativi rimbalzano e serve capire subito dove e perché.
Stack di riferimento: OpenShift Monitoring standard (
openshift-monitoring). Metriche generate dakube-state-metrics,kubelet/cadvisor,openshift-state-metrics. Le query sono lanciabili dalla Console OpenShift (Observe → Metrics), o viaoc execnel pod Prometheus.
1. Top 20 pod che restartano
1.1 Top 20 restart nell’ultima ora
topk(20, sum by (namespace, pod, container, node) ( rate(kube_pod_container_status_restarts_total[1h]) * on(pod, namespace) group_left(node) kube_pod_info ))Cosa mostra: rate di restart (restart/sec) per container, con namespace/pod/container/node. Come leggere: valori > 0.001 significano “restart in corso”; > 0.01 è già un CrashLoop conclamato.
Equivalente oc (rapido, meno preciso):
oc get pods -A --sort-by='.status.containerStatuses[0].restartCount' \ -o custom-columns=NS:.metadata.namespace,POD:.metadata.name,\NODE:.spec.nodeName,\RESTARTS:.status.containerStatuses[0].restartCount,\CONTAINER:.status.containerStatuses[0].name \ | tail -201.2 Top 20 restart nelle ultime 6 ore
topk(20, sum by (namespace, pod, container, node) ( increase(kube_pod_container_status_restarts_total[6h]) * on(pod, namespace) group_left(node) kube_pod_info ))increase(...[6h]) restituisce il conteggio assoluto di restart nelle 6 ore. Più leggibile del rate per una window medio-lunga.
1.3 Top 20 restart nelle ultime 24 ore
topk(20, sum by (namespace, pod, container, node) ( increase(kube_pod_container_status_restarts_total[24h]) * on(pod, namespace) group_left(node) kube_pod_info ))1.4 Restart escludendo namespace di sistema
Filtra kube-* e openshift-* per concentrarti sui workload applicativi:
topk(20, sum by (namespace, pod, container, node) ( increase(kube_pod_container_status_restarts_total{ namespace!~"kube-.*|openshift-.*|default" }[24h]) * on(pod, namespace) group_left(node) kube_pod_info ))2. Container che stanno fallendo ORA
2.1 Pod in CrashLoopBackOff attualmente
sum by (namespace, pod, container, node, reason) ( kube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"} * on(pod, namespace) group_left(node) kube_pod_info) > 0Cosa mostra: elenco puntuale (snapshot ora) di container in CLBO, con node associato.
Equivalente oc:
oc get pods -A --field-selector=status.phase!=Running,status.phase!=Succeeded \ -o custom-columns=NS:.metadata.namespace,POD:.metadata.name,\NODE:.spec.nodeName,\STATUS:.status.containerStatuses[0].state.waiting.reason,\MESSAGE:.status.containerStatuses[0].state.waiting.message \ 2>/dev/null | grep -i "CrashLoopBackOff\|Error"2.2 Pod in ImagePullBackOff / ErrImagePull
sum by (namespace, pod, container, node, reason) ( kube_pod_container_status_waiting_reason{reason=~"ImagePullBackOff|ErrImagePull"} * on(pod, namespace) group_left(node) kube_pod_info) > 0Utile per capire subito se il problema è disponibilità immagine (registry down, pull-secret mancante, tag sbagliato) e non applicativo.
2.3 Container waiting per qualunque motivo
sum by (namespace, pod, container, node, reason) ( kube_pod_container_status_waiting_reason * on(pod, namespace) group_left(node) kube_pod_info) > 0Cattura anche ContainerCreating, PodInitializing, CreateContainerConfigError, etc.
3. Motivi di terminazione (last exit reason)
Quando un container termina, K8s registra reason e exit_code nell’ultimo state. Utile a distinguere OOM da errori applicativi.
3.1 Terminated reason nell’ultima ora
sum by (namespace, pod, container, node, reason) ( kube_pod_container_status_last_terminated_reason * on(pod, namespace) group_left(node) kube_pod_info) > 0Valori tipici di reason:
| Reason | Significato | Prima azione |
|---|---|---|
OOMKilled | Container ha superato il memory limit | Vedi OOMKilled troubleshooting |
Error | Exit code != 0 | oc logs --previous per stacktrace applicativo |
Completed | Exit code 0 (normale per Job/CronJob) | Nessuna azione |
ContainerCannotRun | Runtime CRI-O ha rifiutato di partire | SCC / SELinux / immagine con user root |
DeadlineExceeded | Job ha superato activeDeadlineSeconds | Rivedere timeout Job |
3.2 Solo container terminati con Error
sum by (namespace, pod, container, node) ( kube_pod_container_status_last_terminated_reason{reason="Error"} * on(pod, namespace) group_left(node) kube_pod_info) > 03.3 Correlazione restart ↔ terminated reason
Una view combinata: chi ha restartato E con che motivo l’ultima volta:
( topk(20, sum by (namespace, pod, container) ( increase(kube_pod_container_status_restarts_total[6h]) ))) and on(namespace, pod, container) ( kube_pod_container_status_last_terminated_reason{reason!=""} == 1)Non è bellissima da leggere, ma è utile per un audit “chi ha girato in loop nelle 6h passate e con che ultimo motivo”.
4. Pod appena creati che già falliscono
Distinguere “pod che rimbalza da tempo” da “pod nuovo che parte già rotto”:
4.1 Container con restart nella prima ora di vita del pod
sum by (namespace, pod, container, node) ( increase(kube_pod_container_status_restarts_total[1h]) * on(pod, namespace) group_left(node) kube_pod_info) > 0and on(namespace, pod) ( time() - kube_pod_created < 3600)Cosa dice: pod creati da meno di 1 ora e con restart già osservati. Sintomo classico di:
- Errore nella
command/argsdel deployment - ConfigMap / Secret mancanti (dependency non ancora creata)
- Init-container fallisce
- Startup / readiness probe troppo aggressive
4.2 Età del pod + restart count (utile in Grafana)
(time() - kube_pod_created) / 3600Restituisce l’età del pod in ore. Combinato con kube_pod_container_status_restarts_total in dashboard mostra chi rimbalza da giorni vs. chi è appena partito storto.
5. Restart per namespace / workload
5.1 Namespace con più restart totali (ultime 24h)
topk(20, sum by (namespace) ( increase(kube_pod_container_status_restarts_total[24h]) ))5.2 Deployment con più restart (ultime 24h)
topk(20, sum by (namespace, deployment) ( increase(kube_pod_container_status_restarts_total[24h]) * on(pod, namespace) group_left(deployment) label_replace( kube_pod_owner{owner_kind="ReplicaSet"}, "deployment", "$1", "owner_name", "^(.+)-[a-z0-9]+$" ) ))label_replace estrae il nome del Deployment dal ReplicaSet (che si chiama <deployment>-<hash>).
5.3 StatefulSet con più restart
topk(20, sum by (namespace, owner_name) ( increase(kube_pod_container_status_restarts_total[24h]) * on(pod, namespace) group_left(owner_name, owner_kind) kube_pod_owner{owner_kind="StatefulSet"} ))6. Comandi oc complementari
Le query sopra dicono quali pod restartano; per capire perché, questi comandi oc sono complementari:
# Ultimi eventi Kubernetes ordinati per data (i più freschi in fondo)oc get events -A --sort-by=.lastTimestamp | tail -30
# Solo eventi di tipo Warningoc get events -A --field-selector type=Warning --sort-by=.lastTimestamp | tail -30
# Log del container attualeoc logs -n <ns> <pod> -c <container>
# Log del container PRECEDENTE (crashato) — indispensabile per debug CLBOoc logs -n <ns> <pod> -c <container> --previous
# Describe: vedi Events, State, Last State, Restart Countoc describe pod -n <ns> <pod>
# Restart-count di tutti i container di un podoc get pod -n <ns> <pod> -o json | jq '.status.containerStatuses[] | {name, restartCount, lastState}'
# Node su cui gira un pod (utile per debug node-side)oc get pod -n <ns> <pod> -o jsonpath='{.spec.nodeName}{"\n"}'7. Note operative
ratevsincrease:rate(...[Xm])è ottimo per grafici (mostra la derivata);increase(...[Xm])è più leggibile come singolo numero (“N restart nell’ultima X”).kube_pod_infoper il node: è la metrica-ponte che porta il labelnode. Il join* on(pod, namespace) group_left(node)è il pattern standard per arricchire QUALUNQUE metrica pod con il node.- Metriche a “1”: molte
kube_pod_container_status_*valgono1quando la condizione è vera e0altrimenti. Usare> 0filtra i falsi negativi. - Pod appena eliminati: le metriche
kube_pod_*restano per ~5 minuti dopo l’eliminazione del pod (staleness), poi spariscono. Se cerchi un pod di 30 minuti fa che non c’è più, usa[5m]come range indietro. - Range di tempo: adatta
[1h],[6h],[24h]al tuo caso. Range più lunghi = query più costose per Prometheus (occhio in cluster grandi).
Vedi anche
- OOM Killed — Troubleshooting — quando
reason=OOMKilledè la vera storia - RCA guidata — Pod in CrashLoop — flusso step-by-step
- Consumi risorse per namespace — CPU/memoria a livello namespace