Prometheus

Something is wrong. Pods are restarting, latency is climbing, and a request that usually takes 50ms is now taking 2 seconds. You know something happened — users are complaining — but you have no numbers, no history, and no way to know when it started or which service caused it.

The instinct is to grep the logs. And with one service on one server, that works. But once you have 20 services running across 40 pods, grepping logs to understand system behaviour does not scale. You are looking at individual events trying to infer aggregate trends — the wrong tool for the question. Logs tell you what happened in one place at one moment. Metrics tell you how the system is behaving across all of it, over time.

So you use Prometheus. It scrapes metrics from every pod, node, and cluster component on a regular interval and stores them as time series. Now you have the spike, the exact minute it started, and a number attached to every symptom.

How it works

Prometheus is pull-based: it reaches out to your services and scrapes a /metrics endpoint on a schedule. Services expose metrics in a simple text format; Prometheus stores them and makes them queryable.

# EXPOSE from your app
http://my-service:8080/metrics

# Prometheus scrapes this every 15s and stores:
http_requests_total{method="POST", status="500"} 42
http_request_duration_seconds{quantile="0.99"} 1.847

Most infrastructure components (Kubernetes itself, NGINX, Postgres, Redis, JVM) either expose Prometheus metrics natively or have an exporter that does it for them.

In Kubernetes — kube-prometheus-stack

Running Prometheus in Kubernetes manually is fiddly. The fastest path to a working Prometheus + Grafana + Alertmanager stack is the kube-prometheus-stack Helm chart — it installs the Prometheus Operator, Grafana, Alertmanager, node exporters, and a set of default dashboards and alert rules in one go. Add Loki on top for logs.

CRD workaround

kube-prometheus-stack ships with a large set of CRDs. When managed through ArgoCD, applying CRDs and the chart in the same sync can cause ordering failures. The standard workaround is skipCrds: true in the ArgoCD Application, with CRDs applied via a separate kustomize source in the same Application:

- repoURL: "oci://ghcr.io/prometheus-community/charts/kube-prometheus-stack"
  chart: kube-prometheus-stack
  targetRevision: 82.14.1
  helm:
    releaseName: kube-prometheus-stack
    valueFiles:
      - $values/cluster/development/overlay/monitoring/helm/kube-prometheus-stack.yaml
    skipCrds: true

This keeps the CRDs in Git and lets ArgoCD manage them, while avoiding the race condition on first install.

ServiceMonitor

The Prometheus Operator (installed by the stack) manages scrape config via CRDs. The key one is ServiceMonitor — it tells Prometheus which services to scrape without editing Prometheus config directly:

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-app
spec:
  selector:
    matchLabels:
      app: my-app
  endpoints:
    - port: metrics
      interval: 30s

Deploy this alongside your app and Prometheus picks it up automatically.

PromQL

Prometheus Query Language lets you slice and aggregate metrics. A few patterns worth knowing:

# Request rate over last 5 minutes
rate(http_requests_total[5m])

# 99th percentile latency
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))

# Error ratio
rate(http_requests_total{status=~"5.."}[5m])
  /
rate(http_requests_total[5m])

# Memory usage per pod
container_memory_working_set_bytes{namespace="production"}

Alerting

Prometheus evaluates alert rules continuously and fires them to Alertmanager, which handles routing, grouping, and silencing:

- alert: HighErrorRate
  expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.05
  for: 5m
  labels:
    severity: critical
  annotations:
    summary: "Error rate above 5% for {{ $labels.service }}"

for: 5m means the condition must hold for 5 minutes before firing — avoids noisy alerts on brief spikes.

Metrics Server

Metrics Server is a separate, lightweight component that provides the real-time resource metrics (kubectl top pods, kubectl top nodes) that HPA uses to make scaling decisions. It is not Prometheus — it does not store history and is not queryable. It exists purely to feed the Kubernetes control plane.

Install via ArgoCD:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: metrics-server
  namespace: argo-cd
spec:
  project: default
  sources:
    - repoURL: "https://kubernetes-sigs.github.io/metrics-server"
      chart: metrics-server
      targetRevision: 3.13.0
      helm:
        releaseName: metrics-server
  destination:
    server: https://kubernetes.default.svc
    namespace: kube-system
  syncPolicy:
    automated:
      selfHeal: true
      prune: true
    syncOptions:
      - CreateNamespace=true

Install this early — HPA does nothing without it, and kubectl top is the first thing you reach for when something looks wrong.

What Prometheus is not

Prometheus stores metrics — numbers over time. It does not store logs (see Loki) and it does not trace individual requests across services (see Jaeger). Metrics tell you that something is wrong and when it started. The other two tell you what happened and where.

Resources

Built with Hugo
Theme Stack designed by Jimmy