Observability triage

Metrics → logs → traces path when the pager fires.

Reviewed: ·Tested on: Kubernetes 1.29, Terraform 1.8, Ubuntu 22.04

📈 High HTTP 5xx error rate

1. Dashboard

Grafana / Prometheus

Request rate vs 5xx ratio — ingress or app RED metrics

2. Logs

App + ingress logs

Stack trace or upstream timeout in last 15m

3. Runbook

502 / upstream path

Trace ingress → service → pod

🔄 Pod restart loop / CrashLoopBackOff

1. Dashboard

Pod restart counter

kube_pod_container_status_restarts_total or CloudWatch ContainerInsights

2. Logs

Previous container logs

kubectl logs --previous for exit reason

3. Runbook

Image pull / OOM

ImagePullBackOff or OOMKilled runbooks

⏱️ P99 latency spike

1. Dashboard

Histogram / latency panel

Compare ingress vs app duration — where time is spent

2. Logs

Slow query / timeout logs

DB connection pool, upstream timeout

3. Runbook

502 debug path

Upstream timeout often masquerades as latency

💾 Node DiskPressure / disk full

1. Dashboard

Node disk usage

node_filesystem_avail_bytes or host disk metrics

2. Logs

Evicted pods + kubelet

Eviction events and image layer bloat

3. Runbook

Node NotReady

Cordon, drain, clean images

🔒 TLS certificate expiring soon

1. Dashboard

cert-manager Certificate Ready

certmanager_certificate_expiration_timestamp_seconds

2. Logs

cert-manager controller

ACME challenge failures

3. Runbook

Renew ingress TLS

ClusterIssuer + DNS challenge

🚀 Deployment rollout failed

1. Dashboard

Rollout status

kubectl rollout status or Argo CD sync health

2. Logs

Failed pod events

describe pod Events section

3. Runbook

Helm release failed

history + rollback

Query snippets — PromQL / LogQL / CloudWatch

PromQL: HTTP 5xx error rate

observe · k8s

# 5xx rate / total rate
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))

# by ingress
sum by (ingress) (rate(nginx_ingress_controller_requests{status=~"5.."}[5m]))

PromQL: pod restart rate

observe · k8s

sum by (namespace, pod) (increase(kube_pod_container_status_restarts_total[1h])) > 3

# top restarters
 topk(10, sum by (pod, namespace) (kube_pod_container_status_restarts_total))

PromQL: P99 latency

observe

histogram_quantile(0.99,
  sum by (le) (rate(http_request_duration_seconds_bucket[5m]))
)

# ingress latency
histogram_quantile(0.99, sum by (le) (rate(nginx_ingress_controller_request_duration_seconds_bucket[5m])))

PromQL: node disk usage %

observe · k8s

100 - (
  avg by (instance) (node_filesystem_avail_bytes{mountpoint="/"}) /
  avg by (instance) (node_filesystem_size_bytes{mountpoint="/"}) * 100
)

PromQL: cert expiry days left

observe · k8s

(certmanager_certificate_expiration_timestamp_seconds - time()) / 86400 < 14

# alert when < 7 days
(certmanager_certificate_expiration_timestamp_seconds - time()) / 86400

LogQL: error lines last 15m

observe

{namespace="apps"} |= "error" or |= "ERROR" or |= "panic"
| json
| line_format "{{.pod}} {{.message}}"

# rate of error log lines
sum(rate({namespace="apps"} |= "error" [5m])) by (pod)

CloudWatch Insights: 5xx / errors

observe · aws

fields @timestamp, @message
| filter @message like /(?i)(error|5\d\d|exception)/
| sort @timestamp desc
| limit 100

# EKS container logs
fields @timestamp, kubernetes.pod_name, @message
| filter @message like /ERROR/
| stats count() by kubernetes.pod_name

PromQL: CPU throttling rate

observe · k8s

sum by (pod, namespace) (rate(container_cpu_cfs_throttled_seconds_total[5m]))

# top throttled
 topk(10, sum by (pod) (rate(container_cpu_cfs_throttled_seconds_total[5m])))

PromQL: pod memory vs limit

observe · k8s

sum by (pod, namespace) (container_memory_working_set_bytes)
/
sum by (pod, namespace) (kube_pod_container_resource_limits{resource="memory"}) * 100

# OOM risk > 90%

Elasticsearch cluster health

observe · debug

curl -s http://localhost:9200/_cluster/health?pretty
curl -s http://localhost:9200/_cat/indices?v
curl -s http://localhost:9200/_cat/shards?v | grep UNASSIGNED

Multi-cloud kubeconfig