Skip to content

Monitoring and Logging in Kubernetes

Overview

You cannot operate what you cannot see. Production Kubernetes requires metrics (is the cluster healthy?), logs (what happened when the pod crashed?), and traces (where did this request spend 2 seconds?). The CNCF stack — Prometheus, Grafana, Alertmanager, and Loki — is the de facto observability foundation, often deployed via kube-prometheus-stack Helm chart.

This tutorial covers the observability pillars for Kubernetes, ServiceMonitor patterns, log aggregation, golden signals, and alert design that pages humans only when action is required.

This is Tutorial 18 in Module 6: Production of the REBASH Academy Kubernetes series.

Prerequisites

Learning Objectives

By the end of this tutorial, you will be able to:

  • Explain the three pillars of observability in a Kubernetes context
  • Deploy kube-prometheus-stack and access Grafana dashboards
  • Configure ServiceMonitors to scrape application metrics
  • Aggregate container logs with Loki or the EFK pattern
  • Define alerts on golden signals — latency, traffic, errors, saturation
  • Correate metrics, logs, and Kubernetes events during incident response

Architecture Diagram

flowchart TB
    subgraph Apps["Application namespace"]
        API[votestack-api]
        WEB[votestack-web]
        API -->|/metrics| PROM
    end

    subgraph Observability["monitoring namespace"]
        PROM[Prometheus]
        AM[Alertmanager]
        GRAF[Grafana]
        LOKI[Loki]
        PROM --> AM
        PROM --> GRAF
        LOKI --> GRAF
    end

    subgraph Collection
        SM[ServiceMonitor]
        FB["Fluent Bit / Promtail"]
    end

    SM --> PROM
    API --> FB
    FB --> LOKI
    AM -->|PagerDuty / Slack| ONCALL[On-call engineer]

Theory

Three pillars of observability

Pillar Question Kubernetes tools
Metrics How much, how fast? Prometheus, kube-state-metrics, cAdvisor
Logs What exactly happened? Loki, Elasticsearch, CloudWatch
Traces Where did time go? Jaeger, Tempo, OpenTelemetry

Metrics are cheap at scale and drive alerts. Logs provide forensic detail. Traces connect microservice hops — essential for VoteStack's api → redis → worker → postgres flow.

Golden signals (Google SRE)

Signal Kubernetes manifestation
Latency Histogram http_request_duration_seconds
Traffic Counter http_requests_total
Errors 5xx rate, kube_pod_container_status_restarts_total
Saturation CPU/memory vs requests, node disk pressure

Add Availability (uptime) and Cost (resource usage) for platform dashboards.

Prometheus scraping model

Prometheus pulls metrics from targets on an interval. In Kubernetes:

  1. Pods expose /metrics on a named port
  2. ServiceMonitor CRD tells Prometheus Operator which services to scrape
  3. PodMonitor scrapes pods directly (sidecar or hostNetwork patterns)

Key metric types:

Type Example Use
Counter http_requests_total Rate of change
Gauge memory_usage_bytes Current value
Histogram request_duration_seconds Percentiles (p50, p99)
Summary Pre-computed quantiles Legacy — prefer histogram

Logging architecture

Containers log to stdout/stderr — Kubernetes captures via kubelet. Collection agents run as DaemonSet:

Stack Components
PLG Promtail → Loki → Grafana
EFK Fluent Bit → Elasticsearch → Kibana
Managed CloudWatch, GCP Logging, Azure Monitor

Structured JSON logs ({"level":"error","msg":"..."}) enable label-based filtering in Loki.

Alerting philosophy

Alert on symptoms, not causes. Page when users are impacted:

  • ✅ Error rate > 1% for 5 minutes
  • ✅ API p99 latency > 2s
  • ❌ Single pod restart (use ticket, not page)
  • ❌ Node NotReady for 30s (may self-heal)

Use Alertmanager for routing, inhibition, and silencing during maintenance.

Hands-on Lab

Lab 1 — Install kube-prometheus-stack

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

kubectl create namespace monitoring

helm install kube-prometheus prometheus-community/kube-prometheus-stack \
  -n monitoring \
  --set grafana.adminPassword=changeme \
  --set prometheus.prometheusSpec.retention=15d \
  --wait

kubectl get pods -n monitoring
kubectl port-forward svc/kube-prometheus-grafana -n monitoring 3000:80
# Browser: http://localhost:3000 — admin / changeme

Explore built-in dashboards: Kubernetes / Compute Resources / Namespace, Node Exporter.

Lab 2 — Instrument VoteStack api

Add Prometheus client to the api (Node.js example):

const client = require('prom-client');
const register = new client.Registry();
client.collectDefaultMetrics({ register });

const httpRequests = new client.Counter({
  name: 'http_requests_total',
  help: 'Total HTTP requests',
  labelNames: ['method', 'route', 'status'],
  registers: [register],
});

app.get('/metrics', async (req, res) => {
  res.set('Content-Type', register.contentType);
  res.end(await register.metrics());
});

Rebuild and deploy via GitOps. Verify:

kubectl port-forward svc/votestack-api -n votestack 8080:8080
curl -s localhost:8080/metrics | head -20

Lab 3 — ServiceMonitor for api

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: votestack-api
  namespace: votestack
  labels:
    release: kube-prometheus   # must match Prometheus release label
spec:
  selector:
    matchLabels:
      app: votestack-api
  endpoints:
    - port: http
      path: /metrics
      interval: 30s

Ensure the Service exposes port name http:

ports:
  - name: http
    port: 8080
    targetPort: 8080

Apply and confirm target in Prometheus UI → Status → Targets.

Lab 4 — Grafana dashboard panel

In Grafana, add panel:

  • Query: rate(http_requests_total{namespace="votestack"}[5m])
  • Legend: {{ method }} {{ route }}
  • Add second panel: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))

Save dashboard to ConfigMap for GitOps:

apiVersion: v1
kind: ConfigMap
metadata:
  name: votestack-dashboard
  namespace: monitoring
  labels:
    grafana_dashboard: "1"
data:
  votestack.json: |
    { "... dashboard JSON ..." }

Lab 5 — Deploy Loki for log aggregation

helm repo add grafana https://grafana.github.io/helm-charts
helm install loki grafana/loki-stack -n monitoring \
  --set promtail.enabled=true \
  --set loki.persistence.enabled=true \
  --wait

Query logs in Grafana → Explore → Loki:

{namespace="votestack", app="votestack-api"} |= "error"
{namespace="votestack"} | json | level="error"

Correlate with metrics: click timestamp → view pod logs for same interval.

Lab 6 — Alert rules

PrometheusRule for high error rate:

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: votestack-alerts
  namespace: monitoring
  labels:
    release: kube-prometheus
spec:
  groups:
    - name: votestack
      rules:
        - alert: VoteStackApiHighErrorRate
          expr: |
            sum(rate(http_requests_total{namespace="votestack",status=~"5.."}[5m]))
            /
            sum(rate(http_requests_total{namespace="votestack"}[5m]))
            > 0.01
          for: 5m
          labels:
            severity: critical
          annotations:
            summary: "VoteStack API error rate above 1%"
            runbook: "https://wiki.internal/runbooks/votestack-api-errors"
        - alert: VoteStackApiHighLatency
          expr: |
            histogram_quantile(0.99,
              sum(rate(http_request_duration_seconds_bucket{namespace="votestack"}[5m])) by (le)
            ) > 2
          for: 10m
          labels:
            severity: warning

Configure Alertmanager receiver (Slack webhook in Helm values):

alertmanager:
  config:
    receivers:
      - name: slack
        slack_configs:
          - channel: '#alerts'
            api_url: <webhook-url>
    route:
      receiver: slack
      routes:
        - match:
            severity: critical
          receiver: slack

Test: kubectl exec -n monitoring alertmanager-kube-prometheus-alertmanager-0 -- amtool alert add

Commands & Code

# Quick health checks
kubectl top nodes
kubectl top pods -A --sort-by=memory
kubectl get --raw /metrics | head

# Prometheus port-forward
kubectl port-forward svc/kube-prometheus-prometheus -n monitoring 9090:9090

# Recent events (correlate with metrics spikes)
kubectl get events -A --sort-by='.lastTimestamp' | tail -20

# Pod logs
kubectl logs -n votestack deploy/votestack-api --tail=100 -f
kubectl logs -n votestack <pod> --previous   # crashed container

# LogQL examples
# {namespace="votestack"} |= "panic"
# rate({namespace="votestack"}[1m])
Tool Purpose
kubectl top Snapshot CPU/memory (needs metrics-server)
Prometheus Time-series metrics and alerting
Grafana Dashboards and Explore
Loki/LogQL Log query language
kube-state-metrics Kubernetes object state metrics

Common Mistakes

Scraping without resource limits on Prometheus

Unbounded cardinality (high-cardinality labels like user_id) crashes Prometheus — cap label dimensions.

Alerting on every pod restart

Restart loops need investigation but single restarts during deploys are normal — use for: duration and rate thresholds.

No log retention policy

Loki/Elasticsearch disks fill — set retention (15–30 days typical) and sample debug logs in prod.

Missing ServiceMonitor label selector

Prometheus Operator only discovers ServiceMonitors matching its serviceMonitorSelector — verify label release: kube-prometheus.

Metrics on the main app port without auth

/metrics may expose internal stats — restrict via NetworkPolicy or separate metrics port.

Best Practices

RED method for services

Rate, Errors, Duration — three charts per microservice covers 80% of service health.

USE method for resources

Utilization, Saturation, Errors — for nodes and databases.

Structured logging from day one

JSON with trace_id, level, msg — enables Loki parsing and cross-service correlation.

Runbooks linked in alert annotations

Every page includes runbook_url — on-call should never guess first steps.

Separate monitoring cluster for large estates

At scale, federate Prometheus or use centralized observability SaaS to isolate blast radius.

Troubleshooting

Issue Cause Solution
Target down in Prometheus Wrong port name or NetworkPolicy Match Service port name in ServiceMonitor
Grafana no data Datasource not configured Add Prometheus/Loki datasources in Helm values
High cardinality warning Unbounded label values Remove high-cardinality labels from metrics
Logs missing in Loki Promtail not on node Check DaemonSet; verify pod labels
Alerts not firing Rule syntax or for window Test expr in Prometheus UI → Alerts
OOM on Prometheus pod Too much retention/cardinality Reduce retention; drop expensive metrics

Summary

  • Metrics, logs, and traces form the observability pillars — Prometheus and Loki are the common Kubernetes stack
  • Golden signals (latency, traffic, errors, saturation) drive meaningful alerts and dashboards
  • ServiceMonitors declaratively configure Prometheus scraping for application /metrics endpoints
  • Log aggregation via Promtail/Loki or Fluent Bit/EFK centralizes stdout logs from all pods
  • Alert on symptoms with runbook links — reduce noise with for durations and severity routing
  • Next: Kubernetes Security Hardening

Interview Questions

  1. What are the three pillars of observability?
  2. Explain the golden signals and give a Kubernetes example for each.
  3. How does Prometheus discover scrape targets in Kubernetes?
  4. What is a ServiceMonitor and why use it over static config?
  5. Why should applications log to stdout instead of files?
  6. How do you avoid alert fatigue in Kubernetes operations?
  7. What is metric cardinality and why is it dangerous?
  8. Compare PLG stack vs EFK stack for logging.
  9. How would you debug a latency spike using metrics and logs together?
  10. What is the difference between metrics-server and Prometheus?
Sample Answers (Questions 3, 6, and 10)

Q3 — Prometheus discovery: Prometheus Operator watches ServiceMonitor and PodMonitor CRDs. Each defines label selectors matching Services or Pods, plus scrape endpoints (port, path, interval). Prometheus config is generated dynamically — no manual target list when pods scale.

Q6 — Alert fatigue: Alert only on user-visible symptoms (error rate, SLO breach). Use for: windows to ignore transient blips. Route warnings to Slack, criticals to pager. Add inhibition rules (suppress node alerts when cluster is down). Review and delete unused alerts quarterly. Every alert needs a runbook.

Q10 — metrics-server vs Prometheus: metrics-server provides short-term CPU/memory summaries for kubectl top and HPA — lightweight, in kube-system. Prometheus is a full time-series database with long retention, alerting, and custom application metrics — deployed separately. They serve different purposes.

References