etcd & Control Plane
Fonte primaria: github.com/openshift/runbooks — i runbook ufficiali collegati agli alert runbook_url che OpenShift spedisce di default. Query riportate in forma originale (sono codice, non prosa) con contesto riassunto.
Salute etcd
Section titled “Salute etcd”Latenza fsync su disco (p99) — indicatore #1 di storage lento
Section titled “Latenza fsync su disco (p99) — indicatore #1 di storage lento”histogram_quantile(0.99, sum by (instance, le) (irate(etcd_disk_wal_fsync_duration_seconds_bucket{job="etcd"}[5m])))Soglia di riferimento upstream: 10ms di media sostenuta è già sintomo di disco non adeguato per etcd (serve storage a bassa latenza, tipicamente SSD/NVMe locale, non NFS/SAN condivisa).
Latenza commit su disco (p99)
Section titled “Latenza commit su disco (p99)”histogram_quantile(0.99, sum by (instance, le) (irate(etcd_disk_backend_commit_duration_seconds_bucket{job="etcd"}[5m])))Latenza gRPC complessiva (p99) — sintomo di CPU/disco/rete in sofferenza
Section titled “Latenza gRPC complessiva (p99) — sintomo di CPU/disco/rete in sofferenza”histogram_quantile(0.99, sum(rate(grpc_server_handling_seconds_bucket{job=~".*etcd.*", grpc_type="unary"}[5m])) without (grpc_type))% richieste gRPC fallite (per metodo)
Section titled “% richieste gRPC fallite (per metodo)”100 * ( sum without (grpc_type, grpc_code) ( rate(grpc_server_handled_total{ job="etcd", grpc_code=~"Unknown|FailedPrecondition|ResourceExhausted|Internal|Unavailable|DataLoss|DeadlineExceeded" }[5m]) ) / sum without (grpc_type, grpc_code) (rate(grpc_server_handled_total{job="etcd"}[5m])))Cambi di leader (instabilità del cluster etcd)
Section titled “Cambi di leader (instabilità del cluster etcd)”increase(etcd_server_leader_changes_seen_total{job="etcd"}[1h])Più di qualche cambio leader all’ora è anomalo: indica problemi di rete/disco tra i membri del control plane.
Membri etcd non raggiungibili
Section titled “Membri etcd non raggiungibili”sum(up{job="etcd"} == 0)Top consumer CPU per namespace — diagnosi standard quando etcd/apiserver sono lenti
Section titled “Top consumer CPU per namespace — diagnosi standard quando etcd/apiserver sono lenti”Query ufficiale usata nei runbook etcdGRPCRequestsSlow/etcdMembersDown per capire se un namespace applicativo sta rubando CPU al control plane sullo stesso nodo:
topk(25, sort_desc( sum by (namespace) ( ( sum(avg_over_time(pod:container_cpu_usage:sum{container="", pod!=""}[5m])) by (namespace, pod) * on(pod, namespace) group_left(node) (node_namespace_pod:kube_pod_info:) ) * on(node) group_left(role) (max by (node) (kube_node_role{role=~".+"})) )))Overcommit cluster-wide (KubeCPUOvercommit / KubeMemoryOvercommit)
Section titled “Overcommit cluster-wide (KubeCPUOvercommit / KubeMemoryOvercommit)”Formula ufficiale kube-prometheus/OpenShift: confronta le requests totali con la capacità al netto della perdita del nodo più grande (tolleranza a un node failure).
# CPU overcommit — vero se il cluster NON tollera la perdita del nodo più caricosum(namespace_cpu:kube_pod_container_resource_requests:sum{})/sum(kube_node_status_allocatable{resource="cpu"})>((count(kube_node_status_allocatable{resource="cpu"}) > 1) - 1)/ count(kube_node_status_allocatable{resource="cpu"})# Memoria overcommit — stessa logicasum(namespace_memory:kube_pod_container_resource_requests:sum{})/sum(kube_node_status_allocatable{resource="memory"})>((count(kube_node_status_allocatable{resource="memory"}) > 1) - 1)/ count(kube_node_status_allocatable{resource="memory"})In pratica: se hai N nodi worker, il cluster “tollera” la perdita di 1 nodo solo se le requests totali stanno dentro la capacità degli N-1 nodi rimanenti. L’alert scatta quando questo non è più vero.
API server — error budget burn (SLO)
Section titled “API server — error budget burn (SLO)”Formula semplificata dell’alert KubeAPIErrorBudgetBurn (multi-window burn rate, stile Google SRE workbook cap. 5): confronta un burn rate su finestra corta e una lunga per distinguere spike transitori da degradi strutturali.
# Slow-resource: quota di richieste LIST/GET oltre soglia di latenza (100ms), per resource kindsum by (resource) ( rate(apiserver_request_duration_seconds_count{ job="apiserver", verb=~"LIST|GET", subresource!~"proxy|log|exec", scope="resource" }[1d]))-( sum by (resource) ( rate(apiserver_request_duration_seconds_bucket{ job="apiserver", verb=~"LIST|GET", subresource!~"proxy|log|exec", scope="resource", le="0.1" }[1d]) ) or vector(0))/scalar(sum(rate(apiserver_request_total{job="apiserver", verb=~"LIST|GET", subresource!~"proxy|log|exec"}[1d])))Se il risultato è dominato da un resource specifico (es. un CRD con troppe istanze, watch troppo ampie), quello è il primo sospetto.
Errori client Kubernetes verso l’API server (rest_client)
Section titled “Errori client Kubernetes verso l’API server (rest_client)”sum by (cluster, instance, job, namespace) (rate(rest_client_requests_total{job="apiserver", code=~"5.."}[5m]))/sum by (cluster, instance, job, namespace) (rate(rest_client_requests_total{job="apiserver"}[5m]))Kubelet — CPU/Mem per nodo (vista “chi occupa il nodo”, stile alert KubeletDown/NodeNotReady)
Section titled “Kubelet — CPU/Mem per nodo (vista “chi occupa il nodo”, stile alert KubeletDown/NodeNotReady)”# CPU per pod sul nodo (usata dal control plane per capire l'origine di un sovraccarico)sum by (cluster, namespace, pod) ( rate(container_cpu_usage_seconds_total{job="kubelet", metrics_path="/metrics/cadvisor", image!=""}[5m]))* on (cluster, namespace, pod) group_left(node) topk by (cluster, namespace, pod) (1, max by (cluster, namespace, pod, node) (kube_pod_info{node!=""}))
# Memoria per pod sul nodocontainer_memory_working_set_bytes{job="kubelet", metrics_path="/metrics/cadvisor", image!=""}* on (cluster, namespace, pod) group_left(node) topk by (cluster, namespace, pod) (1, max by (cluster, namespace, pod, node) (kube_pod_info{node!=""}))Node condition (formula esatta usata dall’alert KubeNodeNotReady / NodePressure)
Section titled “Node condition (formula esatta usata dall’alert KubeNodeNotReady / NodePressure)”kube_node_status_condition{ job="kube-state-metrics", condition=~"(MemoryPressure|DiskPressure|PIDPressure)", status="true"} == 1Node marcato per rimozione da autoscaler (da escludere dagli alert NotReady)
Section titled “Node marcato per rimozione da autoscaler (da escludere dagli alert NotReady)”( kube_node_spec_taint{job="kube-state-metrics", key="node.kubernetes.io/unreachable", effect="NoSchedule"} unless ignoring(key, value) kube_node_spec_taint{job="kube-state-metrics", key=~"ToBeDeletedByClusterAutoscaler|cloud.google.com/impending-node-termination|aws-node-termination-handler/spot-itn"}) == 1Riferimenti
Section titled “Riferimenti”- openshift/runbooks — etcdGRPCRequestsSlow
- openshift/runbooks — etcdHighNumberOfFailedGRPCRequests
- openshift/runbooks — etcdHighFsyncDurations
- openshift/runbooks — KubeAPIErrorBudgetBurn
- openshift/cluster-monitoring-operator — control-plane/prometheus-rule.yaml
- runbooks.prometheus-operator.dev — KubeCPUOvercommit / KubeMemoryOvercommit