Skip to content

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 da kube-state-metrics, kubelet/cadvisor, openshift-state-metrics. Le query sono lanciabili dalla Console OpenShift (Observe → Metrics), o via oc exec nel 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):

Terminal window
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 -20

1.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
) > 0

Cosa mostra: elenco puntuale (snapshot ora) di container in CLBO, con node associato.

Equivalente oc:

Terminal window
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
) > 0

Utile 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
) > 0

Cattura 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
) > 0

Valori tipici di reason:

ReasonSignificatoPrima azione
OOMKilledContainer ha superato il memory limitVedi OOMKilled troubleshooting
ErrorExit code != 0oc logs --previous per stacktrace applicativo
CompletedExit code 0 (normale per Job/CronJob)Nessuna azione
ContainerCannotRunRuntime CRI-O ha rifiutato di partireSCC / SELinux / immagine con user root
DeadlineExceededJob ha superato activeDeadlineSecondsRivedere 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
) > 0

3.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
) > 0
and 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 / args del 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) / 3600

Restituisce 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:

Terminal window
# Ultimi eventi Kubernetes ordinati per data (i più freschi in fondo)
oc get events -A --sort-by=.lastTimestamp | tail -30
# Solo eventi di tipo Warning
oc get events -A --field-selector type=Warning --sort-by=.lastTimestamp | tail -30
# Log del container attuale
oc logs -n <ns> <pod> -c <container>
# Log del container PRECEDENTE (crashato) — indispensabile per debug CLBO
oc logs -n <ns> <pod> -c <container> --previous
# Describe: vedi Events, State, Last State, Restart Count
oc describe pod -n <ns> <pod>
# Restart-count di tutti i container di un pod
oc 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

  • rate vs increase: rate(...[Xm]) è ottimo per grafici (mostra la derivata); increase(...[Xm]) è più leggibile come singolo numero (“N restart nell’ultima X”).
  • kube_pod_info per il node: è la metrica-ponte che porta il label node. 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_* valgono 1 quando la condizione è vera e 0 altrimenti. Usare > 0 filtra 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