Incident management: Erfaringer fra 5 år med vagtordning

Introduktion

Efter fem års vagtordning på tværs af forskellige organisationer - fra startups til store virksomheder - har jeg lært, at incident management handler lige så meget om mennesker og processer som om teknologi.

Dette indlæg deler praktiske erfaringer om at opbygge en incident response, der rent faktisk virker, reducere mean time to recovery (MTTR) og skabe en vagtkultur, der ikke brænder dit team ud.

Anatomien af effektiv incident response

Alvorlighedsgrader, der giver mening

De fleste teams overkomplicerer alvorlighedsgrader. Her er en simpel ramme, der virker:

Alvorlighed Definition Svartid Eksempel
SEV1 Fuldstændigt servicenedbrud Øjeblikkeligt Betalingssystem nede
SEV2 Vigtig funktion nedsat 15 minutter Søgning returnerer fejl
SEV3 Mindre påvirkning 1 time Langsom indlæsning af dashboard
SEV4 Ingen brugerpåvirkning Næste arbejdsdag Problem med internt værktøj

Den centrale indsigt: alvorlighed handler om brugerpåvirkning, ikke teknisk kompleksitet. En simpel fejl, der påvirker alle brugere, har højere alvorlighed end et komplekst problem, der ikke påvirker nogen.

De første 5 minutter

De første fem minutter af en hændelse afgør dens forløb. Sådan bør det foregå:

1. Alarm udløses → Vagthavende bekræfter (< 1 min.)
2. Hurtig vurdering: Er dette reelt? Hvor stort er skadesomfanget?
3. Beslut: Kan jeg løse dette alene, eller har jeg brug for hjælp?
4. Ved SEV1/SEV2: Start incident-kanal, tilkald yderligere hjælp
5. Kommunikér: Post en indledende statusopdatering

Jeg har set teams spilde 20+ minutter på at finde ud af, hvem der skulle involveres. Definér eskaleringsveje for almindelige scenarier på forhånd.

Reduktion af MTTR: Hvad der rent faktisk virker

Runbooks, der faktisk bliver brugt

De fleste runbooks er dokumenter, man kun skriver, men aldrig læser. Sådan gør du dem nyttige:

Dårligt runbook:

If the database is slow, check the queries and optimize them.

Godt runbook:

## Symptom: Databaselatens > 500 ms

### Hurtig diagnose (< 2 min.)
1. Tjek aktive forbindelser: `SELECT count(*) FROM pg_stat_activity;`
   - Normalt: < 100
   - Problem: > 200

2. Tjek for langvarige forespørgsler:
   ```sql
   SELECT pid, now() - pg_stat_activity.query_start AS duration, query
   FROM pg_stat_activity
   WHERE state != 'idle'
   ORDER BY duration DESC
   LIMIT 5;

Almindelige løsninger

  • For mange forbindelser: Genstart connection pooleren
    kubectl rollout restart deployment/pgbouncer
  • Langvarig forespørgsel: Afbryd den (hvis sikkert)
    SELECT pg_terminate_backend(<pid>);

Eskalering

Hvis ikke løst inden for 15 min., tilkald @database-team

Forskellen: konkrete kommandoer, forventede værdier og klare eskaleringsveje.

Observability under hændelser

Under en hændelse har du brug for svar hurtigt. Strukturér din observability omkring almindelige spørgsmål:

// Hvad jeg vil vide under en hændelse:
const incidentQueries = {
  // Bliver problemet værre eller bedre?
  errorTrend: 'rate(http_errors_total[5m])',
  
  // Hvornår begyndte dette?
  changePoint: 'changes(deployment_timestamp[1h])',
  
  // Hvad er berørt?
  affectedEndpoints: 'topk(10, sum by (endpoint) (http_errors_total))',
  
  // Er det én enkelt synder, eller er det udbredt?
  errorsByUser: 'sum by (user_id) (http_errors_total)',
};

Byg dashboards, der besvarer disse spørgsmål med ét klik, ikke fem minutters forespørgselsskrivning.

Opbygning af en bæredygtig vagtordning

Vagtkontrakten

Ethvert team bør have en eksplicit vagtkontrakt:

## Forventninger til vagtordningen

**Svartid:**
- SEV1: Bekræftelse inden for 5 minutter
- SEV2: Bekræftelse inden for 15 minutter
- SEV3+: Næste arbejdsdag

**Kompensation:**
- X kr. pr. uges vagt
- Afspadsering efter hændelser (1 times fri pr. times hændelse)

**Støtte:**
- Aldrig en forventning om at løse det alene - eskalering er opmuntret
- Ingen bebrejdelse for opkald uden for arbejdstid
- Sekundær vagthavende som backup

**Grænser:**
- Ingen vagt under ferie
- Maksimalt 1 uge om måneden
- Stilletid: 22-7 (kun SEV1)

At gøre forventninger eksplicitte forebygger udbrændthed og bitterhed.

Reduktion af alarmtræthed

Alarmtræthed er den stille dræber af en velfungerende vagtordning. Sådan bekæmper du den:

80/20-reglen for alarmer:

  • 80 % af opkaldene bør være handlingsrettede
  • Hvis du ignorerer alarmer, så slet dem

Ugentlig alarmgennemgang:

## Alarmgennemgang - uge 15. januar

| Alarm | Opkald | Handlingsrettet | Handling |
|-------|-------|------------|--------|
| Høj CPU | 12 | 2 | Hæv tærskel til 90 % |
| Diskplads | 8 | 8 | Behold |
| API-latens | 15 | 3 | Tilføj auto-skalering |
| Memory leak | 1 | 1 | Behold |

**Beslutning:** Slet Høj CPU-alarm, implementér auto-skalering for API

Det skyldfrie postmortem

Postmortems er stedet, hvor læring sker - eller ikke sker. Her er en skabelon, der virker:

## Hændelse: Nedbrud i betalingsbehandling
**Dato:** 15.08.2024
**Varighed:** 47 minutter
**Alvorlighed:** SEV1
**Forfatter:** [Navn]

### Resumé
Betalingsbehandlingen var utilgængelig i 47 minutter på grund af 
udtømning af en database-connection-pool forårsaget af en 
forespørgselsregression i den seneste deployment.

### Tidslinje
- 14:23 - Deployment afsluttet
- 14:31 - Første kunderapport
- 14:35 - Alarm udløst, vagthavende tilkaldt
- 14:42 - Grundårsag identificeret
- 14:58 - Rollback gennemført
- 15:10 - Fuld genopretning bekræftet

### Grundårsag
En ny forespørgsel i checkout-flowet manglede et indeks, hvilket 
fik forbindelser til at blive holdt i 30+ sekunder i stedet for <100 ms.

### Hvad gik godt
- Hurtig identifikation af grundårsagen
- Rollback-processen forløb problemfrit
- Kundekommunikationen var rettidig

### Hvad kunne forbedres
- Forespørgselsydelsen blev ikke fanget i kodegennemgangen
- Ingen belastningstest for den nye funktion
- Alarmen blev udløst 8 minutter efter den første kunderapport

### Handlingspunkter
| Handling | Ansvarlig | Frist |
|--------|-------|----------|
| Tilføj CI-tjek for forespørgselsydelse | @backend | 22.08.2024 |
| Implementér syntetisk overvågning | @sre | 29.08.2024 |
| Gennemgå alarmgrænser | @on-call | 18.08.2024 |

Nøglen: fokus på systemer, ikke personer. “Deploymentprocessen tillod en langsom forespørgsel” i stedet for “Peter deployede en langsom forespørgsel.”

Kommunikation under hændelser

Intern kommunikation

Under en hændelse: kommunikér hellere for meget end for lidt:

## Skabelon til hændelsesopdatering

**Status:** Undersøges | Identificeret | Overvåges | Løst
**Påvirkning:** [Hvem er berørt, og hvordan]
**Aktuel handling:** [Hvad vi gør lige nu]
**Næste opdatering:** [Hvornår næste opdatering kan forventes]

Eksempel:
---
🔴 **Status:** Identificeret
**Påvirkning:** ~30 % af checkout-forsøg fejler
**Aktuel handling:** Rollback af deployment v2.3.4
**Næste opdatering:** om 10 minutter eller når løst

Ekstern kommunikation

Ved kundevendte hændelser:

Gør:

  • Bekræft hurtigt, selv uden detaljer
  • Brug et klart sprog, ikke teknisk jargon
  • Giv regelmæssige opdateringer, selv når intet har ændret sig
  • Fortæl, hvad I gør for at forhindre gentagelse

Gør ikke:

  • Skyld på tredjeparter (selv hvis det er deres skyld)
  • Lov konkrete løsningstider
  • Overforklar tekniske detaljer
  • Forsvind efter løsningen

Måling af incident response

Målinger, der betyder noget

Følg disse målinger månedligt:

// Hvor ofte har vi hændelser?
const incidentMetrics = {
  incidentCount: 'count by severity',
  
  // Hvor hurtigt reagerer vi?
  timeToAcknowledge: 'median time from alert to acknowledgment',
  
  // Hvor hurtigt løser vi ting?
  timeToResolve: 'median time from alert to resolution',
  
  // Lærer vi noget?
  repeatIncidents: 'incidents with same root cause as previous',
  
  // Er vagtordningen bæredygtig?
  pagesPerWeek: 'average pages per on-call shift',
};

Bevægelse i den rigtige retning

God incident management viser sig ved:

  • Faldende antal hændelser over tid
  • Stabil eller faldende MTTR
  • Lav andel af gentagne hændelser (< 10 %)
  • Bæredygtigt alarmvolumen (< 2 pr. nat)

Erfaringer

Efter hundredvis af hændelser er her, hvad jeg ved med sikkerhed:

  1. Forberedelse slår reaktion. Tid investeret i runbooks og automatisering betaler sig 10-dobbelt under hændelser.
  2. Kommunikation er det halve slag. Det meste stress ved hændelser kommer fra usikkerhed, ikke fra det tekniske problem.
  3. En skyldfri kultur er ikke til forhandling. I det øjeblik folk frygter bebrejdelse, holder de op med at rapportere problemer og dele erfaringer.
  4. Vagtordningens bæredygtighed betyder noget. Udbrændte udviklere træffer dårligere beslutninger og forlader virksomheden.
  5. Hver hændelse er en gave. Det er en gratis stresstest af dine systemer og processer. Lær af den.

Konklusion

Incident management handler ikke om at forhindre alle fejl - det er umuligt. Det handler om at reagere effektivt, når fejl opstår, lære af dem og bygge systemer (både tekniske og menneskelige), der forbedres over tid.

De bedste incident response-teams, jeg har arbejdet med, deler ét træk: de betragter hændelser som muligheder for forbedring, ikke som fejl, der skal skjules.

Ressourcer