WAF++ WAF++
Back to WAF++ Homepage

Best Practice: Alerting auf Symptome statt Ursachen

Kontext

Die häufigste Ursache für On-Call-Burnout ist nicht die Arbeit selbst – es sind die falschen Alerts. Ein Team, das 50 Alerts pro Nacht erhält, von denen 45 nicht actionable sind, ignoriert bald alle Alerts.

Symptom-basiertes Alerting ist die Antwort: Alerte nur wenn Nutzer betroffen sind.

Zielbild

Ein reifes Alerting-System:

  • Jeder paging Alert ist symptom-basiert (Fehlerrate, Latenz, Verfügbarkeit)

  • Jeder Alert hat eine Runbook-URL

  • On-Call-Engineers erhalten < 5 Pages pro Schicht (davon 0 false positive)

  • SLOs sind definiert und burn-rate-basierte Alerts konfiguriert

  • Alert-Noise-Metrik wird wöchentlich tracked

Technische Umsetzung

Schritt 1: SLOs definieren

# slo-definitions.yaml (in Version-Control speichern)
services:
  payment-service:
    slos:
      - name: availability
        description: "Verfügbarkeit des Payment Service"
        target: 99.9  # 99.9% = 8.6 Stunden Downtime/Jahr erlaubt
        window: 30d
        metric:
          good_events: "http_requests_total{service='payment',code!~'5..'}"
          total_events: "http_requests_total{service='payment'}"

      - name: latency-p99
        description: "p99 Latenz für Payment-Anfragen"
        target: 99.0  # 99% der Requests unter 500ms
        window: 30d
        metric:
          good_events: "http_request_duration_seconds_bucket{service='payment',le='0.5'}"
          total_events: "http_requests_total{service='payment'}"

      - name: error-rate
        description: "Fehlerrate aller Anfragen"
        target: 99.9  # < 0.1% Fehlerrate
        window: 30d
        metric:
          good_events: "http_requests_total{service='payment',code!~'5..'}"
          total_events: "http_requests_total{service='payment'}"

Schritt 2: Burn-Rate-Alerts konfigurieren (Prometheus/AlertManager)

# prometheus-alerts.yaml
groups:
  - name: payment-service-slo
    rules:
      # Fast-Burn Alert: Page sofort wenn Error Budget 5x schneller verbraucht wird
      - alert: PaymentServiceHighErrorBudgetBurn
        expr: |
          (
            rate(http_requests_total{service="payment",code=~"5.."}[1h])
            /
            rate(http_requests_total{service="payment"}[1h])
          ) > (5 * 0.001)  # 5x burn rate, SLO target = 99.9% -> 0.1% error budget
        for: 2m
        labels:
          severity: critical
          team: payment
        annotations:
          summary: "Payment Service high error budget burn rate"
          description: "Error budget burning 5x faster than allowed. At this rate, monthly budget exhausted in {{ $value | humanizeDuration }}."
          runbook_url: "https://wiki.company.com/runbooks/payment-service/error-budget-burn"

      # Slow-Burn Alert: Ticket erstellen (kein Page) wenn Budget langsam verbrennt
      - alert: PaymentServiceSlowErrorBudgetBurn
        expr: |
          (
            rate(http_requests_total{service="payment",code=~"5.."}[6h])
            /
            rate(http_requests_total{service="payment"}[6h])
          ) > (2 * 0.001)  # 2x burn rate over 6 hours
        for: 60m
        labels:
          severity: warning
          team: payment
        annotations:
          summary: "Payment Service slow error budget burn"
          description: "Error budget burning 2x expected rate. Review and address within 24 hours."
          runbook_url: "https://wiki.company.com/runbooks/payment-service/slow-error-burn"

      # Latency SLO Alert
      - alert: PaymentServiceHighLatency
        expr: |
          histogram_quantile(0.99,
            rate(http_request_duration_seconds_bucket{service="payment"}[5m])
          ) > 0.5  # p99 > 500ms
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Payment Service p99 latency above SLO"
          description: "p99 latency is {{ $value | humanizeDuration }}, exceeding 500ms SLO threshold."
          runbook_url: "https://wiki.company.com/runbooks/payment-service/high-latency"

Schritt 3: CloudWatch Symptom-Alarm mit Terraform konfigurieren

# Symptom-basierter Alert: HTTP 5xx Error Rate
resource "aws_cloudwatch_metric_alarm" "payment_error_rate" {
  alarm_name          = "payment-service-5xx-error-rate"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 2
  datapoints_to_alarm = 2
  threshold           = 10
  treat_missing_data  = "notBreaching"

  metric_query {
    id          = "error_rate"
    expression  = "errors / total * 100"
    label       = "Error Rate %"
    return_data = true
  }

  metric_query {
    id = "errors"
    metric {
      metric_name = "HTTPCode_Target_5XX_Count"
      namespace   = "AWS/ApplicationELB"
      period      = 60
      stat        = "Sum"
      dimensions = {
        LoadBalancer = aws_lb.main.arn_suffix
        TargetGroup  = aws_lb_target_group.app.arn_suffix
      }
    }
  }

  metric_query {
    id = "total"
    metric {
      metric_name = "RequestCount"
      namespace   = "AWS/ApplicationELB"
      period      = 60
      stat        = "Sum"
      dimensions = {
        LoadBalancer = aws_lb.main.arn_suffix
        TargetGroup  = aws_lb_target_group.app.arn_suffix
      }
    }
  }

  alarm_description = <<-EOT
    Payment Service 5xx Error Rate > 10%

    Symptom: Nutzer erhalten HTTP 5xx Fehler vom Payment Service.
    Runbook: https://wiki.company.com/runbooks/payment-service/5xx-errors
    Dashboard: https://monitoring.company.com/d/payment-service
    Eskalation: payment-oncall@company.com → platform-team@company.com
  EOT

  alarm_actions = [aws_sns_topic.payment_oncall.arn]
  ok_actions    = [aws_sns_topic.payment_oncall.arn]
}

# Latenz-Alert (Symptom: Langsame Antwortzeiten für Nutzer)
resource "aws_cloudwatch_metric_alarm" "payment_latency_p99" {
  alarm_name          = "payment-service-p99-latency"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 3
  datapoints_to_alarm = 2
  metric_name         = "TargetResponseTime"
  namespace           = "AWS/ApplicationELB"
  period              = 60
  extended_statistic  = "p99"
  threshold           = 0.5  # 500ms

  dimensions = {
    LoadBalancer = aws_lb.main.arn_suffix
    TargetGroup  = aws_lb_target_group.app.arn_suffix
  }

  alarm_description = <<-EOT
    Payment Service p99 Latenz > 500ms

    Symptom: 1% der Nutzer erleben Antwortzeiten > 500ms (SLO-Verletzung).
    Runbook: https://wiki.company.com/runbooks/payment-service/high-latency
  EOT

  alarm_actions = [aws_sns_topic.payment_oncall.arn]
}

Schritt 4: Alert-Audit durchführen

#!/bin/bash
# alert-audit.sh – Bestehende Alerts auf Qualität prüfen

# Liste alle CloudWatch Alarms
aws cloudwatch describe-alarms --state-value OK,ALARM,INSUFFICIENT_DATA \
  --query 'MetricAlarms[].{
    Name: AlarmName,
    Metric: MetricName,
    Description: AlarmDescription,
    Actions: AlarmActions
  }' \
  --output table

# Für jeden Alarm prüfen:
# 1. Ist die Metrik symptom-basiert? (nicht CPU, Memory)
# 2. Hat die Beschreibung eine Runbook-URL?
# 3. Gibt es AlarmActions (SNS Topic)?
# 4. Wurde der Alarm in den letzten 90 Tagen ausgelöst? War er actionable?

Typische Fehlmuster

Fehlmuster Problem

CPU > 80% Alert

Nicht symptom-basiert; hohe CPU bedeutet nicht zwingend Nutzerauswirkung

Alert ohne Runbook-URL

On-Call-Engineer wacht um 3 Uhr auf und weiß nicht was zu tun ist

Alerts ohne Alarm-Actions (stille Alarms)

Alarms lösen aus aber niemand wird benachrichtigt

Zu niedrige Schwellenwerte

Ständige false positives trainieren Engineers Alerts zu ignorieren

Keine OK-Action

Engineer weiß nicht wann Problem gelöst ist; manuelles Dashboard-Monitoring nötig

Alle Alerts mit demselben Severity

Kein Unterschied zwischen "Service down" und "Anomalie bemerkt"

Metriken

  • Pages pro Schicht: Ziel: < 5 pages pro On-Call-Schicht (8h)

  • False Positive Rate: % der Pages ohne actionable Reaktion (Ziel: < 10%)

  • Alert-Actionability-Rate: % der Pages die zu einer Aktion geführt haben (Ziel: > 90%)

  • MTTR mit vs. ohne Runbook: Vergleich der Incident-Dauer (aus Postmortems)

Reifegrad

Stufe Charakteristika

Level 1

Keine Alerts oder ausschließlich Infrastructure-Metriken (CPU, Memory). Hohe Alert-Noise.

Level 2

HTTP 5xx und Service-Availability Alerts. Keine Runbooks verlinkt.

Level 3

Alle Alerts symptom-basiert mit Runbook-URLs. SLOs definiert. Alert-Noise < 10/Schicht.

Level 4

Burn-Rate-Alerts für alle SLOs. Alert-Noise Metrik tracked und reportiert.

Level 5

Alert-as-Code. Automatische Anomalie-Erkennung. Alert Coverage Report: alle Services abgedeckt.