srekit-runbook
Скаффолдит runbook через srekit runbook и помогает наполнить его секции прескриптивно (что делать в следующий раз), а не нарративно (что мы делали в этот раз).
Когда использовать
- Пользователь просит написать/оформить/сделать runbook (любая формулировка).
- Пользователь только что починил что-то на проде и хочет зафиксировать процедуру для on-call.
- Пользователь упоминает алерт, под который сейчас runbook'а нет, и хочет завести.
- Известный failure mode, который надо документировать проактивно — до того, как он повторится в 3 ночи.
Не использовать:
- Постмортем разбираемого инцидента → навык
srekit-postmortem. - Investigation log про незакрытое расследование → команда
taskв общем навыкеsrekit. - RFC про архитектурное решение → команда
rfcв общем навыкеsrekit.
Главный принцип: прескриптивно, не нарративно
Это самая частая ошибка при генерации runbook'ов из контекста сессии. Если пользователь рассказывает «мы зашли на ноду w1, увидели stale DRBD primary, сделали drbdadm secondary, и створки разлетелись», есть соблазн транскрибировать это как есть. Не надо. Это incident-лог, а не runbook. Runbook должен переписать то же знание в форме инструкции для будущего читателя.
| ❌ Нарратив (что было) | ✅ Прескрипция (что делать) |
|---|---|
| «Мы зашли на w1 и увидели stale primary» | «На каждой ноде проверь drbdadm status | grep -E 'Primary|StandAlone' — лишний Primary при отсутствии VM = stale» |
| «Алиса откатила деплой к v1.4.2» | «Откати деплой к последнему good-known тегу: kubectl rollout undo deploy/<svc>» |
| «После рестарта csi-controller всё разъехалось» | «Перезапусти csi-controller: kubectl rollout restart deploy/csi-controller -n storage. Verify: pod в Running, в логах нет connection refused в течение 60 секунд» |
| «Это случилось 3 июня в 14:25» | (вырезать; timestamps не место в runbook'е) |
Конкретные временные отметки и личные имена инцидента в runbook не попадают — это не транскрипт. Если хочешь сохранить «вот пример, как это случилось в прошлый раз» — это ссылка на постмортем под References, не пересказ.
Что генерирует srekit runbook
CLI-команда: srekit runbook (требует -T/--title, опционально --service, --alert). По умолчанию пишет в runbook-<slug>.md в CWD.
Шаблон (v1 YAML-артефакт, секции по порядку):
| ID секции | Заголовок | Назначение |
|---|---|---|
symptoms |
Симптомы (Symptoms) | Что видит мониторинг / пользователь — конкретные сигналы |
severity_slo_impact |
Тяжесть и влияние на SLO (Severity & SLO impact) | Что горит, насколько срочно, будить пейджером или нет |
diagnose |
Диагностика (Diagnose) | Упорядоченный список проверок: команда / запрос → ожидаемый и аномальный вывод |
mitigate |
Смягчение (Mitigate) | Упорядоченные идемпотентные шаги. Сначала остановить «кровотечение», потом разбираться |
verify |
Проверка (Verify) | Конкретные критерии «починили»: алерт погас, метрика вернулась, нет повторов N минут |
after_the_fact |
Постфактум (After the fact) | Триггер для постмортема, напоминание обновить сам runbook тем, что удивило |
references |
Ссылки (References) | Дашборды, код, родственные runbook'и, прошлые постмортемы |
Frontmatter: service, alert, tags: [runbook, oncall].
Рабочий процесс
1. Убедись, что srekit доступен
srekit --version
Нет — попроси установить (brew install jtprogru/tap/srekit или go install github.com/jtprogru/srekit@latest), останавливайся.
2. Определи источник материала
Два потока:
A. Реактивный — из реального инцидента, который пользователь только что разрулил. Источники наполнения:
- Реплики пользователя в этой сессии: что увидел, что попробовал, что сработало.
- Если в репо лежит постмортем по этому же инциденту — прочитай (тебе нужны Mitigate-шаги и финальный root cause, не Timeline). Под References добавь ссылку.
- Логи / команды из обсуждения. Конкретные значения параметров.
B. Проактивный — известный failure mode, который пользователь хочет задокументировать до следующего раза. Источники:
- Реплики пользователя: «когда срабатывает X, надо проверить Y и сделать Z».
- Если есть похожий runbook в репо — посмотри его структуру (а не копируй).
- Если есть алерт-определение в
prometheus/alerts.yamlили подобном — прочитай rule, чтобы понять что именно мониторится.
Спроси прямо одной репликой: «runbook по реальному инциденту, который только разрулили, или проактивно фиксируем известную поломку?». Это меняет, что ты у него потом будешь спрашивать.
3. Собери метаданные
| Поле | Откуда брать | Зачем |
|---|---|---|
| title | формулировка пользователя | Должен описывать failure mode, не симптом. «OOMKill of csi-controller after PVC create spike», не «CSI errors». Если разговор на русском — заголовок на русском. |
--service |
сервис, на который заводим | Попадает в frontmatter; помогает аукшен-фильтровать runbook'и в IDE / поиск |
--alert |
имя Prometheus / Alertmanager алерта | Критично для discoverability. Если runbook не назван так же, как алерт его не найдут в 3 ночи. Если алерта ещё нет — спроси, как он будет называться, или оставь GAP-маркер |
--alert — это самое часто пропускаемое поле. Если в репо есть prometheus/, monitoring/, alerts/ — попробуй найти подходящее правило по имени, и предложи его пользователю на подтверждение.
4. Определи путь к файлу
Правило по приоритету:
runbooks/<slug>.md— если есть каталогrunbooks/в корне репо.docs/runbooks/<slug>.md— если естьdocs/runbooks/.runbooks/<service>/<slug>.md— если есть подкаталоги по сервисам внутриrunbooks/. Распознай существующую конвенцию поls runbooks/.<slug>.mdв CWD — последний fallback.
<slug> — короткий kebab-case описание failure mode: csi-controller-oom, drbd-split-brain-recovery, kubelet-inotify-exhaustion. Не транскрипт инцидента — общая поломка.
Передавай --out явно. Срекит-дефолт — runbook-<slug>.md от title. Это часто не то, что надо: в каталоге runbooks/ префикс runbook- лишний, и slug от title может быть слишком длинным. Собери --out сам.
Если файл уже существует — не делай --force, не спросив. Возможно, это другой runbook на тот же сервис или начатый черновик.
5. Скаффолд через srekit
srekit runbook \
-T "<title>" \
--service "<service>" \
--alert "<AlertName>" \
--out "<путь из шага 4>" \
[--templates-dir "<DIR с runbook.yaml>"] # только если нужен проектный шаблон
--templates-dir обычно не нужен: если в ~/.config/srekit/config.yaml указан templates_dir, srekit сам подтянет. Передавай явно, если проектный runbook.yaml лежит вне сконфигурированной директории.
Один вызов сразу финальный, без --dry-run-промежутка.
6. Наполни секции
После скаффолда — Read файла, потом Edit по секциям. Подробное руководство — references/section-guide.md. Краткая логика:
- Symptoms — что видит мониторинг (имя алерта, panel дашборда, паттерн в логах). Не «пользователь жалуется на медленный ответ» — это симптом для incident-доса, для runbook нужны observable сигналы.
- Severity & SLO impact — какой SLO горит, как быстро. Будить пейджером или дотерпит до утра. Зависимости: что ещё ломается, если этот сервис лежит.
- Diagnose — упорядоченный список проверок, от самой быстрой и информативной к более глубоким. Каждая проверка — команда или query, которую можно вставить, с ожидаемым и аномальным выводом. Без копи-пастабельной команды diagnose-секция бесполезна.
- Mitigate — упорядоченные идемпотентные шаги. Первый шаг — остановить кровотечение (rollback / drain / failover), а уже потом root cause. Помечай рискованные шаги (
> ⚠️ Перед выполнением убедись, что …). Каждый шаг — команда + что verify после. - Verify — конкретные success criteria. Чек-лист с
[ ]. «Алерт погас», «p99 вернулся к baseline», «нет повторов N минут» — конкретные метрики и пороги, не «всё ок». - After the fact — когда заводим постмортем (
SEV ≥ N), напоминание обновить runbook тем, что в этот раз удивило, прокидывание action items. - References — дашборды, код, родственные runbook'и, прошлые постмортемы по этому же failure mode.
7. Дисциплина
- Императив настоящего времени. «Перезапусти X», «проверь Y». Не «мы перезапускаем», не «нужно перезапустить».
- Идемпотентность Mitigate. Каждый шаг должен быть безопасно выполнимым повторно. Если шаг не идемпотентен — явно пометь (
> ⚠️ Выполнять ровно один раз). - Без timestamps и личных имён. Конкретное время инцидента и кто что делал — в постмортем, не в runbook. В runbook остаются только команды, выводы, пороги.
- Команды копи-пастабельные.
kubectl get pods -n storage— да. «Посмотри статус подов» — нет. Заполни плейсхолдеры реальными namespace'ами / сервисами, если они известны; если не известны —<placeholder>с пояснением. - Риск явно маркирован. Любой шаг, который может усугубить или сломать другое —
> ⚠️с указанием, что именно может сломаться. - GAP-маркеры вместо галлюцинаций. Не знаешь точную команду / порог / namespace —
<!-- GAP: уточнить … -->, не выдумывай. Особенно опасно в Mitigate: ложный шаг приведёт к усугублению. - Markdown без хардврапа. Один абзац = одна длинная строка. Списки, таблицы, code fences — структурные переносы, как есть.
- Язык — как у пользователя в диалоге. Билингвальный шаблон у срекита уже двуязычный в заголовках; основной текст пиши на языке разговора.
8. Прокинь discoverability (предложение, не действие)
Runbook бесполезен, если на него не наводит сам алерт. После скаффолда напомни пользователю:
- Добавить ссылку в Prometheus alert annotation:
runbook_url: <ссылка на этот файл>. - Если есть internal wiki / dashboard для on-call — добавить туда индексную запись.
- Если runbook покрывает алерт, для которого уже есть
runbook_url(на старый wiki / Confluence) — обновить annotation.
Это предложение — пользователь сам решит, делать ли сейчас. Не патчи alerts.yaml без его явного запроса.
9. Сообщи пользователю
- Путь к созданному файлу.
- Оставшиеся GAP-маркеры списком (что надо досверить — namespace'ы, пороги, ссылки на дашборды).
- Напоминание прокинуть
runbook_urlв alert annotation.
Не предлагай коммит — пользователь решит сам.
Антипаттерны
- Транскрибировать инцидент. «Мы сделали X, потом Y» → переформулируй в «сделай X. Если не помогло, сделай Y». Если не получается переформулировать без потери смысла — значит, runbook не место для этого знания, его место — постмортем.
- Diagnose без команд. «Проверь логи», «посмотри дашборд» — useless. Каждая проверка — конкретный URL дашборда или команда, которую можно скопировать.
- Mitigate без verify-after. Каждый шаг должен говорить, как понять, что он сработал.
- Mitigate-шаги, которые усугубляют, если выполнить не вовремя. Например, «перезапусти Y» без проверки, что Y не Primary — может уронить кластер. Помечай
> ⚠️. - Symptoms = «пользователь жалуется на медленный ответ» — это про incident-доска. В runbook'е Symptoms — это алертовое событие: какой алерт сработал, какая метрика пересекла порог.
- Severity = «high» без пояснения. Severity в runbook'е — это функция от SLO impact, не фиксированная константа. «SEV-1 если в течение часа SLI < 99%, SEV-2 если выше».
- References с битыми ссылками. Не вставляй ссылки, которые не верифицировал в репо / wiki. GAP-маркер лучше.
- Не используй
git commit/git pushв рамках работы скилла. Пользователь сам решает.
Пример
Диалог: пользователь рассказывает, что нашёл stale DRBD primary на ноде w1 (drbdadm status показал Primary без VM), сделал drbdadm secondary <res>, и стейт разъехался. Хочет runbook на алерт DRBDStalePrimary для сервиса linstor.
Команда:
srekit runbook \
-T "DRBD stale primary — demote without running VM" \
--service linstor \
--alert DRBDStalePrimary \
--out runbooks/linstor/drbd-stale-primary-demote.md
Дальше — Edit секций. Mitigate (фрагмент):
## Смягчение (Mitigate)
> ⚠️ Перед каждым шагом убедись, что на ноде **нет работающей VM**, использующей ресурс. `virsh list --all` на ноде должен показать ресурс free.
1. **Определи stale Primary:**
```bash
for node in w1 w2 w3; do
ssh $node 'drbdadm status | grep -E "Primary|StandAlone"'
done
Stale = Primary на ноде, где virsh list не показывает VM с этим ресурсом.
Demote stale Primary (идемпотентно — повторное выполнение безвредно):
ssh <stale-node> 'drbdadm secondary <resource>'Verify (см. секцию Verify): в
drbdadm statusостался ровно один Primary — на ноде с активной VM.
References (фрагмент):
```markdown
## Ссылки (References)
- Алерт: [`prometheus/alerts/drbd.yaml`](../../prometheus/alerts/drbd.yaml) — `DRBDStalePrimary`
- Дашборд: <!-- GAP: уточнить URL Grafana drbd-overview -->
- Прошлый постмортем (этот же failure mode): [`incidents/postmortem-2026-06-03-disk-50gb-create-hang.md`](../../incidents/postmortem-2026-06-03-disk-50gb-create-hang.md)
- Родственный runbook: [`runbooks/linstor/split-brain-resolution.md`](split-brain-resolution.md)
После скаффолда — напомнить пользователю прокинуть runbook_url: <ссылка> в Prometheus annotation для DRBDStalePrimary.