RED + USE methods concept page: Tom Wilkie's RED (Rate, Errors, Duration) for request-driven services on the left, Brendan Gregg's USE (Utilization, Saturation, Errors) for DB host resources on the right, with Prometheus + Grafana + Alertmanager observability stack in the center and worker pool example below. Three scenarios demonstrate RED healthy state, USE catching DB pool/disk saturation before RED errors, and combined RED+USE dashboard for full incident picture.
«Что мерять?» — первый вопрос на каждом новом сервисе. Без явного фреймворка команда выбирает метрики хаотично: кто-то любит CPU%, кто-то thread count, кто-то DB connections. Через год дашборд выглядит как «80 виджетов, никто не понимает, что зелёное и что красное», и в инциденте on-call тратит первые 5 минут не на диагноз, а на навигацию.
RED (Tom Wilkie, Weaveworks, 2015) и USE (Brendan Gregg, Netflix, 2012) дают минимально необходимый набор метрик для двух разных зрений на систему: услуга-как-чёрный-ящик (RED: Rate / Errors / Duration) и ресурс-как-устройство (USE: Utilization / Saturation / Errors). Применённые вместе, они сжимают дашборд до 8–12 виджетов на сервис и покрывают ~95% типовых проблем. Google SRE Book формализовал гибрид как Golden Signals (Latency, Traffic, Errors, Saturation) — по сути RED + одна метрика из USE.
Тема обязательная: без RED+USE невозможно ни нормально настроить SLO, ни поймать leading indicators до того, как пользователь увидит 5xx.
Две оси наблюдения, взаимодополняющие, не взаимозаменяемые:
Главный мысленный приём: utilization ≠ saturation. CPU 90% busy без
очереди — это полная загрузка без замедления (хорошо). CPU 50% busy с
run queue = 20 — это backpressure (плохо). Наивный мониторинг «disk
busy < 80% — ОК» промалчивает там, где iostat avgqu-sz = 20 уже
кричит. USE-saturation ловит именно этот класс невидимых для RED
проблем.
Третья E (Errors) у обоих фреймворков общая — потому что failures одинаково важны и снаружи (HTTP 5xx), и внутри (ECC errors на RAM, EIO на диске, connection_failures на pool).
На холсте — каноническая трёхзонная конфигурация observability:
http_requests_total, http_request_duration_seconds), который
экспортирует три метрики ровно по фреймворку RED: rate (counter
на запросы), errors (rate of 5xx по status label), duration
(histogram с buckets, чтобы считать histogram_quantile(0.99, ...)).postgres_exporter +
node_exporter отдают USE на каждый критичный ресурс: CPU
(util / loadavg / cfs_throttled), mem (util / swap_in / OOM
counter), disk (util / avgqu-sz / EIO+SMART), net (NIC util
/ backlog drops / CRC), и отдельной нодой — DB connection pool
(busy / waiters / connection_failures)./metrics каждые
15s, Grafana рисует «RED top panel + USE bottom panel»,
Alertmanager шлёт multi-window burn-rate алерты в PagerDuty.Рёбра — это либо реальный data flow (client → LB → API → pool → DB),
либо scrape-pull (Prometheus → каждая метрическая нода). Кнопка
[DECISIONS] закреплена на нодах api и use-pool — там лежат ADR-ы по
выбору фреймворка и по обязательности USE на pool.
Нормальная нагрузка, 1000 RPS на API. API инкрементирует counter
(rate), считает {status=~"5.."} ratio (errors), кладёт latency в
histogram bucket (duration). Prometheus pulls /metrics каждые 15s.
Grafana через PromQL рисует ровно три виджета: sum(rate(...)) →
1000/s, error ratio → 0.01%, histogram_quantile(0.99, ...) → 180ms.
Operator видит «зелёное» за 2 секунды без необходимости понимать
бизнес-домен. Это контраст к дашборду из 30 виджетов с thread count,
GC pauses, heap usage — где никто не знает, какие из них важны.
Traffic spike с 1000 до 2500 RPS после маркетингового email. RED
держится: p99 поднялся 180→350ms, всё ещё в SLO, errors 0.01%. Но USE
на disk показывает: util 50→85% (ОК) и одновременно avgqu-sz = 20
(saturation — queue растёт). USE на pool: 20/20 busy, 8 waiters в
очереди. Alertmanager на правиле db_pool_waiters > 5 for 60s шлёт
warning. On-call открывает USE-дашборд, видит disk saturation, находит
root cause (отсутствующий индекс на orders.user_id), накатывает фикс
за 5–10 минут до того, как RED начал бы fairil-ить с
connection_acquire_timeout → 5xx. Это и есть leading vs lagging:
saturation предсказала, errors констатировала бы.
Black Friday: 1000 → 2000 RPS. Top panel дашборда (RED) показывает лёгкую деградацию p99 180→280ms. Bottom panel (USE) одновременно загорается: CPU saturation (k8s CFS throttling), queue.depth с 50 ушла на 800, pool 18/20 близко к насыщению. Composite multi-burn-rate alert триггерит page. On-call видит story прямо на дашборде: traffic ↑ → CPU sat → queue piling → pool near-sat. Запускает HPA scale-out (2→6 pods для API, 5→15 для workers), saturations clear за 2 минуты, RED никогда не пробивает SLO. Это контраст: на RED-only дашборде оператор увидел бы p99=280ms (в SLO, нет алерта), инцидент рос бы до тех пор, пока errors не прорвались — и тогда уже было бы поздно.
На диаграмме закреплены два ключевых решения через кнопку [DECISIONS]:
ADR-RED-001: RED для services, USE для resources — обязательный baseline. Выбор framework делает дашборд предсказуемым для всех команд. Правила распределения: HTTP/gRPC services → чистый RED; stateful компоненты (БД, кэш, очередь) → RED на интерфейс + USE на внутренние ресурсы; container/VM/host → чистый USE; external API dependency → RED (внутрь не контролируешь); worker pool/pipeline → USE с queue depth как saturation. Дашборд жёстко ограничен 8–12 виджетами; 13-й виджет требует defending: «какой incident этот widget помог бы поймать».
ADR-RED-002: Histogram, не summary — почему histogram_quantile
победил. Summary считает quantile клиент-сайд и не allows aggregate
между pods (квантиль не аддитивен). Histogram отдаёт buckets, и
histogram_quantile() считает любой percentile, в любом окне, по
любой агрегации. Для SLO burn-rate alerts через
histogram_quantile(0.99, sum(rate(..._bucket[5m])) by (le)) < 250ms
это обязательное условие. Native histograms (Prometheus 2.40+) дают
~1% precision на любом percentile при 8x меньшем storage —
рекомендуется переход когда cluster ready.
ADR-USE-001: USE на DB connection pool — mandatory, не optional.
Pool exhaustion — самая частая дыра в production observability. RED на
API долго показывает «всё ОК», пока pool waiters накапливаются часами,
а потом cascading failure. Обязательны три метрики:
db_pool_connections_busy, db_pool_connections_idle,
db_pool_waiters_count. Алерт: waiters > 0 for 60s → warning,
> 5 for 60s → page. Pool size считается по little's law
(pool_size = throughput * latency), не «с запасом» — большой pool
прячет slow queries и переносит проблему на max_connections DB.
Дополнительные неявные трейд-оффы:
user_id label на RED rate легко даёт 10M unique
series и убивает Prometheus. Для cardinality use traces
(Tempo/Jaeger), для aggregates use metrics.node_exporter отдаёт
USE на host, cAdvisor — USE на контейнер, postgres_exporter /
redis_exporter / kafka_exporter — RED+USE на stateful сервисы.trace.http.request.duration (RED.Duration),
trace.http.request.errors (RED.Errors), system.cpu.user (USE.U) —
ровно та же ось наблюдения, просто rebranded.loadavg / nproc > 1 — проблема. Использовать loadavg / nproc или k8s
container_cpu_cfs_throttled_seconds_total.user_id, request_id) —
миллионы unique series, Prometheus OOM. Для cardinality use traces,
для aggregates use metrics.job_last_success_timestamp,
job_duration_seconds, job_records_processed.