Veröffentlicht am

Kubernetes Monitoring: Prometheus & Grafana Setup

Teilen:
Authors

TL;DR

  • Der kube-prometheus-stack (Helm Chart) liefert Prometheus, Grafana, Alertmanager, kube-state-metrics und node-exporter in einem Paket
  • Die wichtigsten Metriken: CPU/Memory per Pod, Node-Auslastung, Pod-Restarts, API-Server-Latenz
  • ServiceMonitor-CRDs ersetzen manuelle scrape-Konfigurationen
  • Alerting-Regeln gehoeren in PrometheusRule-Objekte, Routing ueber Alertmanager
  • Retention und Storage frueh planen -- 30 Tage bei 50 Gi reichen fuer die meisten Mittelstands-Cluster

Warum ein dedizierter Monitoring-Stack?

Managed-Kubernetes-Dienste (EKS, AKS, GKE) liefern Basis-Metriken, aber fuer Debugging, Kapazitaetsplanung und Alerting reicht das selten. Ein eigener Stack gibt euch volle Kontrolle ueber Retention, Alerting-Logik und Dashboards -- und laeuft im selben Cluster oder in einem dedizierten Monitoring-Cluster.

Die Kombination aus Prometheus (Metriken), Grafana (Visualisierung) und Alertmanager (Benachrichtigungen) hat sich als De-facto-Standard in der Kubernetes-Welt etabliert. Alle drei sind CNCF-Projekte und werden aktiv weiterentwickelt.

Architektur im Ueberblick

                     +-----------------+
                     |    Grafana      |
                     |  (Dashboards)   |
                     +--------+--------+
                              |
                     +--------v--------+
                     |   Prometheus    |
                     |  (TSDB + Rules) |
                     +--+-----------+--+
                        |           |
          +-------------+           +-------------+
          |                                       |
+---------v---------+                   +---------v---------+
| kube-state-metrics|                   |   node-exporter   |
| (K8s-Objekte)     |                   |  (Host-Metriken)  |
+-------------------+                   +-------------------+
          |                                       |
+---------v---------+                   +---------v---------+
|  Alertmanager     |                   | ServiceMonitors   |
| (Routing/Silence) |                   | (App-Metriken)    |
+-------------------+                   +-------------------+

Komponenten:

KomponenteAufgabeTypischer Ressourcenbedarf
PrometheusMetriken scrapen, speichern, Rules auswerten2-4 Gi RAM, 50-100 Gi Storage
GrafanaDashboards, Datenquellen-Verwaltung256-512 Mi RAM
AlertmanagerAlert-Routing, Silencing, Grouping100-200 Mi RAM
kube-state-metricsKubernetes-Objekte als Metriken exponieren128-256 Mi RAM
node-exporterHost-Level-Metriken (CPU, Disk, Network)DaemonSet, 64 Mi RAM/Node

Installation mit kube-prometheus-stack

Der schnellste Weg zum vollstaendigen Stack ist das Helm Chart kube-prometheus-stack. Es installiert alle Komponenten inklusive vorkonfigurierter Dashboards und Alert-Regeln.

# Helm Repo hinzufuegen
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

# Stack installieren mit angepassten Werten
helm install monitoring prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --create-namespace \
  --set prometheus.prometheusSpec.retention=30d \
  --set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.resources.requests.storage=50Gi \
  --set prometheus.prometheusSpec.resources.requests.memory=2Gi \
  --set prometheus.prometheusSpec.resources.requests.cpu=500m \
  --set prometheus.prometheusSpec.resources.limits.memory=4Gi \
  --set grafana.persistence.enabled=true \
  --set grafana.persistence.size=10Gi

# Pruefen, ob alle Pods laufen
kubectl -n monitoring get pods

Nach 2-3 Minuten sollten alle Pods im Status Running sein. Grafana ist ueber den Service monitoring-grafana erreichbar (Standard-Credentials: admin / prom-operator).

Eigene Anwendungen ueberwachen mit ServiceMonitor

Statt die Prometheus-Konfiguration manuell zu editieren, nutzt der kube-prometheus-stack die Custom Resource ServiceMonitor. Prometheus erkennt neue ServiceMonitors automatisch.

# servicemonitor-app.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-app-metrics
  namespace: production
  labels:
    release: monitoring  # Muss zum Prometheus-Selector passen
spec:
  selector:
    matchLabels:
      app: my-app
  endpoints:
    - port: metrics          # Name des Service-Ports
      interval: 30s
      path: /metrics
      scrapeTimeout: 10s
  namespaceSelector:
    matchNames:
      - production

Wichtig: Das Label release: monitoring muss mit dem serviceMonitorSelector eurer Prometheus-Instanz uebereinstimmen. Prueft das mit:

kubectl -n monitoring get prometheus -o jsonpath='{.items[0].spec.serviceMonitorSelector}'

Die wichtigsten Metriken

Cluster-Ebene

# CPU-Auslastung aller Nodes (Prozent)
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# Memory-Auslastung aller Nodes (Prozent)
100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)

# Disk-Auslastung
100 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"} * 100)

Pod-Ebene

# CPU-Verbrauch pro Pod
sum(rate(container_cpu_usage_seconds_total{container!=""}[5m])) by (namespace, pod)

# Memory-Verbrauch pro Pod
sum(container_memory_working_set_bytes{container!=""}) by (namespace, pod)

# Pod-Restarts (letzten 30 Minuten)
increase(kube_pod_container_status_restarts_total[30m]) > 0

# Pods im Fehlerzustand
kube_pod_status_phase{phase=~"Failed|Unknown"} > 0

API-Server

# Request-Latenz (99. Perzentil)
histogram_quantile(0.99, sum(rate(apiserver_request_duration_seconds_bucket{verb!="WATCH"}[5m])) by (le, verb))

# Fehlerrate
sum(rate(apiserver_request_total{code=~"5.."}[5m])) / sum(rate(apiserver_request_total[5m]))

Alerting mit Alertmanager

PrometheusRule fuer kritische Alerts

Alerts werden als PrometheusRule CRD definiert. Prometheus wertet sie aus und schickt feuernde Alerts an den Alertmanager.

# prometheus-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: cluster-alerts
  namespace: monitoring
  labels:
    release: monitoring
spec:
  groups:
    - name: node-alerts
      rules:
        - alert: NodeHighCPU
          expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
          for: 10m
          labels:
            severity: warning
          annotations:
            summary: "Node {{ $labels.instance }} hat hohe CPU-Last"
            description: "CPU-Auslastung liegt seit 10 Min bei {{ $value | humanize }}%"

        - alert: NodeHighMemory
          expr: 100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) > 90
          for: 5m
          labels:
            severity: critical
          annotations:
            summary: "Node {{ $labels.instance }}: Memory fast voll"
            description: "Memory-Auslastung bei {{ $value | humanize }}%"

        - alert: PodCrashLooping
          expr: increase(kube_pod_container_status_restarts_total[1h]) > 5
          for: 5m
          labels:
            severity: critical
          annotations:
            summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} restartet staendig"
            description: "{{ $value }} Restarts in der letzten Stunde"

    - name: disk-alerts
      rules:
        - alert: DiskSpaceLow
          expr: 100 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"} * 100) > 85
          for: 10m
          labels:
            severity: warning
          annotations:
            summary: "Disk fast voll auf {{ $labels.instance }}"

Alertmanager-Konfiguration

Die Alertmanager-Config steuert, wer welche Alerts bekommt und wie sie gruppiert werden.

# alertmanager-config.yaml
apiVersion: v1
kind: Secret
metadata:
  name: alertmanager-monitoring-kube-prometheus-alertmanager
  namespace: monitoring
type: Opaque
stringData:
  alertmanager.yaml: |
    global:
      resolve_timeout: 5m

    route:
      group_by: ['alertname', 'namespace']
      group_wait: 30s
      group_interval: 5m
      repeat_interval: 4h
      receiver: 'default-slack'
      routes:
        - match:
            severity: critical
          receiver: 'critical-pagerduty'
          repeat_interval: 1h
        - match:
            severity: warning
          receiver: 'default-slack'

    receivers:
      - name: 'default-slack'
        slack_configs:
          - api_url: 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL'
            channel: '#k8s-alerts'
            title: '{{ .GroupLabels.alertname }}'
            text: >-
              {{ range .Alerts }}
              *{{ .Labels.severity | toUpper }}*: {{ .Annotations.summary }}
              {{ .Annotations.description }}
              {{ end }}

      - name: 'critical-pagerduty'
        pagerduty_configs:
          - service_key: 'YOUR-PAGERDUTY-KEY'
            description: '{{ .GroupLabels.alertname }}'

    inhibit_rules:
      - source_match:
          severity: 'critical'
        target_match:
          severity: 'warning'
        equal: ['alertname', 'instance']

Grafana Dashboards

Der kube-prometheus-stack liefert bereits solide Dashboards mit. Fuer eigene Dashboards koennt ihr JSON-Modelle als ConfigMap deployen:

# grafana-dashboard-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: custom-dashboard
  namespace: monitoring
  labels:
    grafana_dashboard: "1"  # Wird automatisch von Grafana importiert
data:
  cluster-overview.json: |
    {
      "dashboard": {
        "title": "Cluster Overview",
        "panels": [
          {
            "title": "CPU pro Node",
            "type": "timeseries",
            "targets": [
              {
                "expr": "100 - (avg by(instance) (rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)",
                "legendFormat": "{{ instance }}"
              }
            ],
            "fieldConfig": {
              "defaults": { "unit": "percent", "max": 100 }
            },
            "gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 }
          },
          {
            "title": "Memory pro Node",
            "type": "timeseries",
            "targets": [
              {
                "expr": "100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)",
                "legendFormat": "{{ instance }}"
              }
            ],
            "fieldConfig": {
              "defaults": { "unit": "percent", "max": 100 }
            },
            "gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 }
          },
          {
            "title": "Pod Restarts (letzte Stunde)",
            "type": "stat",
            "targets": [
              {
                "expr": "sum(increase(kube_pod_container_status_restarts_total[1h])) by (namespace, pod)",
                "legendFormat": "{{ namespace }}/{{ pod }}"
              }
            ],
            "gridPos": { "h": 8, "w": 24, "x": 0, "y": 8 }
          }
        ],
        "time": { "from": "now-6h", "to": "now" },
        "refresh": "30s"
      }
    }

Empfohlene Community-Dashboards (Grafana.com IDs):

DashboardGrafana IDBeschreibung
Node Exporter Full1860Detaillierte Host-Metriken
Kubernetes Cluster7249Cluster-Ueberblick
Pod Resources6879CPU/Memory pro Pod
API Server12006API-Server-Performance
CoreDNS14981DNS-Aufloesung im Cluster

Typische Probleme und Loesungen

Prometheus braucht zu viel Speicher

Haeufigste Ursache: zu viele Metriken mit hoher Kardinalitaet (z.B. Labels mit Request-IDs).

# Top-10-Metriken nach Kardinalitaet finden
kubectl -n monitoring port-forward svc/monitoring-kube-prometheus-prometheus 9090:9090 &

curl -s 'http://localhost:9090/api/v1/status/tsdb' | jq '.data.seriesCountByMetricName[:10]'

Loesung: metric_relabel_configs im ServiceMonitor nutzen, um hochkardinalitaere Labels zu droppen.

ServiceMonitor wird nicht erkannt

Checkliste:

  1. Labels stimmen mit serviceMonitorSelector ueberein
  2. Namespace ist in serviceMonitorNamespaceSelector enthalten (oder Selector ist leer = alle Namespaces)
  3. Der Service hat den referenzierten Port-Namen
# Prometheus-Targets pruefen
kubectl -n monitoring port-forward svc/monitoring-kube-prometheus-prometheus 9090:9090 &
# Dann im Browser: http://localhost:9090/targets

Alerts feuern nicht

# Alertmanager-Status pruefen
kubectl -n monitoring port-forward svc/monitoring-kube-prometheus-alertmanager 9093:9093 &
# Dann: http://localhost:9093/#/alerts

# Prometheus-Rules pruefen
kubectl -n monitoring get prometheusrules
kubectl -n monitoring describe prometheusrule cluster-alerts

Skalierung fuer groessere Cluster

Ab ca. 50 Nodes oder 5.000 Pods stossen einzelne Prometheus-Instanzen an Grenzen. Optionen:

AnsatzWann sinnvollKomplexitaet
Vertikale SkalierungBis ~100 Nodes, einfachster WegNiedrig
Prometheus Sharding100+ Nodes, viele NamespacesMittel
Thanos / MimirMulti-Cluster, Langzeit-RetentionHoch
Victoria MetricsKosteneffiziente Alternative zu ThanosMittel

Fuer die meisten Mittelstands-Cluster reicht eine einzelne Prometheus-Instanz mit 4-8 Gi RAM und 100-200 Gi Storage voellig aus.

Checkliste: Monitoring-Stack produktionsreif machen

  • Persistent Storage fuer Prometheus und Grafana konfiguriert
  • Retention passend zum Bedarf eingestellt (15-30 Tage typisch)
  • Resource Requests und Limits fuer alle Monitoring-Pods gesetzt
  • Alertmanager-Routing getestet (Test-Alert senden)
  • ServiceMonitors fuer alle geschaeftskritischen Anwendungen angelegt
  • Grafana-Dashboards fuer die wichtigsten Services eingerichtet
  • RBAC: Monitoring-Namespace hat nur die noetigsten Berechtigungen
  • Backup-Strategie fuer Grafana-Dashboards und Alertmanager-Config

Weitergehende Ressourcen

Fuer verwandte Themen empfehlen wir:


Monitoring-Stack aufsetzen oder optimieren? Wir unterstuetzen Teams beim Aufbau produktionsreifer Observability-Loesungen -- von der initialen Architektur bis zu massgeschneiderten Alerting-Regeln. Kontakt aufnehmen fuer ein unverbindliches Erstgespraech.

Kubernetes-Beratung gesucht?

Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.

📖 Verwandte Artikel

Weitere interessante Beiträge zu ähnlichen Themen