Merge pull request 'Тревоги уровня приложения, cAdvisor и пропавшие фоновые контейнеры; critical переживает подавление' (#3477) from feat/3471-app-alerts-coverage into main
This commit is contained in:
commit
5d4d17a5e1
3 changed files with 109 additions and 5 deletions
|
|
@ -71,9 +71,16 @@ route:
|
|||
repeat_interval: 3h
|
||||
|
||||
inhibit_rules:
|
||||
# Если хост целиком недоступен, не сыпать отдельно про каждый его сервис.
|
||||
# Если хост целиком недоступен, не сыпать отдельно про каждый его
|
||||
# warning-сервис. `target_matchers` НАМЕРЕННО ограничен одним warning
|
||||
# (#3471): раньше сюда попадал и critical, и падение node-exporter молча
|
||||
# гасило заодно PostgresLongTransactionCritical и все critical cAdvisor-
|
||||
# алерты того же хоста — самое важное сообщение исчезало вместе с шумом,
|
||||
# который оно должно было подавить. Warning того же хоста подавлять по-
|
||||
# прежнему стоит (диск/память/своп неотличимы от «нет данных»), а critical
|
||||
# обязан пережить это подавление и дойти до дежурного отдельно.
|
||||
- source_matchers: [alertname = "HostAgentDown"]
|
||||
target_matchers: [severity =~ "warning|critical"]
|
||||
target_matchers: [severity = "warning"]
|
||||
equal: ["host"]
|
||||
|
||||
receivers:
|
||||
|
|
|
|||
|
|
@ -57,9 +57,13 @@ prometheus.scrape "cadvisor" {
|
|||
prometheus.relabel "cadvisor_trim" {
|
||||
forward_to = [prometheus.remote_write.central.receiver]
|
||||
|
||||
// `up`/`scrape_samples_scraped` — служебные ряды самого скрейпа, не
|
||||
// container_*-метрики. Без явного допуска этот keep-фильтр резал их вместе
|
||||
// с прочим шумом, и у cAdvisor как job'а не было своей серии `up` вообще —
|
||||
// его смерть выглядела так же, как «ничего не изменилось» (#3471).
|
||||
rule {
|
||||
source_labels = ["__name__"]
|
||||
regex = "container_(memory_(usage_bytes|working_set_bytes|rss)|cpu_(usage_seconds_total|cfs_throttled_seconds_total)|network_(receive|transmit)_bytes_total|fs_(usage|limit)_bytes|last_seen|spec_memory_limit_bytes|start_time_seconds|processes)"
|
||||
regex = "up|scrape_samples_scraped|container_(memory_(usage_bytes|working_set_bytes|rss)|cpu_(usage_seconds_total|cfs_throttled_seconds_total)|network_(receive|transmit)_bytes_total|fs_(usage|limit)_bytes|last_seen|spec_memory_limit_bytes|start_time_seconds|processes)"
|
||||
action = "keep"
|
||||
}
|
||||
|
||||
|
|
@ -70,9 +74,15 @@ prometheus.relabel "cadvisor_trim" {
|
|||
//
|
||||
// NB: на пустые панели это правило НЕ влияло. Причина была в cAdvisor 0.52 на
|
||||
// Docker 29 — до сюда доезжал ровно один ряд, корневой. Лечится версией 0.55.1.
|
||||
//
|
||||
// `up`/`scrape_samples_scraped` лейбла `name` не несут вовсе (они не про
|
||||
// конкретный контейнер, а про сам скрейп) — фильтр по нему вырезал бы и их.
|
||||
// Поэтому здесь смотрим на пару (__name__, name): для служебных рядов
|
||||
// достаточно самого __name__, для контейнерных метрик по-прежнему обязателен
|
||||
// непустой name.
|
||||
rule {
|
||||
source_labels = ["name"]
|
||||
regex = ".+"
|
||||
source_labels = ["__name__", "name"]
|
||||
regex = "up;.*|scrape_samples_scraped;.*|container_[^;]*;.+"
|
||||
action = "keep"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,21 @@ groups:
|
|||
summary: "С продуктового хоста 15 минут не приходят метрики"
|
||||
description: "Либо лёг агент на Poincare, либо оборван канал до metrics.gendsgn.ru, либо приёмник не принимает remote-write."
|
||||
|
||||
# cAdvisor как job не публикует `up` естественным образом — до фикса
|
||||
# keep-фильтра в alloy-infra.alloy/alloy-apps.alloy (#3471) эта серия
|
||||
# вырезалась тем же правилом, что чистит container_* от мусорных
|
||||
# лейблов. Итог — смерть cAdvisor молча гасила ContainerRestartLoop и
|
||||
# ContainerNearMemoryLimit: обе метрики просто переставали поступать, а
|
||||
# выглядело это как «событий не было».
|
||||
- alert: CadvisorDown
|
||||
expr: up{job="cadvisor"} == 0 or absent(up{job="cadvisor"})
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "cAdvisor не отвечает"
|
||||
description: "{{ if $labels.host }}{{ $labels.host }}: {{ end }}job=\"cadvisor\" вернул up=0 либо серия пропала целиком. Без неё контейнерные алерты этого хоста молчат вне зависимости от реального состояния контейнеров."
|
||||
|
||||
# ── Хост ────────────────────────────────────────────────────────────────────
|
||||
- name: host
|
||||
interval: 60s
|
||||
|
|
@ -145,6 +160,78 @@ groups:
|
|||
summary: "Контейнер у своего потолка памяти"
|
||||
description: "{{ $labels.host }} / {{ $labels.name }}: {{ $value | humanizePercentage }} от mem_limit. Дальше OOM-kill."
|
||||
|
||||
# tradein-tgbot и tradein-scraper не HTTP-сервисы — у них нет `up{}`
|
||||
# вообще, поэтому крэш-без-рестарта или удаление контейнера иначе не
|
||||
# поймать. `absent()` на каждое имя отдельно (не одним regex-селектором):
|
||||
# regex-селектор с несколькими сериями считается «пустым» только когда
|
||||
# ПРОПАЛИ ОБЕ — если жив хотя бы один из двух контейнеров, absent() по
|
||||
# общему selector'у молчит и не заметит пропажу второго.
|
||||
#
|
||||
# ВАЖНО, чего это правило НЕ ловит: container_last_seen обновляется, пока
|
||||
# Docker видит контейнер живым, — зависший, но не упавший процесс
|
||||
# (внутренний цикл встал, контейнер по-прежнему числится running) эту
|
||||
# метрику не тронет. Слепая зона «живой процесс с застрявшим циклом»
|
||||
# остаётся открытой: подходящей метрики для неё сейчас нет.
|
||||
- alert: TradeInBackgroundContainerMissing
|
||||
expr: |
|
||||
absent(container_last_seen{name="tradein-tgbot"})
|
||||
or absent(container_last_seen{name="tradein-scraper"})
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Фоновый контейнер Меры пропал из cAdvisor"
|
||||
description: "{{ $labels.name }}: серия container_last_seen исчезла — контейнер, судя по всему, не работает и не перезапускается."
|
||||
|
||||
# ── Приложение ──────────────────────────────────────────────────────────────
|
||||
# `job="app"` — job из alloy-apps.alloy, лейбл `app` различает продукты
|
||||
# (sitefinder / mera). Метрики отдаёт `MetricsMiddleware`
|
||||
# (`backend/app/observability/metrics.py` у «Птицы»,
|
||||
# `tradein-mvp/backend/app/observability/metrics.py` у «Меры») — счётчик
|
||||
# `http_requests_total{method,route,status}` и гистограмма
|
||||
# `http_request_duration_seconds{method,route}`. До этой группы доля 5xx и
|
||||
# задержка были видны только постфактум в GlitchTip, без порога срабатывания
|
||||
# (#3471).
|
||||
#
|
||||
# severity: critical + host: apps здесь ОБЯЗАТЕЛЬНЫ содержательно, не для
|
||||
# красоты: именно эта пара матчится маршрутом telegram-clients в
|
||||
# alertmanager.yml.tmpl — тот зовёт дежурного и напоминает каждые 30 минут.
|
||||
# `host` выставлен статически: `sum by (app)` вырезает его из результата
|
||||
# запроса, а job="app" в принципе существует только на продуктовом хосте.
|
||||
- name: app
|
||||
interval: 60s
|
||||
rules:
|
||||
# Гейт по RPS внутри знаменателя — тот же приём, что у
|
||||
# PostgresLowHotUpdateRatio: делит только там, где трафик уже есть,
|
||||
# иначе один упавший запрос при нулевой нагрузке даёт 100% и будит
|
||||
# дежурного зря.
|
||||
- alert: AppHighErrorRate
|
||||
expr: |
|
||||
sum by (app) (rate(http_requests_total{job="app", status=~"5.."}[5m]))
|
||||
/ (sum by (app) (rate(http_requests_total{job="app"}[5m])) > 0.1) > 0.05
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
host: apps
|
||||
annotations:
|
||||
summary: "Доля 5xx выше 5%"
|
||||
description: "{{ $labels.app }}: {{ $value | humanizePercentage }} ответов 5xx за последние 5 минут при RPS выше 0.1."
|
||||
|
||||
# Порог 5s — заведомо выше рабочего профиля обоих продуктов (у «Меры»
|
||||
# типичный расчёт 90мс, у «Птицы» верхняя граница гистограммы — 60с под
|
||||
# тяжёлую геометрию, но это единичные хвостовые запросы, не p95).
|
||||
# Калибровка по реальному трафику — отдельная задача, не эта.
|
||||
- alert: AppHighLatencyP95
|
||||
expr: |
|
||||
histogram_quantile(0.95, sum by (le, app) (rate(http_request_duration_seconds_bucket{job="app"}[10m]))) > 5
|
||||
for: 10m
|
||||
labels:
|
||||
severity: critical
|
||||
host: apps
|
||||
annotations:
|
||||
summary: "p95 задержки ответа выше 5 секунд"
|
||||
description: "{{ $labels.app }}: p95 за 10 минут — {{ $value | humanizeDuration }}."
|
||||
|
||||
# ── Postgres ────────────────────────────────────────────────────────────────
|
||||
- name: postgres
|
||||
interval: 60s
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue