Чому управління інцидентами важливе
Кожна продакшн-система рано чи пізно зазнає збою. Різниця між високопродуктивними організаціями та рештою — не в тому, чи трапляються інциденти, а в тому, як вони реагують, коли це стається.
Практики Site Reliability Engineering від Google стали галузевим стандартом для управління надійністю у масштабі. Але вам не потрібна інфраструктура масштабу Google, щоб скористатися перевагами SRE-принципів. Навіть малі команди можуть кардинально покращити свою реакцію на інциденти, прийнявши структуровані практики навколо сповіщень, ескалації, комунікації та навчання на збоях.
Крок 1: Визначте SLO перед визначенням сповіщень
Найбільша помилка команд — сповіщати на метрики, не пов'язані з впливом на користувача. Ваш моніторинг має керуватися Service Level Objectives (SLO), а не довільними порогами.
Почніть з визначення того, що важливо вашим користувачам:
# slo-definition.yaml
service: checkout-api
slos:
- name: availability
description: "Checkout API returns successful responses"
target: 99.95%
window: 30d
sli:
type: ratio
good: 'http_requests_total{status=~"2..|3.."}'
total: 'http_requests_total'
- name: latency
description: "Checkout completes within acceptable time"
target: 99.0%
window: 30d
sli:
type: ratio
good: 'http_request_duration_seconds_bucket{le="0.5"}'
total: 'http_request_duration_seconds_count'
Маючи визначені SLO, ви можете обчислити свій error budget — кількість ненадійності, яку можете толерувати. Ціль доступності 99.95% дає вам приблизно 21.6 хвилин простою на місяць. Коли ваш error budget вигорає швидше за очікуване — саме тоді ви сповіщаєте.
Впровадження error budget сповіщень з Prometheus
Використовуйте multi-window, multi-burn-rate сповіщення, як рекомендує книга Google SRE:
# prometheus-rules.yaml
groups:
- name: slo-alerts
rules:
# Fast burn: 14.4x burn rate over 1 hour (fires within 1h)
- alert: CheckoutHighErrorBudgetBurn
expr: |
(
sum(rate(http_requests_total{service="checkout",status=~"5.."}[1h]))
/
sum(rate(http_requests_total{service="checkout"}[1h]))
) > (14.4 * 0.0005)
and
(
sum(rate(http_requests_total{service="checkout",status=~"5.."}[5m]))
/
sum(rate(http_requests_total{service="checkout"}[5m]))
) > (14.4 * 0.0005)
for: 2m
labels:
severity: critical
team: checkout
annotations:
summary: "Checkout API burning error budget 14.4x faster than normal"
runbook: "https://wiki.internal/runbooks/checkout-high-error-rate"
dashboard: "https://grafana.internal/d/checkout-slo"
# Slow burn: 3x burn rate over 3 days (catches gradual degradation)
- alert: CheckoutSlowErrorBudgetBurn
expr: |
(
sum(rate(http_requests_total{service="checkout",status=~"5.."}[3d]))
/
sum(rate(http_requests_total{service="checkout"}[3d]))
) > (3 * 0.0005)
for: 1h
labels:
severity: warning
team: checkout
annotations:
summary: "Checkout API slowly burning through error budget"
Цей підхід усуває шумні сповіщення, які не відображають реального впливу на користувача. Ви пейджите людей лише тоді, коли сервіс порушує свою обіцянку користувачам.
Крок 2: Створіть on-call ротацію, що не вигоряє людей
On-call повинен бути sustainable. Команди, що випалюють своїх on-call інженерів, отримують високу плинність, низький моральний дух і, іронічно, гіршу надійність. Дотримуйтеся цих принципів:
- Мінімум двоє людей на ротацію — Primary і secondary, щоб ніхто не ніс навантаження сам
- Регулярна частота ротації — Тижневі ротації стандартні; довше ніж два тижні веде до втоми
- Компенсуйте on-call час — Через додаткову оплату, відгули або те і інше
- Максимальна частота переривань — Якщо команда отримує більше двох пейджів за зміну on-call, інвестуйте в покращення надійності, перш ніж додавати нові функції
Ось розклад PagerDuty, налаштований через Terraform:
# pagerduty.tf
resource "pagerduty_schedule" "checkout_oncall" {
name = "Checkout Team On-Call"
time_zone = "America/New_York"
layer {
name = "Primary"
start = "2025-01-01T00:00:00-05:00"
rotation_virtual_start = "2025-01-01T00:00:00-05:00"
rotation_turn_length_seconds = 604800 # 1 week
users = [
pagerduty_user.alice.id,
pagerduty_user.bob.id,
pagerduty_user.carol.id,
pagerduty_user.dave.id,
]
}
layer {
name = "Secondary"
start = "2025-01-01T00:00:00-05:00"
rotation_virtual_start = "2025-01-08T00:00:00-05:00"
rotation_turn_length_seconds = 604800
users = [
pagerduty_user.alice.id,
pagerduty_user.bob.id,
pagerduty_user.carol.id,
pagerduty_user.dave.id,
]
}
}
resource "pagerduty_escalation_policy" "checkout" {
name = "Checkout Escalation"
num_loops = 2
rule {
escalation_delay_in_minutes = 10
target {
type = "schedule_reference"
id = pagerduty_schedule.checkout_oncall.id
}
}
rule {
escalation_delay_in_minutes = 15
target {
type = "user_reference"
id = pagerduty_user.engineering_manager.id
}
}
}
Крок 3: Встановіть фреймворк реагування на інциденти
Коли стається інцидент, хаос — ворог. Визначте чіткі ролі та структурований процес ще до того, як вони знадобляться.
Рівні серйозності інцидентів
| Серйозність | Визначення | Час реакції | Комунікація |
|---|---|---|---|
| SEV-1 | Сервіс повністю лежить, всі користувачі зачеплені | 5 хвилин | War room, status page, сповіщення керівництва |
| SEV-2 | Великий функціонал деградував, значний вплив | 15 хвилин | War room, status page |
| SEV-3 | Мала деградація, обмежений вплив | 30 хвилин | Сповіщення в каналі команди |
| SEV-4 | Без впливу на користувача, потенційна майбутня проблема | Наступний робочий день | Створено тікет |
Ролі під час інциденту
Кожен SEV-1 чи SEV-2 інцидент повинен мати ці ролі явно призначені:
- Incident Commander (IC) — Координує реакцію, приймає рішення, веде до резолюції
- Technical Lead — Діагностує root cause, впроваджує виправлення
- Communications Lead — Оновлює стейкхолдерів, status page та клієнтів
- Scribe — Документує таймлайн, вжиті дії та ключові рішення
Автоматизоване створення інцидентів зі Slack та PagerDuty
Використовуйте бота для стандартизації створення інцидентів. Ось спрощений incident bot на Python:
# incident_bot.py
import slack_sdk
import requests
from datetime import datetime
class IncidentManager:
def __init__(self, slack_token, pagerduty_token):
self.slack = slack_sdk.WebClient(token=slack_token)
self.pd_token = pagerduty_token
def declare_incident(self, severity, title, reporter):
# Create dedicated incident channel
timestamp = datetime.now().strftime("%Y%m%d-%H%M")
channel_name = f"inc-{timestamp}-{severity}"
channel = self.slack.conversations_create(
name=channel_name,
is_private=False
)
channel_id = channel["channel"]["id"]
# Post incident template
self.slack.chat_postMessage(
channel=channel_id,
blocks=[
{
"type": "header",
"text": {"type": "plain_text", "text": f"🚨 {severity.upper()}: {title}"}
},
{
"type": "section",
"fields": [
{"type": "mrkdwn", "text": f"*Reporter:* {reporter}"},
{"type": "mrkdwn", "text": f"*Status:* Investigating"},
{"type": "mrkdwn", "text": f"*IC:* Unassigned"},
{"type": "mrkdwn", "text": f"*Started:* {datetime.now().isoformat()}"},
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": (
"*Checklist:*\n"
"☐ Assign Incident Commander\n"
"☐ Assess user impact\n"
"☐ Update status page\n"
"☐ Identify mitigation steps\n"
"☐ Implement fix or rollback\n"
"☐ Verify resolution\n"
"☐ Schedule postmortem"
)
}
}
]
)
# Trigger PagerDuty if SEV-1 or SEV-2
if severity in ("sev-1", "sev-2"):
self._trigger_pagerduty(title, severity, channel_name)
return channel_id
def _trigger_pagerduty(self, title, severity, channel):
requests.post(
"https://events.pagerduty.com/v2/enqueue",
json={
"routing_key": "YOUR_INTEGRATION_KEY",
"event_action": "trigger",
"payload": {
"summary": f"[{severity.upper()}] {title}",
"severity": "critical" if severity == "sev-1" else "error",
"source": f"slack/{channel}",
}
}
)
Крок 4: Пишіть безвинні post-mortem
Post-mortem — це місце, де відбувається організаційне навчання. Без нього ви приречені повторювати ті ж збої. Ключове слово — безвинний — фокусуйтесь на системах і процесах, а не людях.
Кожен post-mortem має охоплювати:
Шаблон Post-Mortem
## Incident Summary
- **Date:** 2025-12-15
- **Duration:** 47 minutes
- **Severity:** SEV-2
- **Impact:** 12% of checkout requests failed for users in EU region
## Timeline (UTC)
- 14:23 — Monitoring detects elevated 5xx rate on checkout-api-eu
- 14:25 — PagerDuty pages on-call engineer (Alice)
- 14:28 — Alice joins incident channel, assumes IC role
- 14:35 — Root cause identified: database connection pool exhausted
- 14:42 — Mitigation applied: increased pool size via config change
- 14:50 — Error rates return to normal
- 15:10 — Incident resolved, monitoring confirmed stable
## Root Cause
A background migration job was opened without connection limits,
consuming all available database connections in the EU region pool.
## Contributing Factors
1. Migration jobs share the same connection pool as production traffic
2. No connection limit enforced on batch operations
3. Connection pool exhaustion alert had a 15-minute delay
## Action Items
| Action | Owner | Priority | Due Date |
|--------|-------|----------|----------|
| Separate connection pool for batch jobs | Bob | P1 | 2025-12-22 |
| Add connection pool utilization alert | Carol | P1 | 2025-12-20 |
| Document migration runbook | Alice | P2 | 2026-01-05 |
| Load test batch operations | Dave | P2 | 2026-01-10 |
## Lessons Learned
- **What went well:** Fast detection (2 min), clear escalation, mitigation applied quickly
- **What could improve:** Batch jobs need resource isolation, need better pre-migration checklist
Заплануйте перегляд post-mortem протягом 48 годин після інциденту, поки деталі свіжі. Зробіть відвідування обов'язковим для команди і опціональним для решти організації. Публікуйте post-mortem широко — прозорість будує довіру і запобігає повторюваним помилкам.
Крок 5: Вимірюйте та покращуйте з часом
Відстежуйте ці метрики управління інцидентами щомісяця:
- MTTD (Mean Time to Detect) — Як швидко ви помічаєте проблеми
- MTTA (Mean Time to Acknowledge) — Як швидко хтось реагує на пейдж
- MTTR (Mean Time to Resolve) — Загальний час від виявлення до резолюції
- Частота інцидентів за серйозністю — Тренд вгору чи вниз?
- Рівень виконання action items з post-mortem — Чи ви насправді вчитеся?
- Alert noise ratio — Відсоток сповіщень, що не вимагали людської дії
Побудуйте дашборд Grafana, що відстежує це з часом. Якщо MTTR не покращується від кварталу до кварталу, ваш post-mortem процес потребує уваги.
Інструменти, які ми рекомендуємо
- Сповіщення та on-call: PagerDuty, Opsgenie або Grafana OnCall
- Координація інцидентів: Slack з Rootly, incident.io або FireHydrant
- Status pages: Atlassian Statuspage, Instatus або Cachet
- Відстеження SLO: Sloth, Pyrra або Nobl9
- Управління post-mortem: Jeli, Blameless або простий шаблон у спільному документі
Висновок
Ефективне управління інцидентами — це не запобігання кожному збою, а побудова систем і процесів, що мінімізують вплив і максимізують навчання. Організації, що добре справляються з інцидентами, — це ті, що практикують ще до того, як знадобиться, інвестують в інструменти і ставляться до кожного post-mortem як до подарунка.
У DevOpsVibe ми допомагаємо командам будувати процеси управління інцидентами світового рівня з нуля. Від проєктування SLO-керованих сповіщень до впровадження автоматизованих incident workflows та тренування вашої команди на фасилітації post-mortem — ми приносимо реальний SRE-досвід у вашу організацію. Дозвольте нам допомогти вам спати краще на on-call.