fix(metrics): дисковые тревоги в абсолютных ГБ + алерт на скорость утечки
All checks were successful
CI Trade-In / changes (pull_request) Successful in 16s
CI / changes (pull_request) Successful in 19s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m34s
CI / backend-tests (pull_request) Successful in 6m40s

Инцидент 16.09: кэш архивов Forgejo ел 2 ГБ/ч, а проценты на диске 145 ГБ
(host=infra) давали слишком короткое окно реакции (85% = 21 ГБ запаса,
93% = 10 ГБ). На диске host=apps (~910 ГБ) те же проценты значат совсем
другой запас в ГБ — переведено на абсолютные пороги (30/15 ГБ свободного
места), одинаково осмысленные независимо от размера диска. Сужено до
mountpoint="/" — на /boot и /boot/efi (меньше 1 ГБ целиком) абсолютный
порог горел бы постоянно.

Добавлен DiskSpaceDepletingFast: deriv() по 15-минутному окну, >1 ГБ/ч
устойчиво 30 минут — ловит именно быструю течь независимо от текущего
уровня заполнения, пока DiskSpaceLow/Critical ещё молчат (места много,
но утечка есть). DiskWillFillIn24h остаётся отдельным сигналом для более
медленных, но устойчивых трендов (предупреждает позже за счёт 6h окна
сглаживания шума) — роли не дублируют друг друга, разделение описано
комментариями в файле.

Проверено promtool check rules (24 rules found) и живыми запросами к
Prometheus на Beget — ни одно из трёх правил сейчас не горит.
This commit is contained in:
bot-backend 2026-09-16 22:23:58 +03:00
parent 1c039b358c
commit a8d3499bdc

View file

@ -63,40 +63,85 @@ groups:
- name: host
interval: 60s
rules:
# Диск. На Beget уже был случай, когда занято 79 % и никто не смотрел;
# порог 85 % даёт запас на реакцию, а не сообщает о свершившемся факте.
# Диск. Инцидент 16.09: кэш архивов Forgejo ел 2 ГБ/ч на диске 145 ГБ
# (host=infra). Проценты на дисках разного размера значат разное —
# host=apps держит /-раздел ~910 ГБ, те же 85 % там это ещё ~135 ГБ
# запаса, а на infra (145 ГБ) — почти ничего. Порог переведён в
# АБСОЛЮТНЫЕ ГБ свободного места: он одинаково осмыслен на любом диске,
# потому что напрямую отвечает на вопрос «сколько времени есть до нуля
# при текущей скорости утечки», а не «какая доля занята».
#
# `mountpoint="/"` — намеренное сужение с прежнего «все fstype кроме
# tmpfs/overlay»: /boot и /boot/efi по обоим хостам меньше 1 ГБ
# целиком, с порогом 30/15 ГБ они бы горели ПОСТОЯННО (проверено живыми
# метриками 16.09 — infra:/boot/efi 98 МБ, apps:/boot 553 МБ). Данные
# приложения и Postgres лежат на "/", туда и целится алерт.
- alert: DiskSpaceLow
expr: |
(1 - node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"}
/ node_filesystem_size_bytes{fstype!~"tmpfs|overlay"}) > 0.85
node_filesystem_avail_bytes{fstype!~"tmpfs|overlay", mountpoint="/"}
/ 1073741824 < 30
for: 15m
labels:
severity: warning
annotations:
summary: "Диск занят больше 85 %"
description: "{{ $labels.host }} {{ $labels.mountpoint }}: занято {{ $value | humanizePercentage }}."
summary: "Свободно на диске меньше 30 ГБ"
description: "{{ $labels.host }} {{ $labels.mountpoint }}: свободно {{ printf \"%.1f\" $value }} ГБ."
- alert: DiskSpaceCritical
expr: |
(1 - node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"}
/ node_filesystem_size_bytes{fstype!~"tmpfs|overlay"}) > 0.93
node_filesystem_avail_bytes{fstype!~"tmpfs|overlay", mountpoint="/"}
/ 1073741824 < 15
for: 5m
labels:
severity: critical
annotations:
summary: "Диск почти кончился"
description: "{{ $labels.host }} {{ $labels.mountpoint }}: занято {{ $value | humanizePercentage }}. Postgres при заполнении диска останавливается."
summary: "Свободно на диске меньше 15 ГБ"
description: "{{ $labels.host }} {{ $labels.mountpoint }}: свободно {{ printf \"%.1f\" $value }} ГБ. Postgres при заполнении диска останавливается."
# Прогноз важнее порога: он ловит утечку до того, как она упрётся в стену.
# Скорость, а не уровень. Ловит именно ту утечку, что была 16.09: пока
# DiskSpaceLow/Critical ещё не сработали (места вагон), но оно тает
# быстрее ~1 ГБ/ч устойчиво — это уже течь, а не органический рост.
# `deriv()` — линейная регрессия по 15-минутному окну (не мгновенная
# разница двух точек, ту дёргает шум). Знак минус спереди и деление
# переносят результат из "Б/с, отрицательное при убыли" в "ГБ/ч,
# положительное когда тает" — чтобы $value в тексте читался нормально
# (не "-2.1 ГБ/ч"). `for: 30m` поверх 15-минутного окна регрессии
# требует НЕПРЕРЫВНОЙ убыли около часа, чтобы не будить на разовый
# скачок (бэкап, ротация логов).
- alert: DiskSpaceDepletingFast
expr: |
-deriv(node_filesystem_avail_bytes{fstype!~"tmpfs|overlay", mountpoint="/"}[15m])
* 3600 / 1073741824 > 1
for: 30m
labels:
severity: warning
annotations:
summary: "Диск пустеет быстрее ГБ в час"
description: "{{ $labels.host }} {{ $labels.mountpoint }}: свободное место убывает на {{ printf \"%.1f\" $value }} ГБ/час устойчиво последние ~30 минут — независимо от того, сколько места осталось сейчас."
# Прогноз важнее порога: он ловит утечку до того, как она упрётся в
# стену — но роль другая, чем у DiskSpaceDepletingFast выше. Тот ловит
# БЫСТРУЮ (>1 ГБ/ч) утечку рано, за счёт короткого 15-минутного окна.
# Этот ловит УМЕРЕННУЮ утечку (может быть медленнее 1 ГБ/ч), которую
# короткое окно не поймает, но которая всё равно приведёт к нулю в
# течение суток при текущем уровне занятости — окно 6h усредняет шум
# ценой более позднего срабатывания. На одной и той же быстрой утечке
# оба правила могут сработать (сначала это, следом то) — это
# ЗАДУМАННАЯ эскалация двумя разными сигналами (скорость сейчас →
# подтверждённый тренд на сутки), а не дублирующее письмо: тексты и
# время срабатывания разные. DiskSpaceLow/Critical выше добавляют
# третий, независимый от скорости сигнал — «места мало» само по себе,
# даже если утечки нет и ничего не тает быстро.
- alert: DiskWillFillIn24h
expr: |
predict_linear(node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"}[6h], 24*3600) < 0
(0 - predict_linear(node_filesystem_avail_bytes{fstype!~"tmpfs|overlay", mountpoint="/"}[6h], 24*3600))
/ 1073741824 > 0
for: 30m
labels:
severity: warning
annotations:
summary: "По текущему темпу диск кончится за сутки"
description: "{{ $labels.host }} {{ $labels.mountpoint }}: экстраполяция по последним 6 часам."
description: "{{ $labels.host }} {{ $labels.mountpoint }}: по экстраполяции последних 6 часов через сутки не хватит ~{{ printf \"%.1f\" $value }} ГБ."
- alert: MemoryPressure
expr: |