From 99125b8093b38f665eb16e7a4e1387197e54efb0 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Thu, 27 Aug 2026 21:10:25 +0300 Subject: [PATCH] =?UTF-8?q?fix(ops/metrics):=20Prometheus=20=D0=BD=D0=B5?= =?UTF-8?q?=20=D0=B2=D0=B8=D0=B4=D0=B5=D0=BB=20=D0=BD=D0=B8=20=D0=BE=D0=B4?= =?UTF-8?q?=D0=BD=D0=BE=D0=B3=D0=BE=20Alertmanager=20=E2=80=94=20=D1=86?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=20file=5Fsd=20=D0=BE=D1=81=D1=82=D0=B0=D0=BB?= =?UTF-8?q?=D0=B0=D1=81=D1=8C=20=D0=BF=D1=83=D1=81=D1=82=D0=BE=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Профиль alerts включили, а файл целей `alertmanager_targets.yml` остался плейсхолдером `[]`. Снаружи всё зелёное: alertmanager и alert-ack Up/healthy, деплой зелёный, в логах ни одной ошибки — при этом `activeAlertmanagers: []` и 1568 уведомлений в `prometheus_notifications_dropped_total`. Горящий с 26.08 `Watchdog` не доехал никуда, как и HostAgentDown с RemoteWriteStalled. Причина в том, что включатель профиля и цель для Prometheus лежали в разных местах: профиль поднимает deploy-metrics.yml по наличию токена и чата, а файл целей правился руками. Разъезд не ловится ничем — `[]` штатен при выключенном профиле, поэтому ни валидация, ни healthcheck, ни лог на него не реагируют. Файл становится производным (`alertmanager_targets.gen.yml`, в .gitignore) и рендерится деплоем тем же условием, что включает профиль: цель при включённых алертах, `[]` при выключенных. Рендер идёт до `up` и пишет усечением на месте, поэтому инод сохраняется и работающий Prometheus подхватывает цель сам — та же ловушка одиночного бинд-маунта, что уже описана в этом workflow у Alertmanager. Пустой список пишется явно, а не удалением файла: несуществующий путь docker подменяет каталогом, и Prometheus не стартует вовсе. Closes #3155 --- .forgejo/workflows/deploy-metrics.yml | 32 +++++++++++++++++++ .gitignore | 7 ++++ docker-compose.metrics.yml | 4 ++- .../prometheus/alertmanager_targets.yml | 14 -------- ops/metrics/prometheus/prometheus.yml | 21 ++++++++---- 5 files changed, 56 insertions(+), 22 deletions(-) delete mode 100644 ops/metrics/prometheus/alertmanager_targets.yml diff --git a/.forgejo/workflows/deploy-metrics.yml b/.forgejo/workflows/deploy-metrics.yml index ed17c6be..637c4d5a 100644 --- a/.forgejo/workflows/deploy-metrics.yml +++ b/.forgejo/workflows/deploy-metrics.yml @@ -213,6 +213,38 @@ jobs: echo "::warning title=Алерты выключены::METRICS_TELEGRAM_BOT_TOKEN/CHAT_ID не заданы. Метрики и логи собираются, но при срабатывании правила НИКТО не будет уведомлён. Канал доставки — открытый вопрос #3078." fi + # ── Цели file_sd для Prometheus (#3155) ──────────────────────── + # Включатель профиля и цель для Prometheus обязаны стоять в ОДНОМ + # условии. Пока они жили порознь, вышло так: 27.08 профиль alerts + # подняли, Alertmanager стартовал Up и healthy, а файл целей остался + # плейсхолдером [] — Prometheus не видел ни одного приёмника + # (activeAlertmanagers: []) и сложил 1568 уведомлений в + # prometheus_notifications_dropped_total. Ни ошибки в логах, ни + # красного деплоя: снаружи алертинг выглядел рабочим. + # + # Пишем ДО `up`: свежесозданный контейнер обязан увидеть готовый + # файл. И пишем усечением на месте (`:>` вместо rm) — инод + # сохраняется, поэтому работающий Prometheus подхватывает + # содержимое сам, без пересоздания. Это ровно та ловушка одиночного + # бинд-маунта, что описана у Alertmanager ниже, только здесь её + # удаётся обойти, не трогая контейнер. + AM_TARGETS_FILE=ops/metrics/prometheus/alertmanager_targets.gen.yml + : > "$AM_TARGETS_FILE" + echo "# Файл рендерится деплоем (deploy-metrics.yml), правки руками затрутся." >> "$AM_TARGETS_FILE" + if [ "$PROFILES" = "alerts" ]; then + echo '- targets: ["alertmanager:9093"]' >> "$AM_TARGETS_FILE" + echo " labels:" >> "$AM_TARGETS_FILE" + echo " host: infra" >> "$AM_TARGETS_FILE" + echo "Prometheus: приёмник alertmanager:9093 прописан в целях." + else + # Пустой список, а НЕ отсутствующий файл: одиночный бинд-маунт + # несуществующего пути docker подменяет каталогом, и Prometheus + # не стартует вовсе. + echo "# Профиль alerts выключен — приёмников нет." >> "$AM_TARGETS_FILE" + echo "[]" >> "$AM_TARGETS_FILE" + echo "Prometheus: профиль alerts выключен — целей нет, это штатно." + fi + # ── read-only роль для датасорса GlitchTip ───────────────────── # Идемпотентно. Прав на запись не выдаём вовсе: датасорс Grafana # обязан быть безопасен даже при полном доступе к дашбордам. diff --git a/.gitignore b/.gitignore index 2ece89ee..1990723a 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,10 @@ ds-bundle/ # Боевой конфиг Alertmanager собирается на хосте из .tmpl (deploy-metrics.yml): # содержит токен бота и идентификатор чата, поэтому в репозиторий не попадает. ops/metrics/alertmanager/alertmanager.yml + +# Цели file_sd для Prometheus рендерит тот же деплой — по условию, которым +# включает профиль alerts. Отслеживаемая версия правилась руками и жила отдельно +# от включателя: 27.08 профиль включили, а файл так и остался плейсхолдером, +# и Prometheus не видел ни одного приёмника (#3155). Производный файл убирает +# сам зазор — правится только там же, где принимается решение о профиле. +ops/metrics/prometheus/alertmanager_targets.gen.yml diff --git a/docker-compose.metrics.yml b/docker-compose.metrics.yml index e7658647..381d517d 100644 --- a/docker-compose.metrics.yml +++ b/docker-compose.metrics.yml @@ -52,9 +52,11 @@ services: - ./ops/metrics/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro - ./ops/metrics/prometheus/rules:/etc/prometheus/rules:ro # file_sd для job "alertmanager" — см. комментарий в prometheus.yml. + # Файл производный: рендерится деплоем (deploy-metrics.yml) по тому же + # условию, что включает профиль alerts, и потому в репозитории его нет. # Забыть этот монт — значит вернуть "no such host" из-за отсутствующего # файла целей (был инцидент, когда похожий пропущенный монт положил Caddy). - - ./ops/metrics/prometheus/alertmanager_targets.yml:/etc/prometheus/alertmanager_targets.yml:ro + - ./ops/metrics/prometheus/alertmanager_targets.gen.yml:/etc/prometheus/alertmanager_targets.gen.yml:ro - prometheus_data:/prometheus expose: - "9090" diff --git a/ops/metrics/prometheus/alertmanager_targets.yml b/ops/metrics/prometheus/alertmanager_targets.yml deleted file mode 100644 index e4cdf51f..00000000 --- a/ops/metrics/prometheus/alertmanager_targets.yml +++ /dev/null @@ -1,14 +0,0 @@ -# file_sd target-файл для job "alertmanager" (см. prometheus.yml). -# -# Пустой список ([]) — штатное состояние, пока профиль "alerts" в -# docker-compose.metrics.yml выключен (#3078, канал доставки не решён): -# сервиса alertmanager не существует, и Prometheus просто не видит для -# него ни одной цели — без ошибок разрешения имени и без up{job=...}=0. -# -# Prometheus перечитывает этот файл на лету (file_sd), рестарт не нужен. -# Когда профиль alerts включат — заменить [] на: -# -# - targets: ["alertmanager:9093"] -# labels: -# host: infra -[] diff --git a/ops/metrics/prometheus/prometheus.yml b/ops/metrics/prometheus/prometheus.yml index d646d145..0ca972f5 100644 --- a/ops/metrics/prometheus/prometheus.yml +++ b/ops/metrics/prometheus/prometheus.yml @@ -22,15 +22,22 @@ rule_files: # Пока профиль alerts выключен, сервиса alertmanager не существует. Со # static_configs это раз в интервал давало "lookup alertmanager: no such # host" в логах и up{job="alertmanager"}=0. file_sd_configs вместо -# static_configs делает цель опциональной идиоматично для Prometheus: файл -# целей по умолчанию содержит пустой список ([]) — алертов нет и ошибок -# разрешения имени тоже нет. Prometheus перечитывает file_sd на лету, так что -# включение профиля alerts сводится к наполнению файла, без рестарта и без -# правки этого конфига. +# static_configs делает цель опциональной идиоматично для Prometheus: пустой +# список ([]) — алертов нет и ошибок разрешения имени тоже нет. Prometheus +# перечитывает file_sd на лету, так что включение профиля сводится к +# наполнению файла, без рестарта и без правки этого конфига. +# +# Файл ПРОИЗВОДНЫЙ (суффикс .gen, в .gitignore): его рендерит deploy-metrics.yml +# тем же условием, которым включает профиль. Одно условие — один результат. +# Отслеживаемая git'ом версия этого не давала: 27.08 профиль подняли, а файл +# так и остался плейсхолдером []. Alertmanager был Up и healthy, Prometheus же +# не видел ни одного приёмника (activeAlertmanagers: []), и 1568 уведомлений +# ушли в prometheus_notifications_dropped_total — молча, без единой ошибки ни +# в одном логе. Держать включатель и цель в разных местах больше нельзя. alerting: alertmanagers: - file_sd_configs: - - files: ["/etc/prometheus/alertmanager_targets.yml"] + - files: ["/etc/prometheus/alertmanager_targets.gen.yml"] scrape_configs: # Сам Prometheus. Нужен не для красоты: по нему строится алерт на здоровье @@ -46,7 +53,7 @@ scrape_configs: # просто не появится в выдаче вместо ошибки "no such host". - job_name: alertmanager file_sd_configs: - - files: ["/etc/prometheus/alertmanager_targets.yml"] + - files: ["/etc/prometheus/alertmanager_targets.gen.yml"] - job_name: loki static_configs: