Опирается на правила:
R-OBS-SLO-1…R-OBS-SLO-4иR-OBS-SLO-X1…R-OBS-SLO-X3из Observability Style Guide → раздел 7. SLO и алерты.
Важно знать
- Каждый critical-endpoint имеет SLO (например, 99.9% non-5xx + p95 < 500ms на rolling 30-day window).
- В Python метрика запросов экспонируется через
prometheus-fastapi-instrumentator— имя отличается от Micrometer:http_requests_totalиhttp_request_duration_seconds.- Multi-window multi-burn-rate alerts по Google SRE Workbook: fast burn (1h, rate > 14.4) и slow burn (6h, rate > 6).
- Error budget: 99.9% target = 43 минуты downtime/month. 100% target — нечем оперировать.
- Алерт на исчерпание бюджета (< 10%) → сигнал команде переключиться с features на reliability.
- Алерты отдельные от SLO: infra (event-loop lag, worker saturation), domain (бизнес-фейлы), resilience (CB open), Kafka lag.
- Alert на каждый ERROR → fatigue → команда игнорирует. Агрегируй по типу исключения.
- Каждый алерт имеет runbook. Без инструкции дежурный не знает, кому звонить и что трогать.
SLO (Service Level Objective) — обещание уровня обслуживания: не «всё работает», а «99.9% запросов успешны в течение 30-day window». Разница принципиальная: появляется error budget, появляется инструмент приоритизации reliability vs features.
Экспозиция HTTP-метрик в FastAPI
R-OBS-SLO-1 требует SLI-метрику для каждого critical-endpoint. В Python/FastAPI источником служит prometheus-fastapi-instrumentator, который авто-регистрирует гистограмму http_request_duration_seconds и счётчик http_requests_total с labels handler, method, status_code.
from fastapi import FastAPI
from prometheus_fastapi_instrumentator import Instrumentator
app = FastAPI()
Instrumentator(
should_group_status_codes=False,
should_ignore_untemplated=True,
excluded_handlers=["/health/live", "/health/ready", "/metrics"],
).instrument(app).expose(app, endpoint="/metrics", include_in_schema=False)
Дополнительные бизнес-счётчики — через prometheus_client напрямую:
from prometheus_client import Counter, Histogram
ORDER_CREATED = Counter(
"order_created_total",
"Successful order creations",
["payment_method"],
)
ORDER_FAILED = Counter(
"order_failed_total",
"Failed order creations",
["reason"],
)
CHECKOUT_DURATION = Histogram(
"checkout_duration_seconds",
"End-to-end checkout latency",
buckets=[0.1, 0.25, 0.5, 1.0, 2.5, 5.0],
)
Имена — snake_case с единицей (_seconds, _total): правило R-OBS-MTR-6.
SLO для critical endpoints
R-OBS-SLO-1: для каждого critical-endpoint — availability и latency SLO.
| Endpoint | Availability SLO | Latency SLO |
|---|---|---|
POST /orders | 99.9% non-5xx | p95 < 500ms |
POST /payments | 99.95% non-5xx | p95 < 1s |
GET /orders/{id} | 99.95% non-5xx | p95 < 200ms |
GET /products/search | 99.5% non-5xx | p95 < 800ms |
Расчёт SLI в Prometheus для prometheus-fastapi-instrumentator:
# Availability SLI — доля non-5xx запросов
sum(rate(http_requests_total{handler="/orders",method="POST",status_code!~"5.."}[30d]))
/
sum(rate(http_requests_total{handler="/orders",method="POST"}[30d]))
# Latency SLI — p95
histogram_quantile(0.95,
sum by (le) (rate(http_request_duration_seconds_bucket{handler="/orders",method="POST"}[30d]))
)
Обрати внимание: label называется handler (значение — шаблон пути, например /orders/{order_id}), не uri как в Micrometer. Перед написанием PromQL-запросов стоит проверить реальные имена labels через /metrics.
Multi-window multi-burn-rate
R-OBS-SLO-2: классический подход SRE Workbook chapter 5.
Идея: SLO 99.9% даёт error budget 0.1% за 30 дней. Если за 1 час расходуется 5% бюджета — это быстрое сжигание, при таком темпе бюджет кончится за 20 часов. Это алерт.
Формула burn rate:
burn_rate = (error_rate_in_window) / (1 - SLO_target)
Для SLO 99.9% (target = 0.999, budget = 0.001):
- burn rate > 14.4 (window 1h) → 5% бюджета сгорает за 1 час → немедленный инцидент
- burn rate > 6 (window 6h) → медленная деградация → создай ticket
- burn rate ≤ 1 → нормальная скорость
groups:
- name: orders.slo
rules:
- alert: OrdersSloFastBurn
expr: |
(
sum(rate(http_requests_total{handler="/orders",method="POST",status_code=~"5.."}[1h]))
/
sum(rate(http_requests_total{handler="/orders",method="POST"}[1h]))
) > (14.4 * (1 - 0.999))
for: 2m
labels:
severity: critical
team: order-service
annotations:
summary: "Orders SLO fast burn — потенциальный outage"
runbook: https://runbooks.internal/orders-slo-fast-burn
- alert: OrdersSloSlowBurn
expr: |
(
sum(rate(http_requests_total{handler="/orders",method="POST",status_code=~"5.."}[6h]))
/
sum(rate(http_requests_total{handler="/orders",method="POST"}[6h]))
) > (6 * (1 - 0.999))
for: 15m
labels:
severity: warning
team: order-service
annotations:
summary: "Orders SLO slow burn — деградация без outage"
runbook: https://runbooks.internal/orders-slo-slow-burn
Fast burn будит ops немедленно. Slow burn создаёт ticket — деградация, не критичная, но требует разбора на следующий рабочий день.
Error budget exhaustion
R-OBS-SLO-3: отдельный алерт на остаток бюджета за rolling 30-day window.
# Сколько бюджета осталось (0 = весь израсходован, 1 = весь свободен)
1 - (
(1 - sum(rate(http_requests_total{handler="/orders",method="POST",status_code!~"5.."}[30d]))
/
sum(rate(http_requests_total{handler="/orders",method="POST"}[30d])))
/
(1 - 0.999)
)
Алерт если < 10%:
- alert: OrdersErrorBudgetExhausted
expr: <budget_remaining_expression> < 0.1
for: 1h
labels:
severity: warning
annotations:
summary: "Только 10% error budget остался — /orders"
description: |
Команда переключается с features на reliability.
Релизы рискованных изменений приостановлены до восстановления бюджета.
runbook: https://runbooks.internal/orders-error-budget
Это не «срочно чинить ночью», а сигнал бизнесу: следующий месяц команда фокусируется на устойчивости.
Алерты отдельные от SLO
R-OBS-SLO-4: SLO — про user-facing успешность. Python/FastAPI-сервис порождает отдельные категории алертов.
| Категория | Метрика | Действие |
|---|---|---|
| Infrastructure | process_resident_memory_bytes > 1.5G, event-loop lag > 100ms | SRE: scale, profile |
| Worker saturation | uvicorn_workers_busy / uvicorn_workers_total > 0.9 | scale workers |
| Domain | order_failed_total rate > 100/min | Product: что-то изменилось в данных |
| Resilience | circuitbreaker_state{state="open"} (через stamina или custom) | внешний сервис недоступен |
| Cache | cache_hits / (hits + misses) < 0.7 | tune TTL / size |
| Kafka consumer lag | kafka_consumer_lag_max > 10000 | scale consumers |
Пример domain-алерта для сервиса заказов:
from prometheus_client import Counter
PRODUCT_CHECKOUT_BLOCKED = Counter(
"product_checkout_blocked_total",
"Checkout blocked due to inventory hold failure",
["reason"],
)
- alert: ProductCheckoutBlockedHigh
expr: sum(rate(product_checkout_blocked_total[5m])) > 5
for: 5m
labels:
severity: warning
annotations:
summary: "Высокий rate заблокированных checkout — проверь инвентарь"
runbook: https://runbooks.internal/product-checkout-blocked
Resilience CB open → запросы уходят на fallback → SLO ещё в норме, но проблема назревает. Поэтому инфра-алерты нужны параллельно с SLO-алертами.
Что запрещено
| Антипаттерн | Правило | Что взамен |
|---|---|---|
| Alert на каждый ERROR в логах | R-OBS-SLO-X1 | rate(http_requests_total{status_code=~"5.."}[5m]) с for: 5m |
| SLO 100% target | R-OBS-SLO-X2 | 99.9% (43m/mo) или 99.95% / 99.99% |
Алерт без annotations.runbook | R-OBS-SLO-X3 | URL runbook обязателен в каждом алерте |
| Один алерт «что-то сломалось» | R-OBS-SLO-4 | категории: infra / domain / resilience / SLO burn |
| Burn rate только по 30d без short window | R-OBS-SLO-2 | multi-window: 1h fast + 6h slow |
Latency SLO по avg | R-OBS-SLO-1 | histogram_quantile(0.95, ...) — только percentile |
for: отсутствует или for: 0m | R-OBS-SLO-2 | for: 2m fast / for: 15m slow — фильтр flapping |
High-cardinality label в SLI (customer_id=<value>) | R-OBS-MTR-X1 | только низкая cardinality: handler/method/status_class |
Куда дальше
- Metrics —
prometheus-client,prometheus-fastapi-instrumentator, business-метрики, cardinality. - Tracing — manual span через context-manager, sampling, разбор burn через traces с ошибками.
- Health checks — почему liveness/readiness не для бизнес-SLO.
- Logging — structlog, contextvars, связка
trace_id→ лог → алерт. - Context propagation —
bind_contextvars, async-propagation,copy_contextдля thread-offload. - Конфигурация — отдельный management-порт, explicit endpoints,
/metricsбез auth в проде. - Google SRE Workbook ch.5 — Alerting on SLOs — первоисточник multi-window burn rate.