SLO, бюджеты ошибок и алерты, которые не кричат впустую
SLI — отношение good/valid, SLO — его цель, бюджет ошибок — 1−SLO: 99.9% даёт ~43 мин/мес. Пейджи по симптомам (burn rate, p99), не по причинам (CPU); композитные алармы и treat-missing-as-breaching убивают шум, приучающий дежурного игнорировать пейджер.
Три недели назад кто-то навесил CPU-аларм на каждый хост парка: пейдж, если CPU > 80% в течение пяти минут. Он срабатывал на каждый деплой, каждый batch-джоб, каждый ночной cron — десяток раз в день, и к моменту, когда кто-то смотрел, всё уже было зелёным. Дежурная смена поступила рационально: замьютила канал. И вот во вторник в 02:14 платёжный провайдер начал отваливаться по таймаутам, error rate чекаута пополз к 30%, а единственный аларм, который имел значение, выстрелил в канал, который уже никто не читал. Авария шла сорок минут, пока её не эскалировал твит клиента. Разбор инцидента винил не замьюченный канал — он винил дизайн, который сделал мьют рациональным. Ты алертил по причине, которая постоянно срабатывает без влияния на пользователя, и так приучил команду игнорировать пейджер — а потом он пропустил симптом, который реально бил.
SLI, SLO и бюджет ошибок, который ты тратишь
Надёжность — это не ощущение; это число, и у числа три слоя. SLI (Service Level Indicator) — это измеренное отношение хороших событий ко всем валидным: доля запросов, обслуженных корректно. Два канонических SLI: доступность = ответы не-5xx / все ответы и латентность = запросы быстрее 300мс / все запросы. Всегда отношение в [0, 1], потому что сырой счёт ничего не значит без знаменателя. SLO (Service Level Objective) — это цель для этого отношения за окно — скажем, 99.9% доступности за скользящие 28 дней. Бюджет ошибок — это то, что осталось: 1 − SLO. При 99.9% бюджет равен 0.1%, что за 30 дней даёт ~43 минуты разрешённого отказа (при 99.95% это ~22 мин; при 99% — просторные ~7.2 часа). Этот крошечный остаток и есть вся суть — он превращает надёжность в величину, которую ты тратишь. Жги его медленно весь месяц — и всё в порядке; сожги половину одним плохим деплоем — и ты замораживаешь рискованные изменения, пока он не восстановится. Бюджет — это контракт между «деплоить быстро» и «оставаться на ногах»: пока ты в бюджете — деплой; как только превысил — работа над надёжностью прыгает в начало очереди.
SLI вычисляется прямо из metric math в CloudWatch — good / total — и алармить надо по результату, а не по прокси.
# SLI доступности как metric math: good (не-5xx) / valid (все) запросы.
# m1 = общий счёт запросов, m2 = счёт 5xx; expr = (m1 - m2) / m1.
aws cloudwatch put-metric-alarm \
--alarm-name checkout-availability-slo \
--comparison-operator LessThanThreshold \
--threshold 0.999 \
--evaluation-periods 5 --datapoints-to-alarm 5 \
--treat-missing-data breaching \
--metrics '[
{ "Id": "total", "MetricStat": { "Metric": { "Namespace": "MyApp/Checkout", "MetricName": "Requests" }, "Period": 300, "Stat": "Sum" }, "ReturnData": false },
{ "Id": "errors", "MetricStat": { "Metric": { "Namespace": "MyApp/Checkout", "MetricName": "HTTP5xx" }, "Period": 300, "Stat": "Sum" }, "ReturnData": false },
{ "Id": "sli", "Expression": "(total - errors) / total", "Label": "availability", "ReturnData": true }
]' \
--alarm-actions <sns-topic-arn>Компромисс: SLO — это осознанное обещание, а не 100%. Гнаться за 100% бесконечно дорого и бессмысленно — твои пользователи уже теряют запросы на собственном капризном Wi-Fi задолго до того, как узким местом станет твой сервис. Каждая девятка примерно умножает стоимость; выбирай наименьшую цель, при которой пользователи довольны, потому что каждая ненужная девятка — это деньги и скорость, которые ты сохраняешь. Режим отказа: SLO без знаменателя (аларм по сырому счёту ошибок) ломается в худший момент. Пятьдесят ошибок — катастрофа при 100 req/s и погрешность округления при 100k req/s — аларм по счёту пейджит на всплеске трафика и молчит во время малотрафичной аварии. Отношения масштабируются с нагрузкой; счётчики — нет.
RED, USE и четыре золотых сигнала
Всё измерить нельзя, поэтому измеряй те немногие сигналы, что отображаются в боль пользователя. Два фреймворка делят пространство. RED — для request-driven сервисов: Rate (запросов/сек), Errors (упавших запросов/сек), Duration (распределение латентности, смотришь по p99, а не по среднему). RED отвечает на «обслуживаются ли пользователи, быстро и корректно?» USE — для ресурсов: Utilization (насколько занят: CPU %, mem %), Saturation (сколько в очереди/перегрузе: глубина run-queue, ожидание пула соединений), Errors (ошибки устройства/драйвера). USE отвечает на «не вот-вот ли опрокинется этот CPU, диск или пул соединений?» Гугловские четыре золотых сигнала — латентность, трафик, ошибки, насыщение — это та же идея в дистилляте: первые три — это RED, насыщение — несущая половина USE.
# RED для сервиса: p99-латентность (Duration) — релевантная для SLO статистика.
# Алармь по перцентилю, никогда по Average, которое прячет медленный хвост.
aws cloudwatch put-metric-alarm \
--alarm-name checkout-p99-latency \
--namespace MyApp/Checkout --metric-name LatencyMs \
--extended-statistic p99 --period 60 \
--threshold 500 --comparison-operator GreaterThanThreshold \
--evaluation-periods 3 --datapoints-to-alarm 3 \
--treat-missing-data breaching \
--alarm-actions <sns-topic-arn>Компромисс: RED и USE дополняют друг друга, а не взаимозаменяемы. RED говорит, что пользователям больно; USE часто говорит, почему (растёт насыщение → растёт латентность). Дисциплина в том, чтобы пейджить по RED (симптом, обращённый к пользователю) и держать USE на дашбордах для диагностики — навесить пейдж на каждую USE-метрику — это ровно способ заново собрать шумный CPU-аларм из Hook. Режим отказа: измерять всё — это шум. Команда, рисующая триста метрик и пейджащая по трети из них, не имеет сигнала — когда важно всё, не важно ничего, и единственная реальная авария тонет в той же струе, что и каждый рутинный всплеск.
Симптом против причины: алертируй по тому, что чувствует пользователь
Это сердцевина, и именно здесь большинство систем алертинга ошибается. Пейджи по симптомам — тому, что пользователь реально испытывает: error rate вырос, p99-латентность выросла, burn rate SLO высок. Не пейджи по причинам — CPU 90%, один хост упал, диск наполняется, рестарт пода. Причины срабатывают постоянно без всякого влияния на пользователя: CPU достигает 90% на каждом деплое, а сервис при этом совершенно исправен, так что CPU-пейдж — гарантированная машина ложных алармов. А у ложных алармов есть накапливающаяся цена — усталость от алертов. Когда пейджит каждый деплой, дежурный усваивает, что пейджер не значит ничего, мьютит канал и пропускает единственную реальную аварию (ровно как в Hook). Цена шумного алерта — не прерывание; она в том, что он уничтожает доверие к каждому будущему алерту.
Senior-техника — это multi-window, multi-burn-rate алертинг. Burn rate — это насколько быстро ты тратишь бюджет ошибок относительно SLO; burn rate 1 тратит весь месячный бюджет ровно за месяц, так что всё выше 1 — слишком быстро. Ставишь два уровня: fast-burn алерт (например, burn 14.4× = 2% 30-дневного бюджета сожжено за 1 час, подтверждённый на коротком окне) → пейдж сейчас, потому что при таком темпе весь месячный бюджет уйдёт за ~2 дня. И slow-burn алерт (например, ~3× за 6 часов, ~10% бюджета) → заведи тикет, без подъёма в 3 ночи, потому что у тебя есть дни на реакцию. Часть с двумя окнами подавляет всплески: быстрый алерт требует, чтобы прожиг держался и на длинноватом окне (ловит реальное), и на коротком (подтверждает, что это всё ещё происходит прямо сейчас), так что единичный всплеск на 90 секунд не пейджит. Числа, которые важны: SLO 99.9% = бюджет ~43 мин/мес; пейдж по burn rate 14.4× = действуй сейчас; опросы стабильно дают долю ложных срабатываний дежурного около 50%+ при алертинге по причинам — половина всех пейджей будит кого-то впустую.
▸Почему это работает
Зачем два окна и две скорости прожига вместо одного простого порога? Единственный быстрый порог («трата бюджета > 2% за час, пейдж») ловит подлинные быстрые прожиги, но также срабатывает на минутном всплеске, который сам заживает, — шум. Единственный медленный порог («трата бюджета > 10% за 6 часов») тих на шуме, но реагирует слишком медленно на жёсткую аварию, прожигающую весь бюджет за два часа. Тебе нужны оба свойства, поэтому ты запускаешь оба алерта. Fast-burn пейдж (высокая скорость, например 14.4×) требует, чтобы прожиг был истинен на длинном окне и всё ещё истинен на коротком замыкающем окне — короткое окно быстро сбрасывает аларм, когда инцидент закончился, так что тебя не пейджит по уже завершившейся аварии. Slow-burn алерт (меньшая скорость, длиннее окно) маршрутизируется в тикет, а не в пейдж, ловя тихую эрозию, которая иначе незаметно съела бы бюджет за выходные. Быстрые прожиги пейджат; медленные — в тикет; всплески, что не держатся, не делают ни того, ни другого. Именно этот дизайн отличает систему алертинга, которой ты доверяешь, от той, которую ты мьютишь.
Проводка на AWS без воссоздания шума
CloudWatch даёт примитивы, и три из них держат пейджер честным. Композитные алармы комбинируют дочерние алармы булевым правилом — ALARM(p99High) AND ALARM(errorRateHigh) или ALARM(slo) AND NOT ALARM(deployInProgress) — так что одна первопричина, подтверждённая двумя симптомами, пейджит один раз вместо четырёх связанных алармов, а известный деплой подавляет пейдж целиком. Аларм на отсутствие данных — это страж живучести: мёртвый сервис не эмитит ничего, так что аларм с --treat-missing-data notBreaching молча никогда не сработает, когда то, что он сторожит, опрокинется — ровно наоборот. Для heartbeat/liveness-аларма ставишь --treat-missing-data breaching, чтобы тишина тебя пейджила. Полосы anomaly detection обучают ожидаемый диапазон по истории и алармят на отклонениях, что бьёт плоский порог для трафика с дневной или недельной сезонностью. Действия маршрутизируются через SNS → PagerDuty / Slack, и — без вариантов — ссылка на runbook идёт в описание аларма, чтобы пейдж, будящий кого-то в 3 ночи, говорил ему, что делать.
// Композитный аларм: пейдж только когда реальный обращённый к пользователю симптом
// подтверждён И мы не в середине деплоя. Один пейдж на первопричину вместо четырёх.
{
"AlarmName": "checkout-user-pain-page",
"AlarmRule": "(ALARM(\"checkout-p99-latency\") OR ALARM(\"checkout-error-rate\")) AND NOT ALARM(\"deploy-in-progress\")",
"AlarmActions": ["arn:aws:sns:us-east-1:123456789012:pagerduty-critical"],
"AlarmDescription": "Нарушение SLO на чекауте, обращённое к пользователю. Runbook: https://runbooks.internal/checkout-slo"
}Режим отказа (два способа победить самого себя): аларм с treat-missing-data = notBreaching на критичном сервисе — это молчаливое слепое пятно: в день, когда сервис умрёт и не эмитит метрик, аларм навечно зависнет в INSUFFICIENT_DATA и никогда не запейджит. А некомпозитный CPU-аларм на каждый хост — это машина шума из Hook: он пейджит на каждый деплой, приучает людей его игнорировать, и доверие, которое он сжигает, утягивает за собой настоящие алерты. Решение о маршрутизации «пейдж или тикет» — это вся игра: быстрый прожиг и подтверждённые симптомы → пейджи человека; медленный прожиг и одиночные причины → тикет, который прочитают в понедельник.
У чекаута SLO доступности 99.9% (бюджет ~43 мин/мес). Дежурный устал: аларм на каждый хост CPU>80% пейджит десяток раз в день на деплоях, а на прошлой неделе реальная авария провайдера была пропущена, потому что канал замьютили. Выбери, по чему ПЕЙДЖИТЬ.
Liveness-аларм сторожит heartbeat-метрику, которую здоровый сервис эмитит каждую минуту. Он настроен с --treat-missing-data notBreaching. Сервис падает и полностью перестаёт эмитить. Что происходит?
Твоя команда пейджит по аларму на каждый хост 'CPU > 80%'. Он срабатывает десяток раз в день — почти всегда во время деплоев и batch-джобов, и почти всегда снова зелёный прежде, чем кто-то отреагирует. В чём корневая проблема?
- 01Дай определение SLI, SLO и бюджета ошибок и объясни, как бюджет управляет деплоями.
- 02Почему пейджить по симптомам, а не по причинам, и что такое multi-window multi-burn-rate алертинг?
Надёжность измеряется в трёх слоях. SLI — это отношение хороших событий ко всем валидным: доступность как не-5xx ко всем ответам, латентность как запросы быстрее 300мс ко всем запросам — вычисляется прямо из metric math CloudWatch как good/total, никогда как сырой счёт, ломающийся при сдвиге трафика. SLO — это цель для этого отношения за окно (99.9% за 28 дней), а бюджет ошибок — это 1 − SLO: при 99.9% около 43 минут отказа в месяц, величина, которую ты тратишь осознанно — деплой свободно, пока в бюджете, замораживай рискованные изменения, как только его сжёг. Два фреймворка выбирают те немногие сигналы, что стоит сторожить: RED (Rate, Errors, Duration по p99) для request-driven сервисов, USE (Utilization, Saturation, Errors) для ресурсов, дистиллированные Гуглом в четыре золотых сигнала — латентность, трафик, ошибки и насыщение. Дисциплина, предотвращающая катастрофу, — это симптом против причины: пейджи по тому, что чувствуют пользователи — error rate, p99, burn rate SLO — и никогда по причинам вроде CPU 90% или упавшего одного хоста, потому что причины срабатывают постоянно без влияния на пользователя и приучают дежурного замьютить канал, после чего единственная реальная авария пропускается. Multi-window, multi-burn-rate алертинг кодирует это: быстрый прожиг (14.4×, ~2% бюджета за час, подтверждённый на двух окнах) пейджит человека сейчас, а медленный прожиг (~3× за часы) заводит тикет. На AWS ты проводишь это композитными алармами, комбинирующими дочерние алармы булевыми правилами для требования подтверждения и подавления связанного шума (и для мьюта во время известного деплоя), с treat-missing-data, выставленным в breaching на любом liveness-аларме, чтобы мёртвый, молчащий сервис пейджил вместо вечного зависания в INSUFFICIENT_DATA, с полосами anomaly detection для сезонного трафика и с действиями, маршрутизированными через SNS в PagerDuty или Slack — всегда со ссылкой на runbook в описании аларма. Маршрутизация «пейдж или тикет» — это всё ремесло: быстрый прожиг и подтверждённые симптомы будят человека, медленный прожиг и одиночные причины ждут понедельника. Теперь, когда видишь шумный аларм — срабатывающий дюжину раз прежде, чем кто-то реагирует, — твой первый вопрос: пейджит ли он по причине или по симптому; а когда кто-то спросит «какова наша надёжность?», у тебя будет число: коэффициент SLI, цель SLO и сколько минут бюджета осталось в этом месяце.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.