ЁЯПл The SchoolтА║ЁЯй║ ObservabilityтА║ЁЯзп рдзрдбрд╛ 10 тАФ Incident response: рдЕрдЧреНрдирд┐рд╢рдорди рд╕рд░рд╛рд╡
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯзп рдзрдбрд╛ 10 тАФ Incident response: рдЕрдЧреНрдирд┐рд╢рдорди рд╕рд░рд╛рд╡

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 10 ┬╖ рдорд╛рдЧреАрд▓: lesson-09-slos ┬╖ рдкреБрдвреАрд▓: lesson-11-root-cause


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбреЗ 01тАУ09, рдЖрдгрд┐ page рд╡рд╛рдЬрд▓реНрдпрд╛рд╡рд░ рдХрд╛рдп рд╣реЛрддреЗ: severity levels, incident рдЪреНрдпрд╛ рднреВрдорд┐рдХрд╛ (incident commander, operations lead, communications lead, scribe), рдЖрдзреА mitigate, status page, рдЖрдгрд┐ рдЪрд╛рд░ рдШрдбреНрдпрд╛рд│реЗ тАФ detect, acknowledge, mitigate, resolve (MTTD, MTTA, MTTR). incident_metrics() рдЖрдгрд┐ SEVERITY obs/reliability.py рдордзреНрдпреЗ рдЖрд╣реЗрдд; obs/demo.py рдордзрд▓реЗ incident() рдирд┐рдХрд╛рд▓рд╛рдЪреНрдпрд╛ рджрд┐рд╡рд╕рд╛рдЪреЗ incident рдЪрд╛рд▓рд╡рддреЗ.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рдЖрдЧреАрдЪреА рдШрдВрдЯрд╛ рд╡рд╛рдЬрддреЗ. рдХрд╛рдп рд╡реНрд╣рд╛рдпрд▓рд╛ рд╣рд╡реЗ? 400 рд╡рд┐рджреНрдпрд╛рд░реНрдереА 400 рджрд┐рд╢рд╛рдВрдирд╛ рдкрд│рдгреЗ рдирд╡реНрд╣реЗ.

рд╢рд╛рд│рд╛ рдЕрдЧреНрдирд┐рд╢рдорди рд╕рд░рд╛рд╡ ЁЯзп рдХрд░рддреЗ, рдЖрдгрд┐ рд╕рд░рд╛рд╡рд╛рдд рдкреНрд░рддреНрдпреЗрдХ рдореЛрдареНрдпрд╛ рд╡реНрдпрдХреНрддреАрдХрдбреЗ рдПрдХрдЪ рдХрд╛рдо рдЕрд╕рддреЗ:

рдЖрдгрд┐ рд╕рд░рд╛рд╡рд╛рдЪрд╛ рдкрд╣рд┐рд▓рд╛ рдирд┐рдпрдо: рдЖрдзреА рд╕рдЧрд│реНрдпрд╛рдВрдирд╛ рд╕реБрд░рдХреНрд╖рд┐рдд рдХрд░рд╛. Corridor рдзреБрд░рд╛рдиреЗ рднрд░рд▓реЗрд▓рд╛ рдЕрд╕рддрд╛рдирд╛ рдХреЛрдгреАрд╣реА рдерд╛рдВрдмреВрди "toaster рдХреЛрдгреА рдЪрд╛рд▓реВ рдареЗрд╡рд▓рд╛?" рдЕрд╕реЗ рд╡рд┐рдЪрд╛рд░рдд рдирд╛рд╣реА. рддреЛ рдкреНрд░рд╢реНрди рдирдВрддрд░ рдпреЗрддреЛ тАФ рдзрдбреЗ 11 рдЖрдгрд┐ 12 рдордзреНрдпреЗ.

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart LR
    s["тП▒я╕П 400<br/>errors start"] -->|"detect 9 min"| d["ЁЯЪи 409<br/>page fires"]
    d -->|"acknowledge 2 min"| a["ЁЯСЛ 411<br/>Dipika takes command"]
    a --> m["тЖйя╕П 432<br/>rollback тАФ mitigated<br/>(32 min from start)"]
    m --> r["тЬЕ 470<br/>resolved<br/>(70 min from start)"]
    subgraph roles["ЁЯзСтАНЁЯЪТ roles"]
      ic["ЁЯОЦя╕П incident commander тАФ Dipika"]
      ops["ЁЯФз operations lead тАФ Katrina"]
      com["ЁЯУг communications lead тАФ Aishwarya"]
      sc["ЁЯУЭ scribe"]
    end

ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрдХреГрддреА + рдПрдХ lab: https://school-edh.pages.dev/observability/lesson-diagrams.html#l10

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рдЧреЛрдВрдзрд│ рд╣реАрдЪ рдореВрд│ рд╕реНрдерд┐рддреА рдЕрд╕рддреЗ. рднреВрдорд┐рдХрд╛ рдирд╕рддреАрд▓ рддрд░, рдкрд╛рдЪ engineers рдПрдХрд╛рдЪ рд╡реЗрд│реА рдкрд╛рдЪ рдЧреЛрд╖реНрдЯреА рдмрджрд▓рддрд╛рдд, support рд▓рд╛ рдХреЛрдгреА рд╕рд╛рдВрдЧрдд рдирд╛рд╣реА, рдЖрдгрд┐ рдХрд╛рдп рдХрд░реВрди рдкрд╛рд╣рд┐рд▓реЗ рд╣реЗ рдорд╛рд╣реАрдд рдЕрд╕рд▓реЗрд▓реА рдПрдХрдореЗрд╡ рд╡реНрдпрдХреНрддреА рдЬреЗрд╡рд╛рдпрд▓рд╛ рдЬрд╛рддреЗ. "рдЖрдзреА mitigate" рдирд╕реЗрд▓ рддрд░, team 40 рдорд┐рдирд┐рдЯреЗ cause рд╢реЛрдзрдгреНрдпрд╛рдд рдШрд╛рд▓рд╡рддреЗ рдЖрдгрд┐ рдкрд╛рд▓рдХрд╛рдВрдирд╛ errors рджрд┐рд╕рдд рд░рд╛рд╣рддрд╛рдд тАФ рдЬреЗрд╡реНрд╣рд╛ rollback рдиреЗ рддреЗ 2 рдорд┐рдирд┐рдЯрд╛рдВрдд рд╕рдВрдкрд▓реЗ рдЕрд╕рддреЗ. рдЖрдгрд┐ users рдирд╛ mitigate рдЪреА рд╡реЗрд│ рдЬрд╛рдгрд╡рддреЗ, рд╕рдордЬреВрди рдШреЗрдгреНрдпрд╛рдЪреА рд╡реЗрд│ рдирд╛рд╣реА.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

obs/reliability.py рдордзрд▓реЗ incident_metrics(tl) minute stamps рдШреЗрддреЗ тАФ started, detected, acknowledged, mitigated, resolved тАФ рдЖрдгрд┐ time_to_detect (detected тИТ started), time_to_acknowledge (acknowledged тИТ detected), time_to_mitigate рдЖрдгрд┐ time_to_resolve (рджреЛрдиреНрд╣реА started рдкрд╛рд╕реВрди), рдЖрдгрд┐ users_hurt_minutes (started тЖТ mitigated) рджреЗрддреЗ. SEVERITY рдордзреНрдпреЗ рддреАрди levels рдЖрд╣реЗрдд. obs/demo.py рдордзрд▓реЗ incident() рдирд┐рдХрд╛рд▓рд╛рдЪреНрдпрд╛ рджрд┐рд╡рд╕рд╛рдЪреА рдЦрд░реА timeline рд╡рд╛рдкрд░рддреЗ: 400 рд▓рд╛ errors, 409 рд▓рд╛ page (рдзрдбрд╛ 08), 411 рд▓рд╛ acknowledged, 432 рд▓рд╛ rollback, 470 рд▓рд╛ resolved.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

рдШрдбреНрдпрд╛рд│реЗ рд╣рд▓рд╡рд╛. Bad deploy рдордзреНрдпреЗ рдорд┐рдирд┐рдЯрд╛рд▓рд╛ рд╕реБрдорд╛рд░реЗ 60 requests fail рдЭрд╛рд▓реНрдпрд╛ (1,000 рдЪреЗ 6%), рдореНрд╣рдгреВрди рд╣рд╛рдиреАрдЪреА рдорд┐рдирд┐рдЯреЗ ├Ч 60 тЙИ fail рдЭрд╛рд▓реЗрд▓реЗ рдирд┐рдХрд╛рд▓ views:

python3 obs/demo.py incident
python3 - <<'EOF'
import sys; sys.path.insert(0, "obs"); from reliability import incident_metrics
base = dict(started=400, detected=409, acknowledged=411, mitigated=432, resolved=470)
for name, change in (("today", {}), ("page after 3 min", dict(detected=403, acknowledged=405, mitigated=426)),
                     ("auto-rollback at 404", dict(detected=403, acknowledged=405, mitigated=404)),
                     ("found by parents on social media", dict(detected=425, acknowledged=427, mitigated=448, resolved=486))):
    m = incident_metrics({**base, **change})
    print(f"{name:<34} detect {m['time_to_detect']:>2} ┬╖ ack {m['time_to_acknowledge']} ┬╖ mitigate {m['time_to_mitigate']:>2} ┬╖ resolve {m['time_to_resolve']:>2} ┬╖ failed views тЙИ {m['users_hurt_minutes'] * 60:,}")
EOF

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

incident рд╣реЗ print рдХрд░рддреЗ:

тФАтФА roles: incident commander (Dipika) ┬╖ ops lead (Katrina) ┬╖ communications (Aishwarya) ┬╖ scribe
   SEV1: most parents cannot see results тАФ page everyone, status page, updates every 30 min
   SEV2: a feature is broken for many тАФ page on-call, updates every hour
   SEV3: degraded or a small group affected тАФ fix in working hours
тФАтФА the timeline (minutes): {'started': 400, 'detected': 409, 'acknowledged': 411, 'mitigated': 432, 'resolved': 470}
   time_to_detect       9 min
   time_to_acknowledge  2 min
   time_to_mitigate     32 min
   time_to_resolve      70 min
   users_hurt_minutes   32 min
   first mitigate (roll back, switch off, add capacity), THEN find the cause

рддреБрдордЪрд╛ snippet рд╣реЗ print рдХрд░рддреЛ:

today                              detect  9 ┬╖ ack 2 ┬╖ mitigate 32 ┬╖ resolve 70 ┬╖ failed views тЙИ 1,920
page after 3 min                   detect  3 ┬╖ ack 2 ┬╖ mitigate 26 ┬╖ resolve 70 ┬╖ failed views тЙИ 1,560
auto-rollback at 404               detect  3 ┬╖ ack 2 ┬╖ mitigate  4 ┬╖ resolve 70 ┬╖ failed views тЙИ 240
found by parents on social media   detect 25 ┬╖ ack 2 ┬╖ mitigate 48 ┬╖ resolve 86 ┬╖ failed views тЙИ 2,880

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рдкрд╛рд▓рдХрд╛рдВрдирд╛ mitigate рдЪреЗ рдШрдбреНрдпрд╛рд│ рдЬрд╛рдгрд╡рд▓реЗ, resolve рдЪреЗ рдирд╛рд╣реА: рдкрд╣рд┐рд▓реНрдпрд╛ рддреАрди рдУрд│реАрдВрдордзреНрдпреЗ resolve рдЪреА рд╡реЗрд│ 70 рдорд┐рдирд┐рдЯреЗ рд╣реЛрддреА, рдкрдг fail рдЭрд╛рд▓реЗрд▓реЗ views 1,920 рд╡рд░реВрди 240 рд╡рд░ рдЖрд▓реЗ. рдЬрд▓рдж detection рдиреЗ 6 рдорд┐рдирд┐рдЯреЗ (360 views) рд╡рд╛рдЪрд╡рд▓реА; burn-rate signal рд╡рд░рдЪреНрдпрд╛ automatic rollback рдиреЗ 28 рдорд┐рдирд┐рдЯреЗ (1,680 views) рд╡рд╛рдЪрд╡рд▓реА. Social media рд╡рд░ рдкрд╛рд▓рдХрд╛рдВрдХрдбреВрди рдХрд│рдгреЗ тАФ рдХреЛрдгрддрд╛рд╣реА alert рдирд╛рд╣реА тАФ рдпрд╛рдЪреА рдХрд┐рдВрдордд 16 рдЬрд╛рджрд╛ рдорд┐рдирд┐рдЯреЗ рдЖрдгрд┐ 960 рдЬрд╛рд╕реНрдд fail рдЭрд╛рд▓реЗрд▓реЗ views. рд╕рд░реНрд╡реЛрддреНрддрдо incident рддреЛ, рдЬреЛ рд╡реНрдпрдХреНрддреА рдЬрд╛рдЧреА рд╣реЛрдгреНрдпрд╛рдкреВрд░реНрд╡реАрдЪ machine mitigate рдХрд░рддреЗ.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд

рдЦрд▒реНрдпрд╛ account рд╡рд░ тАФ Kubernetes рд╡рд░ bad deploy рд╕рд╛рдареА рд╕рдЧрд│реНрдпрд╛рдд рдЬрд▓рдж mitigation:

kubectl rollout history deployment/results-api
kubectl rollout undo deployment/results-api            # back to the previous version
kubectl rollout status deployment/results-api --timeout=5m

PagerDuty Events API v2 рдордзреВрди рдХреЛрдгрддреНрдпрд╛рд╣реА script рд╡рд░реВрди page рдХрд░рд╛ (routing key рдПрдХрд╛ service integration рдордзреВрди рдорд┐рд│рддреЗ):

curl -s -X POST https://events.pagerduty.com/v2/enqueue -H 'Content-Type: application/json' -d '{
  "routing_key": "<integration key>",
  "event_action": "trigger",
  "dedup_key": "results-api-burn-rate",
  "payload": {"summary": "results-api fast error-budget burn (14.4x)", "source": "prometheus", "severity": "critical"}
}'

Atlassian Statuspage page рд╡рд░ incident рдЙрдШрдбрд╛:

curl -s -X POST "https://api.statuspage.io/v1/pages/<page id>/incidents" \
  -H "Authorization: OAuth <api key>" -H 'Content-Type: application/json' -d '{
  "incident": {"name": "Results page errors", "status": "investigating",
               "body": "Some parents see an error on the results page. We are rolling back a change. Next update in 30 minutes."}
}'

PagerDuty, incident.io, FireHydrant, Rootly рдЖрдгрд┐ Jira Service Management рд╕рд╛рд░рдЦреА incident tools рддреБрдордЪреНрдпрд╛рд╕рд╛рдареА chat channel рдЙрдШрдбрддрд╛рдд, рднреВрдорд┐рдХрд╛ рд╡рд╛рдЯрддрд╛рдд рдЖрдгрд┐ timeline рдареЗрд╡рддрд╛рдд. Incident channel рд╕рд╛рдареА рдкрд╣рд┐рд▓реНрдпрд╛ message рдЪрд╛ рдирдореБрдирд╛:

ЁЯФ┤ SEV1 declared 11:49 ┬╖ IC: Dipika ┬╖ Ops: Katrina ┬╖ Comms: Aishwarya ┬╖ Scribe: on-call #2
Impact: ~6% of results views fail (504) since 11:40.
Current action: rolling back results-api v42 тЖТ v41 (Katrina).
Next update: 12:19, or sooner if something changes.

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рд╕рд░рд╛рд╡ рдХрд░рд╛. рджрд░ рддрд┐рдорд╛рд╣реАрдд рдПрдХ game day рдШреНрдпрд╛ тАФ staging рдордзреНрдпреЗ рдореБрджреНрджрд╛рдо рдХрд╛рд╣реАрддрд░реА рдореЛрдбрд╛, on-call рд▓рд╛ page рдХрд░рд╛, рднреВрдорд┐рдХрд╛ рдкрд╛рд░ рдкрд╛рдбрд╛ тАФ рдореНрд╣рдгрдЬреЗ рдкрд╣рд┐рд▓реНрдпрд╛ рдЦрд▒реНрдпрд╛ SEV1 рдордзреНрдпреЗ рдХреЛрдгреАрддрд░реА рдкрд╣рд┐рд▓реНрдпрд╛рдВрджрд╛рдЪ IC рдЪреА рднреВрдорд┐рдХрд╛ рд╕рд╛рдВрднрд╛рд│рдд рдирд╕реЗрд▓.

тПня╕П рдкреБрдвреЗ

Rollback рдиреЗ рд╣рд╛рдиреА рдерд╛рдВрдмрд╡рд▓реА. рдкрдг v42 рдХрд╛ рдореЛрдбрд▓реЗ тАФ рдЖрдгрд┐ рдкрд╛рд▓рдХрд╛рдВрдЪреНрдпрд╛ рдЖрдзреА рдХрд╢рд╛рдиреЗрдЪ рддреЗ рдХрд╛ рдкрдХрдбрд▓реЗ рдирд╛рд╣реА? Root-cause analysis.

git checkout lesson-11-root-cause

ЁЯзп Lesson 10 тАФ Incident response: the fire drill

ЁЯУН You are here: Lesson 10 of 12 ┬╖ Previous: lesson-09-slos ┬╖ Next: lesson-11-root-cause


ЁЯУж What's in this branch

Lessons 01тАУ09, plus what happens when the page fires: severity levels, the incident roles (incident commander, operations lead, communications lead, scribe), mitigate first, the status page, and the four clocks тАФ detect, acknowledge, mitigate, resolve (MTTD, MTTA, MTTR). incident_metrics() and SEVERITY live in obs/reliability.py; incident() in obs/demo.py runs the results-day incident.

ЁЯзТ Explain like I'm 5

The fire alarm rings. What should happen? Not 400 pupils running in 400 directions.

The school practises a fire drill ЁЯзп, and in the drill every adult has one job:

And the first rule of the drill: get everyone safe first. Nobody stops to ask "who left the toaster on?" while the corridor is full of smoke. That question comes later тАФ in lessons 11 and 12.

ЁЯЧ║я╕П Diagram

flowchart LR
    s["тП▒я╕П 400<br/>errors start"] -->|"detect 9 min"| d["ЁЯЪи 409<br/>page fires"]
    d -->|"acknowledge 2 min"| a["ЁЯСЛ 411<br/>Dipika takes command"]
    a --> m["тЖйя╕П 432<br/>rollback тАФ mitigated<br/>(32 min from start)"]
    m --> r["тЬЕ 470<br/>resolved<br/>(70 min from start)"]
    subgraph roles["ЁЯзСтАНЁЯЪТ roles"]
      ic["ЁЯОЦя╕П incident commander тАФ Dipika"]
      ops["ЁЯФз operations lead тАФ Katrina"]
      com["ЁЯУг communications lead тАФ Aishwarya"]
      sc["ЁЯУЭ scribe"]
    end

ЁЯЧ║я╕П Drawn version + a lab: https://school-edh.pages.dev/observability/lesson-diagrams.html#l10

тЭУ What

ЁЯдФ Why

Because chaos is the default. Without roles, five engineers change five things at once, nobody tells support, and the one person who knows what was tried goes to lunch. Without "mitigate first", the team spends 40 minutes finding the cause while parents keep seeing errors тАФ when a rollback would have ended it in 2 minutes. And users feel the time to mitigate, not the time to understand.

ЁЯФз How (in this repo)

incident_metrics(tl) in obs/reliability.py takes minute stamps тАФ started, detected, acknowledged, mitigated, resolved тАФ and returns time_to_detect (detected тИТ started), time_to_acknowledge (acknowledged тИТ detected), time_to_mitigate and time_to_resolve (both from started), and users_hurt_minutes (started тЖТ mitigated). SEVERITY holds the three levels. incident() in obs/demo.py uses the real results-day timeline: errors at 400, page at 409 (lesson 08), acknowledged at 411, rollback at 432, resolved at 470.

ЁЯзк Try it

Move the clocks. The bad deploy failed about 60 requests a minute (6% of 1,000), so minutes of harm ├Ч 60 тЙИ failed results views:

python3 obs/demo.py incident
python3 - <<'EOF'
import sys; sys.path.insert(0, "obs"); from reliability import incident_metrics
base = dict(started=400, detected=409, acknowledged=411, mitigated=432, resolved=470)
for name, change in (("today", {}), ("page after 3 min", dict(detected=403, acknowledged=405, mitigated=426)),
                     ("auto-rollback at 404", dict(detected=403, acknowledged=405, mitigated=404)),
                     ("found by parents on social media", dict(detected=425, acknowledged=427, mitigated=448, resolved=486))):
    m = incident_metrics({**base, **change})
    print(f"{name:<34} detect {m['time_to_detect']:>2} ┬╖ ack {m['time_to_acknowledge']} ┬╖ mitigate {m['time_to_mitigate']:>2} ┬╖ resolve {m['time_to_resolve']:>2} ┬╖ failed views тЙИ {m['users_hurt_minutes'] * 60:,}")
EOF

тЬЕ Verify тАФ what you should see

incident prints:

тФАтФА roles: incident commander (Dipika) ┬╖ ops lead (Katrina) ┬╖ communications (Aishwarya) ┬╖ scribe
   SEV1: most parents cannot see results тАФ page everyone, status page, updates every 30 min
   SEV2: a feature is broken for many тАФ page on-call, updates every hour
   SEV3: degraded or a small group affected тАФ fix in working hours
тФАтФА the timeline (minutes): {'started': 400, 'detected': 409, 'acknowledged': 411, 'mitigated': 432, 'resolved': 470}
   time_to_detect       9 min
   time_to_acknowledge  2 min
   time_to_mitigate     32 min
   time_to_resolve      70 min
   users_hurt_minutes   32 min
   first mitigate (roll back, switch off, add capacity), THEN find the cause

Your snippet prints:

today                              detect  9 ┬╖ ack 2 ┬╖ mitigate 32 ┬╖ resolve 70 ┬╖ failed views тЙИ 1,920
page after 3 min                   detect  3 ┬╖ ack 2 ┬╖ mitigate 26 ┬╖ resolve 70 ┬╖ failed views тЙИ 1,560
auto-rollback at 404               detect  3 ┬╖ ack 2 ┬╖ mitigate  4 ┬╖ resolve 70 ┬╖ failed views тЙИ 240
found by parents on social media   detect 25 ┬╖ ack 2 ┬╖ mitigate 48 ┬╖ resolve 86 ┬╖ failed views тЙИ 2,880

ЁЯПБ What you just proved

Parents felt the mitigate clock, not the resolve clock: the resolve time was 70 minutes in the first three rows, but failed views went from 1,920 to 240. Faster detection saved 6 minutes (360 views); an automatic rollback on the burn-rate signal saved 28 minutes (1,680 views). Being told by parents on social media тАФ no alert тАФ cost 16 extra minutes and 960 more failed views. The best incident is the one a machine mitigates before a person wakes up.

тЪая╕П Common mistakes

ЁЯПн In production

On a real account тАФ the fastest mitigation for a bad deploy on Kubernetes:

kubectl rollout history deployment/results-api
kubectl rollout undo deployment/results-api            # back to the previous version
kubectl rollout status deployment/results-api --timeout=5m

Page from any script through the PagerDuty Events API v2 (the routing key comes from a service integration):

curl -s -X POST https://events.pagerduty.com/v2/enqueue -H 'Content-Type: application/json' -d '{
  "routing_key": "<integration key>",
  "event_action": "trigger",
  "dedup_key": "results-api-burn-rate",
  "payload": {"summary": "results-api fast error-budget burn (14.4x)", "source": "prometheus", "severity": "critical"}
}'

Open an incident on an Atlassian Statuspage page:

curl -s -X POST "https://api.statuspage.io/v1/pages/<page id>/incidents" \
  -H "Authorization: OAuth <api key>" -H 'Content-Type: application/json' -d '{
  "incident": {"name": "Results page errors", "status": "investigating",
               "body": "Some parents see an error on the results page. We are rolling back a change. Next update in 30 minutes."}
}'

Incident tools such as PagerDuty, incident.io, FireHydrant, Rootly and Jira Service Management open the chat channel, assign roles and keep the timeline for you. A first message template for the incident channel:

ЁЯФ┤ SEV1 declared 11:49 ┬╖ IC: Dipika ┬╖ Ops: Katrina ┬╖ Comms: Aishwarya ┬╖ Scribe: on-call #2
Impact: ~6% of results views fail (504) since 11:40.
Current action: rolling back results-api v42 тЖТ v41 (Katrina).
Next update: 12:19, or sooner if something changes.

ЁЯПн Why this matters in production: practise. Run a game day each quarter тАФ break something on purpose in staging, page the on-call, run the roles тАФ so the first real SEV1 is not the first time anyone has held the IC role.

тПня╕П Next

The rollback stopped the harm. But why did v42 break тАФ and why did nothing catch it before parents did? Root-cause analysis.

git checkout lesson-11-root-cause
тЖР PreviousslosNext тЖТroot cause

This page is the lesson's README from the lesson-10-incident-response branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.