Skip to content

Alerting operativo — Query interattive

Alerting operativo — Query interattive

Query PromQL da usare interattivamente nella Console OpenShift (Observe → Metrics) o via oc exec sul pod Prometheus. Sono query “moderne” (allineate a cluster OCP 4.14+ con OVN-Kubernetes) per capire lo stato reale del cluster prima che qualcuno apra un ticket.

Non sono PrometheusRule (alert autoconsistenti); sono query da tenere in una collezione personale — copia-incolla, aggiusta la finestra, valuta.


1. CPU throttling — il killer silenzioso

Il CPU throttling è la vera causa dietro molti “l’app è lenta” quando la CPU sembra “libera” sul monitoring. Un container con limits.cpu = 500m che serve un carico spike a 700m viene throttled dal cgroup e la latenza salta alle stelle, ma le metriche di usage dicono “50%“.

1.1 Top 20 container più throttled nell’ultima ora

topk(20,
sum by (namespace, pod, container, node) (
rate(container_cpu_cfs_throttled_periods_total{container!="", container!="POD"}[1h])
/
rate(container_cpu_cfs_periods_total{container!="", container!="POD"}[1h])
)
* on(pod, namespace) group_left(node) kube_pod_info
) > 0.05

Cosa mostra: percentuale di periodi CPU throttled. Se > 0.05 (5%) sei già in zona “il container sta soffrendo”. > 0.20 = grave.

Fix tipico: alzare limits.cpu. Regola pratica: limits.cpu = requests.cpu * 2 (QoS Burstable), oppure limits.cpu = requests.cpu (QoS Guaranteed, più costoso ma no throttling).

1.2 Namespace con più CPU throttling totale

topk(10,
sum by (namespace) (
rate(container_cpu_cfs_throttled_seconds_total{container!="", container!="POD"}[1h])
)
)

Restituisce secondi/s di throttling per namespace. Utile per capire “quali applicazioni stanno effettivamente strette”.


2. Pending / Unschedulable pods

Pod che non partono mai perché lo scheduler non trova un node valido. Cause tipiche: overcommit, taint mancanti, nodeSelector troppo stretti.

2.1 Pod attualmente Pending

sum by (namespace, pod, reason) (
kube_pod_status_unschedulable
) > 0

Oppure via oc:

Terminal window
oc get pods -A --field-selector=status.phase=Pending \
-o custom-columns=NS:.metadata.namespace,POD:.metadata.name,\
NODE:.spec.nodeName,\
REASON:.status.conditions[0].message

2.2 Pod Pending per motivo

sum by (namespace, reason) (
kube_pod_status_scheduled{condition="false"}
)

reason tipici:

  • Unschedulable → nessun node valido (vedi §2.3)
  • SchedulerError → scheduler in errore
  • Vuoto → pod appena creato, dagli tempo

2.3 Perché lo scheduler non trova posto

Manca in PromQL, va con oc:

Terminal window
oc describe pod -n <ns> <pod> | grep -A5 "Events:"
# Cerchi messaggi tipo:
# - "0/6 nodes are available: 3 Insufficient cpu, 3 Insufficient memory"
# - "node(s) had untolerated taint {node-role.kubernetes.io/master: true}"
# - "node(s) didn't match node selector"

3. Memory pressure a livello node

3.1 Node in MemoryPressure attualmente

kube_node_status_condition{condition="MemoryPressure", status="true"}

3.2 Memory available per node

node_memory_MemAvailable_bytes / 1024 / 1024 / 1024

Restituisce GiB disponibili per node. Se < 2 su node medi (32-64 GB) sei vicino al margine kubelet reserved (500 Mi hard-reserved by default).

3.3 Node con memory usage > 85%

(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) > 0.85

3.4 Overcommit ratio (requests vs allocatable)

sum by (node) (
kube_pod_container_resource_requests{resource="memory"}
* on(pod, namespace) group_left(node) kube_pod_info
)
/
sum by (node) (
kube_node_status_allocatable{resource="memory"}
)

> 0.95 = zona critica. > 1.0 = impossibile (scheduler dovrebbe averlo bloccato, se lo vedi c’è un bug o pod DaemonSet che non rispetta requests).


4. Disk pressure & filesystem

4.1 Node con disk usage > 80%

100 * (
1 - (
node_filesystem_avail_bytes{fstype!~"tmpfs|devtmpfs|overlay",mountpoint="/"}
/
node_filesystem_size_bytes{fstype!~"tmpfs|devtmpfs|overlay",mountpoint="/"}
)
) > 80

4.2 Node con inode pressure

100 * (
1 - (
node_filesystem_files_free{fstype!~"tmpfs|devtmpfs|overlay",mountpoint="/"}
/
node_filesystem_files{fstype!~"tmpfs|devtmpfs|overlay",mountpoint="/"}
)
) > 80

Overlooked — cluster con molti container che scrivono lock/pid file possono esaurire gli inode prima del disco.

4.3 Node con /var/lib/containers pieno (image cache)

100 * (
1 - (
node_filesystem_avail_bytes{mountpoint="/var/lib/containers"}
/
node_filesystem_size_bytes{mountpoint="/var/lib/containers"}
)
) > 85

Se questa cresce, i pull di nuove immagini falliranno.


5. API server health

L’API server (kube-apiserver) è il collo di bottiglia più importante del control plane.

5.1 API server latency p99 nelle ultime 5 min

histogram_quantile(0.99,
sum by (le, verb) (
rate(apiserver_request_duration_seconds_bucket{
verb!~"CONNECT|WATCH",
subresource!~"log|exec|portforward"
}[5m])
)
)

Restituisce p99 latency per verb (GET, POST, PUT, DELETE). Se > 1s per verb LIST significa cluster grande / etcd lento.

5.2 Error rate API server

sum by (verb, code) (
rate(apiserver_request_total{code=~"5.."}[5m])
)

5xx sull’API server. Deve essere ~0. Se non lo è, c’è un problema serio (etcd, cert, memory di kube-apiserver).

5.3 Request rate per resource

topk(10,
sum by (resource, verb) (
rate(apiserver_request_total[5m])
)
)

Utile a capire “chi sta bombardando l’API”. A volte è un operator che pollera troppo aggressivo.


6. etcd health

etcd è il DB del control plane. Un etcd lento = cluster lento.

6.1 etcd fsync latency (write to disk)

histogram_quantile(0.99,
rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])
)

Se > 0.010 (10 ms) sei in zona rossa. Red Hat suggerisce < 10 ms per etcd sano. Cause tipiche di lentezza: disco lento, contention IO.

6.2 etcd backend commit latency

histogram_quantile(0.99,
rate(etcd_disk_backend_commit_duration_seconds_bucket[5m])
)

Se > 0.025 (25 ms) c’è pressione IO su etcd.

6.3 etcd leader changes (dovrebbe essere ~0)

increase(etcd_server_leader_changes_seen_total[1h])

Se > 0 nell’ultima ora, il quorum etcd è instabile. Cause: rete tra master lenta, un master sotto stress, o proprio problemi HW.

6.4 etcd DB size vs quota

etcd_mvcc_db_total_size_in_bytes / (1024 * 1024 * 1024)

Se si avvicina alla quota (default 2 GB, spesso portata a 8 GB su OCP), etcd andrà in NOSPACE e il cluster diventa read-only.


7. OVN-Kubernetes network health

OpenShift 4.18 usa OVN-K di default. Query per sanità della SDN:

7.1 OVN pod restart nel control plane

sum by (pod, container) (
increase(kube_pod_container_status_restarts_total{
namespace="openshift-ovn-kubernetes"
}[1h])
) > 0

I pod ovnkube-master, ovnkube-node, ovs-node non dovrebbero restartare in produzione. Se lo fanno, hai problemi di rete o memoria sul control plane.

7.2 OVN control plane latency

histogram_quantile(0.99,
rate(ovnkube_master_pod_creation_latency_seconds_bucket[5m])
)

Latenza percepita quando OVN crea la network config per un nuovo pod. > 5s è sintomo di control plane satura.

7.3 Node OVN in errore

kube_pod_container_status_ready{
namespace="openshift-ovn-kubernetes",
container="ovnkube-controller"
} == 0

Se qualche ovnkube-controller non è ready, i pod su quel node hanno rete parziale o assente.


8. Ingress / Router health

Il router OpenShift (basato su HAProxy) espone le app all’esterno. Se soffre lui, soffre tutto.

8.1 Router pod restart

sum by (pod, container, node) (
increase(kube_pod_container_status_restarts_total{
namespace="openshift-ingress",
container="router"
}[1h])
* on(pod, namespace) group_left(node) kube_pod_info
) > 0

8.2 HTTP 5xx nel router negli ultimi 5 min

sum by (namespace, route, code) (
rate(haproxy_backend_http_responses_total{code="5xx"}[5m])
)

Filtra i backend che stanno restituendo errori 5xx (backend applicativo giù, non l’infrastruttura).

8.3 Router queue depth

haproxy_backend_current_queue{state="current"}

Se il backend applicativo è lento, HAProxy accoda le connessioni. Coda > 0 sostenuta = throughput backend insufficiente.


9. Persistent Volumes

9.1 PV in stato Failed

kube_persistentvolume_status_phase{phase="Failed"} == 1

9.2 PVC in Pending

kube_persistentvolumeclaim_status_phase{phase="Pending"} == 1

Se un PVC resta Pending, il pod che lo usa non parte. Cause: StorageClass non esiste, CSI operator degraded, quota namespace superata.

9.3 PV con uso > 90% (predittivo prima che l’app si blocchi)

100 * (
1 - (
kubelet_volume_stats_available_bytes
/
kubelet_volume_stats_capacity_bytes
)
) > 90

Con nome PVC e namespace:

topk(20,
100 * (
1 - (
kubelet_volume_stats_available_bytes
/
kubelet_volume_stats_capacity_bytes
)
)
)

10. Cluster operator health

I cluster operator sono i “componenti auto-manutentivi” del cluster. Se anche uno solo va Degraded per troppo tempo, il cluster è a rischio upgrade.

10.1 Cluster operator non Available

cluster_operator_conditions{condition="Available", name!=""} == 0

10.2 Cluster operator Degraded

cluster_operator_conditions{condition="Degraded", name!=""} == 1

10.3 Cluster operator che stanno Progressing da troppo tempo

cluster_operator_conditions{condition="Progressing"} == 1

Se un CO è Progressing da > 15 min in condizioni normali (fuori da upgrade), è bloccato. Vedi con oc describe co <name> cosa dice negli Events.


11. Query “state of the cluster” — 1 minute health check

Quando qualcuno chiede “come sta il cluster?”, queste 5 query in ordine ti danno una salute rapida:

# 1. Node non Ready
count(kube_node_status_condition{condition="Ready", status="false"} == 1)
# 2. Pod non Running/Succeeded
count(kube_pod_status_phase{phase!~"Running|Succeeded"} == 1)
# 3. Cluster operator Degraded
count(cluster_operator_conditions{condition="Degraded"} == 1)
# 4. Pod OOMKilled ultima ora
count(
kube_pod_container_status_last_terminated_reason{reason="OOMKilled"} == 1
)
# 5. API server error rate
sum(rate(apiserver_request_total{code=~"5.."}[5m]))

Se tutte e 5 tornano 0 (o valori molto bassi), il cluster è in salute. Se una è alta, hai il punto d’attacco per l’investigazione.


Note operative

  • Windows di tempo: query con [5m] sono economiche, con [24h] costose. Non abusare di finestre lunghe su cluster grandi (rallenta Prometheus).
  • by (label1, label2, ...) — sempre presente nelle aggregazioni: senza, PromQL somma tutto in un solo numero e perdi contesto.
  • * on(pod, namespace) group_left(node) kube_pod_info — il pattern-oro per arricchire QUALUNQUE metrica pod-level con il node.
  • Salvare le query preferite — nella Console OpenShift, in Observe → Metrics, puoi salvare come “Named query” nei bookmark del browser. Utile.

Vedi anche