← Alle Beiträge

Observability in der Produktion: Metriken, Traces und Logs die wirklich zählen

Matthias Bruns · · 8 Min. Lesezeit
observability monitoring cloud-native devops

Produktionssysteme fallen aus. Das ist kein Pessimismus – das ist Realität. Die Frage ist nicht, ob deine Cloud-Native-Anwendungen auf Probleme stoßen werden, sondern ob du sie diagnostizieren und beheben kannst, bevor sie Nutzer beeinträchtigen. Hier wird Observability kritisch und geht über einfaches Monitoring hinaus, um tiefe Einblicke in das Systemverhalten zu liefern.

Observability in Software-Systemen bedeutet im Kern, interne Zustände durch externe Ausgaben zu verstehen. Im Gegensatz zu traditionellem Monitoring, das dir sagt was passiert ist, hilft dir Observability zu verstehen warum es passiert ist. Für Teams, die Microservices auf Kubernetes betreiben, kann dieser Unterschied zwischen einer fünfminütigen Reparatur und einer dreistündigen Krisensitzung entscheiden.

Die drei Säulen: Mehr als Marketing-Buzzwords

Die Branche hat sich auf drei Säulen der Observability standardisiert: Metriken, Logs und Traces. Aber wie die Kubernetes-Dokumentation anmerkt, sind das nicht nur Kategorien – es sind sich ergänzende Datenquellen, die zusammen ein vollständiges Bild der Systemgesundheit liefern.

Metriken geben dir die quantitativen Daten: Antwortzeiten, Fehlerquoten, Ressourcenverbrauch. Sie sind die Vitalzeichen deines Systems.

Logs liefern den qualitativen Kontext: was passiert ist, wann es passiert ist und oft warum es passiert ist. Sie sind das Tagebuch deines Systems.

Traces zeigen dir die Reise: wie eine Anfrage durch dein verteiltes System fließt, wo sie langsamer wird und wo sie fehlschlägt. Sie sind das GPS deines Systems.

Das macht es kraftvoll: Jede Säule kompensiert die Schwächen der anderen. Metriken sind effizient, aber ihnen fehlt Kontext. Logs bieten Kontext, können aber überwältigend sein. Traces zeigen Beziehungen, erzeugen aber massive Datenmengen.

Metriken, die tatsächlich Entscheidungen treiben

Die meisten Teams sammeln zu viele Metriken und handeln nach zu wenigen. Der Schlüssel liegt darin, Metriken zu identifizieren, die direkt mit Nutzererfahrung und Geschäftsergebnissen korrelieren.

Die vier goldenen Signale

Beginne mit Googles vier goldenen Signalen, angepasst an deinen spezifischen Kontext:

  • Latenz: Wie lange Anfragen zur Vervollständigung brauchen
  • Traffic: Wie viele Anfragen du bearbeitest
  • Fehler: Wie viele Anfragen fehlschlagen
  • Sättigung: Wie nah deine Ressourcen an der Kapazitätsgrenze sind

Für eine Kubernetes-basierte E-Commerce-API könnte das so aussehen:

# Prometheus recording rules Beispiel
groups:
- name: ecommerce_sli
  rules:
  - record: http_request_duration_seconds:rate5m
    expr: rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m])
  
  - record: http_requests:rate5m
    expr: sum(rate(http_requests_total[5m])) by (service, method)
  
  - record: http_requests_errors:rate5m
    expr: sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
  
  - record: pod_cpu_utilization
    expr: rate(container_cpu_usage_seconds_total[5m]) / container_spec_cpu_quota * 100

Geschäftsspezifische Metriken

Über Infrastruktur-Metriken hinaus, verfolge was für dein Geschäft wichtig ist:

  • Conversion-Funnel-Metriken: Warenkorbhinzufügungen, Checkout-Abschlüsse, Zahlungserfolge
  • Feature-Adoption: Nutzungsraten neuer Features, Tiefe des Nutzerengagements
  • Umsatzauswirkungen: Transaktionsvolumen, durchschnittliche Bestellwerte, fehlgeschlagene Zahlungsraten

Das Ziel ist eine direkte Verbindung von technischen Metriken zu Geschäftsauswirkungen zu schaffen. Wenn die CPU-Auslastung steigt, solltest du sofort wissen, ob es die Checkout-Abschlussraten beeinflusst.

Strukturiertes Logging: Deine Debug-Rettungsleine

Laut Stack Overflows Analyse steigt die Entwicklerproduktivität erheblich, wenn Ingenieure direkt zur Grundursache springen können, anstatt über mehrere Systeme zu suchen. Strukturiertes Logging ist fundamental für diese Fähigkeit.

JSON für alles

Strukturierte Logs im JSON-Format ermöglichen mächtige Abfragen und Korrelationen:

// Schlecht: Unstrukturiertes Logging
console.log("User john_doe failed to complete checkout for order 12345");

// Gut: Strukturiertes Logging
logger.info({
  event: "checkout_failed",
  user_id: "john_doe",
  order_id: "12345",
  error_code: "PAYMENT_DECLINED",
  payment_method: "credit_card",
  cart_value: 89.99,
  session_id: "sess_abc123",
  trace_id: "trace_xyz789"
});

Kontext-Weitergabe

Jeder Log-Eintrag sollte genug Kontext enthalten, um den Anfrage-Fluss zu verstehen:

const logger = winston.createLogger({
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json(),
    winston.format.printf(({ timestamp, level, message, ...meta }) => {
      return JSON.stringify({
        timestamp,
        level,
        message,
        service: process.env.SERVICE_NAME,
        version: process.env.SERVICE_VERSION,
        trace_id: meta.trace_id,
        span_id: meta.span_id,
        user_id: meta.user_id,
        ...meta
      });
    })
  )
});

Log-Level die Sinn ergeben

Nutze Log-Level strategisch:

  • ERROR: Etwas ist kaputt und erfordert sofortige Aufmerksamkeit
  • WARN: Etwas Unerwartetes ist passiert, aber das System hat sich erholt
  • INFO: Normale Geschäftsoperationen (Nutzeraktionen, externe API-Aufrufe)
  • DEBUG: Detaillierte Informationen zur Fehlersuche (in Produktion deaktiviert)

Distributed Tracing: Den Brotkrümeln folgen

In Microservices-Architekturen kann eine einzelne Nutzeranfrage Dutzende von Services berühren. Distributed Tracing verbindet diese Interaktionen und zeigt dir genau, wo Anfragen langsamer werden oder fehlschlagen.

OpenTelemetry-Implementierung

OpenTelemetry ist zum Standard für Instrumentierung geworden. So fügst du Tracing zu einem Node.js-Service hinzu:

const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');

const sdk = new NodeSDK({
  traceExporter: new JaegerExporter({
    endpoint: process.env.JAEGER_ENDPOINT,
  }),
  instrumentations: [getNodeAutoInstrumentations({
    '@opentelemetry/instrumentation-http': {
      requestHook: (span, request) => {
        span.setAttributes({
          'user.id': request.headers['x-user-id'],
          'request.size': request.headers['content-length']
        });
      }
    }
  })]
});

sdk.start();

Custom Spans für Geschäftslogik

Auto-Instrumentierung deckt HTTP-Aufrufe und Datenbankabfragen ab, aber füge Custom Spans für kritische Geschäftsoperationen hinzu:

const opentelemetry = require('@opentelemetry/api');

async function processPayment(orderId, paymentDetails) {
  const tracer = opentelemetry.trace.getTracer('payment-service');
  
  return tracer.startActiveSpan('process_payment', async (span) => {
    span.setAttributes({
      'order.id': orderId,
      'payment.method': paymentDetails.method,
      'payment.amount': paymentDetails.amount
    });
    
    try {
      const result = await paymentGateway.charge(paymentDetails);
      span.setAttributes({
        'payment.status': result.status,
        'payment.transaction_id': result.transactionId
      });
      return result;
    } catch (error) {
      span.recordException(error);
      span.setStatus({ code: opentelemetry.SpanStatusCode.ERROR });
      throw error;
    } finally {
      span.end();
    }
  });
}

Kubernetes-spezifische Observability

Kubernetes Observability erfordert das Verständnis sowohl für Anwendungsverhalten als auch für Cluster-Gesundheit. Die dynamische Natur der Plattform – Pods starten, stoppen und bewegen sich – fügt Komplexität hinzu, die traditionelle Monitoring-Ansätze nicht bewältigen können.

Pod- und Node-Metriken

Überwache Ressourcenverbrauch und Verfügbarkeit:

# Prometheus scrape config für Kubernetes-Metriken
- job_name: 'kubernetes-pods'
  kubernetes_sd_configs:
  - role: pod
  relabel_configs:
  - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
    action: keep
    regex: true
  - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
    action: replace
    target_label: __metrics_path__
    regex: (.+)

Service Mesh Integration

Wenn du ein Service Mesh wie Istio nutzt, nutze dessen eingebaute Observability:

apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: custom-metrics
spec:
  metrics:
  - providers:
    - name: prometheus
  - overrides:
    - match:
        metric: ALL_METRICS
      tagOverrides:
        request_id:
          operation: UPSERT
          value: "%{REQUEST_ID}"

Anwendungsperformance im Kubernetes-Kontext

Traditionelle APM-Tools übersehen oft Kubernetes-spezifischen Kontext. Stelle sicher, dass dein Observability-Stack Anwendungsperformance korreliert mit:

  • Pod-Neustarts und Scheduling-Events
  • Ressourcenlimits und -anforderungen
  • Network Policies und Service Mesh Konfiguration
  • ConfigMap- und Secret-Änderungen

Effektive Dashboards erstellen

Dashboards sollten eine Geschichte erzählen, nicht nur Daten anzeigen. Strukturiere sie um Nutzerreisen und Systemabläufe.

Der umgekehrte Pyramiden-Ansatz

Beginne mit hochrangigen Geschäftsmetriken, dann bohre tiefer:

  1. Geschäfts-KPIs: Umsatz, Conversion-Raten, Nutzerzufriedenheit
  2. Service-Level-Indikatoren: Anfrageraten, Fehlerquoten, Latenzen
  3. Infrastruktur-Metriken: CPU, Speicher, Netzwerk, Storage
  4. Detaillierte Diagnostik: Individuelle Service-Performance, Datenbankabfragen

Alert-Fatigue ist real

Wie Splunk anmerkt, ist das Ziel die Reduzierung der Zeit bis zur Lösung, nicht die Erhöhung des Alert-Volumens. Entwerfe Alerts, die Handlungen erfordern:

# Gut: Handlungsrelevanter Alert
- alert: HighErrorRate
  expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05
  for: 2m
  annotations:
    summary: "Hohe Fehlerrate erkannt"
    description: "Fehlerrate ist {{ $value | humanizePercentage }} für Service {{ $labels.service }}"
    runbook_url: "https://wiki.company.com/runbooks/high-error-rate"

# Schlecht: Störender Alert
- alert: HighCPU
  expr: cpu_usage > 80
  for: 30s

Observability-getriebene Entwicklung

Wikipedia beschreibt Observability-getriebene Entwicklung als das Ausliefern von Features mit Custom Instrumentierung. Das bedeutet, über Observability während der Entwicklung nachzudenken, nicht nach dem Deployment.

Instrumentierung als Code

Beziehe Observability-Anforderungen in deine Definition of Done ein:

  • Jeder neue API-Endpoint beinhaltet Metriken, Logging und Tracing
  • Geschäftslogik beinhaltet Custom Spans für kritische Operationen
  • Fehlerbehandlung beinhaltet strukturiertes Error-Logging
  • Feature Flags beinhalten Adoptions- und Performance-Metriken

Observability testen

Teste deine Observability-Instrumentierung wie jeden anderen Code:

describe('Payment Processing', () => {
  it('should create trace spans for payment operations', async () => {
    const mockTracer = new MockTracer();
    
    await processPayment('order-123', paymentDetails);
    
    const spans = mockTracer.report().spans;
    expect(spans).toHaveLength(3); // payment validation, gateway call, result processing
    expect(spans[0].operationName).toBe('validate_payment');
    expect(spans[1].operationName).toBe('gateway_charge');
    expect(spans[2].operationName).toBe('process_result');
  });
});

Die Wirtschaftlichkeit von Observability

Observability ist nicht kostenlos. Datenaufnahme-, Speicher- und Verarbeitungskosten können schnell außer Kontrolle geraten. Plane etwa 5-15% deiner Infrastrukturkosten für Observability-Tooling ein.

Sampling-Strategien

Für Traffic-intensive Services, implementiere intelligentes Sampling:

const sampler = {
  shouldSample: (context, traceId, spanName, spanKind, attributes) => {
    // Immer Fehler sampeln
    if (attributes['http.status_code'] >= 400) {
      return { decision: SamplingDecision.RECORD_AND_SAMPLE };
    }
    
    // 1% der erfolgreichen Anfragen sampeln
    if (traceId[0] % 100 === 0) {
      return { decision: SamplingDecision.RECORD_AND_SAMPLE };
    }
    
    return { decision: SamplingDecision.NOT_RECORD };
  }
};

Datenaufbewahrungsrichtlinien

Implementiere gestufte Aufbewahrung:

  • Hochauflösende Metriken: 7 Tage
  • Mittelauflösende Metriken: 30 Tage
  • Niedrigauflösende Metriken: 1 Jahr
  • Traces: 7 Tage (mit längerer Aufbewahrung für Fehler)
  • Logs: 30 Tage (mit längerer Aufbewahrung für Fehler und Sicherheitsereignisse)

Observability umsetzbar machen

Das beste Observability-Setup ist nutzlos, wenn es nicht zu besseren Ergebnissen führt. Microsofts Forschung zu AI-Systemen betont, dass Observability fundamental für operative Kontrolle in Produktionssystemen ist.

Runbooks und Automatisierung

Jeder Alert sollte zu einem Runbook verlinken, das erklärt:

  • Was der Alert bedeutet
  • Wie das Problem untersucht wird
  • Häufige Ursachen und Lösungen
  • Wann eskaliert werden sollte

Noch besser, automatisiere Antworten wo möglich:

apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
  name: auto-remediation
spec:
  entrypoint: high-error-rate-response
  templates:
  - name: high-error-rate-response
    steps:
    - - name: scale-up
        template: scale-deployment
        arguments:
          parameters:
          - name: deployment
            value: "{{workflow.parameters.deployment}}"
          - name: replicas
            value: "{{workflow.parameters.current-replicas * 2}}"

Kontinuierliche Verbesserung

Nutze Observability-Daten, um Architekturentscheidungen zu treiben:

  • Identifiziere Services, die von Caching profitieren würden
  • Erkenne Datenbankabfragen, die Optimierung benötigen
  • Finde Microservice-Grenzen, die unnötige Latenz erzeugen
  • Entdecke Features, die nicht genutzt werden und deprecated werden können

Der Weg nach vorn

Observability ist kein Ziel – es ist eine Fähigkeit, die sich mit deinen Systemen entwickelt. Beginne mit den Grundlagen: strukturiertes Logging, Schlüsselmetriken und Distributed Tracing für kritische Pfade. Erstelle Dashboards, die Geschichten erzählen, nicht nur Daten anzeigen. Schaffe Alerts, die Handlungen antreiben, nicht Lärm.

Am wichtigsten: Mache Observability zu einer Teamverantwortung, nicht nur zu einem Operations-Anliegen. Wenn Entwickler schnell verstehen können, wie sich ihr Code in der Produktion verhält, gewinnen alle: schnelleres Debugging, weniger Ausfälle und Systeme, die tatsächlich mit Vertrauen skalieren.

Die Komplexität von Cloud-Native-Systemen wird nicht verschwinden. Aber mit ordentlicher Observability wird diese Komplexität handhabbar, debugbar und letztendlich ein Wettbewerbsvorteil.

Lesebarkeit

Schriftgröße