From c6e15954ba438a973066bffccfc7ba8d320f394d Mon Sep 17 00:00:00 2001 From: bot-backend Date: Wed, 26 Aug 2026 13:14:28 +0300 Subject: [PATCH] =?UTF-8?q?fix(observability):=20=D0=BA=D0=BE=D0=BD=D1=82?= =?UTF-8?q?=D0=B5=D0=B9=D0=BD=D0=B5=D1=80=D0=BD=D1=8B=D0=B5=20=D0=BC=D0=B5?= =?UTF-8?q?=D1=82=D1=80=D0=B8=D0=BA=D0=B8=20=D1=82=D0=B5=D1=80=D1=8F=D0=BB?= =?UTF-8?q?=D0=B8=D1=81=D1=8C=20=D1=86=D0=B5=D0=BB=D0=B8=D0=BA=D0=BE=D0=BC?= =?UTF-8?q?=20+=20=D0=B4=D0=B2=D0=B0=20=D1=85=D0=B2=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B0=20=D1=81=D1=82=D0=B5=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Три независимых дефекта, найденных на живом проде после подъёма стека метрик. 1. cAdvisor-метрики не доезжали ВООБЩЕ. В Prometheus ноль имён container_* при 2034 именах всего, хотя cAdvisor отдаёт 880 рядов, scrape-таргет в alloy health=up с последним скрейпом 10 мс назад, а remote_write рабочий (node/postgres идут через него же и доезжают). Методом исключения — потери в prometheus.relabel.cadvisor_trim, во втором правиле: rule { source_labels = ["name"], regex = "", action = "drop" } Замысел был выкинуть безымянные cgroup-ряды (id="/"). Но regex в Alloy документированно дефолтится в (.*), и пустая строка неотличима от незаданного значения — такой drop рискует выкидывать вообще всё, что и наблюдалось. Заменено на однозначное keep regex=".+" — тот же замысел, без зависимости от того, как трактуется пустой regex. 2. healthcheck alloy не мог пройти никогда: дёргал wget, которого в образе grafana/alloy нет (как и curl, и nc). Контейнер вечно unhealthy при полностью исправном alloy — ложная тревога, маскирующая настоящие сбои. Заменено на сырой HTTP через bash /dev/tcp, без внешних утилит. 3. Prometheus раз в минуту писал "lookup alertmanager: no such host" и держал up{job="alertmanager"}=0. Alertmanager намеренно за профилем alerts до решения #3078 — дефект не в профиле, а в безусловной ссылке на сервис. Оба места (alerting.alertmanagers и job_name: alertmanager) переведены на file_sd_configs с файлом целей, по умолчанию пустым: целей нет — ошибок тоже нет. Prometheus перечитывает file_sd на лету, поэтому включение профиля сведётся к наполнению файла, без рестарта и правки конфига. Файл целей смонтирован в сервис prometheus явным volume. Проверено на живом хосте, не на глаз: - alloy fmt обоих .alloy в одноразовом контейнере grafana/alloy:v1.6.1 - exit 0 - promtool check config в prom/prometheus:v3.1.0 - valid, 16 rules found - механизм нового healthcheck выполнен внутри работающего gendesign-alloy: первая строка ответа "HTTP/1.0 200 OK", grep матчится, RESULT=HEALTHY - наличие bash/head/grep/printf в образе alloy подтверждено command -v --- docker-compose.metrics-agent.yml | 8 +++++- docker-compose.metrics.yml | 4 +++ ops/metrics/alloy/alloy-apps.alloy | 8 ++++-- ops/metrics/alloy/alloy-infra.alloy | 7 +++-- .../prometheus/alertmanager_targets.yml | 14 ++++++++++ ops/metrics/prometheus/prometheus.yml | 26 +++++++++++-------- 6 files changed, 51 insertions(+), 16 deletions(-) create mode 100644 ops/metrics/prometheus/alertmanager_targets.yml diff --git a/docker-compose.metrics-agent.yml b/docker-compose.metrics-agent.yml index 4ce49308..11eddf4b 100644 --- a/docker-compose.metrics-agent.yml +++ b/docker-compose.metrics-agent.yml @@ -75,7 +75,13 @@ services: mem_limit: 512m logging: *default-logging healthcheck: - test: ["CMD-SHELL", "wget -q --spider http://localhost:12345/-/ready || exit 1"] + # В образе grafana/alloy нет wget/curl/nc, только bash — шлём + # сырой HTTP-запрос через /dev/tcp и проверяем код 200 в ответе. + test: + [ + "CMD-SHELL", + "bash -c 'exec 3<>/dev/tcp/localhost/12345 && printf \"GET /-/ready HTTP/1.0\r\n\r\n\" >&3 && head -1 <&3 | grep -q 200' || exit 1", + ] interval: 30s timeout: 10s retries: 5 diff --git a/docker-compose.metrics.yml b/docker-compose.metrics.yml index 4b70b18d..0ab29cd0 100644 --- a/docker-compose.metrics.yml +++ b/docker-compose.metrics.yml @@ -51,6 +51,10 @@ services: volumes: - ./ops/metrics/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro - ./ops/metrics/prometheus/rules:/etc/prometheus/rules:ro + # file_sd для job "alertmanager" — см. комментарий в prometheus.yml. + # Забыть этот монт — значит вернуть "no such host" из-за отсутствующего + # файла целей (был инцидент, когда похожий пропущенный монт положил Caddy). + - ./ops/metrics/prometheus/alertmanager_targets.yml:/etc/prometheus/alertmanager_targets.yml:ro - prometheus_data:/prometheus expose: - "9090" diff --git a/ops/metrics/alloy/alloy-apps.alloy b/ops/metrics/alloy/alloy-apps.alloy index 638e3268..f1e03dd3 100644 --- a/ops/metrics/alloy/alloy-apps.alloy +++ b/ops/metrics/alloy/alloy-apps.alloy @@ -92,10 +92,14 @@ prometheus.relabel "cadvisor_trim" { action = "keep" } + // Служебные контейнеры docker без имени только зашумляют графики. + // keep .+ вместо drop с пустым regex: в Alloy незаданный regex + // документированно дефолтится в (.*), а пустая строка неотличима от + // незаданного значения — такой drop рискует выкинуть вообще все ряды. rule { source_labels = ["name"] - regex = "" - action = "drop" + regex = ".+" + action = "keep" } } diff --git a/ops/metrics/alloy/alloy-infra.alloy b/ops/metrics/alloy/alloy-infra.alloy index 17c00c47..0ecc6ffe 100644 --- a/ops/metrics/alloy/alloy-infra.alloy +++ b/ops/metrics/alloy/alloy-infra.alloy @@ -64,10 +64,13 @@ prometheus.relabel "cadvisor_trim" { } // Служебные контейнеры docker без имени только зашумляют графики. + // keep .+ вместо drop с пустым regex: в Alloy незаданный regex + // документированно дефолтится в (.*), а пустая строка неотличима от + // незаданного значения — такой drop рискует выкинуть вообще все ряды. rule { source_labels = ["name"] - regex = "" - action = "drop" + regex = ".+" + action = "keep" } } diff --git a/ops/metrics/prometheus/alertmanager_targets.yml b/ops/metrics/prometheus/alertmanager_targets.yml new file mode 100644 index 00000000..e4cdf51f --- /dev/null +++ b/ops/metrics/prometheus/alertmanager_targets.yml @@ -0,0 +1,14 @@ +# 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 35c52b97..d646d145 100644 --- a/ops/metrics/prometheus/prometheus.yml +++ b/ops/metrics/prometheus/prometheus.yml @@ -19,15 +19,18 @@ global: rule_files: - /etc/prometheus/rules/*.yml -# Пока профиль alerts выключен, этой цели не существует и Prometheus раз в -# интервал пишет в лог, что не смог её разрешить. Это шум, а не отказ: правила -# считаются и видны в интерфейсе, просто уведомлять некому. Молча выключать -# alerting не стали — тогда включение алертов потребовало бы правки конфига, -# а не одной переменной. +# Пока профиль alerts выключен, сервиса alertmanager не существует. Со +# static_configs это раз в интервал давало "lookup alertmanager: no such +# host" в логах и up{job="alertmanager"}=0. file_sd_configs вместо +# static_configs делает цель опциональной идиоматично для Prometheus: файл +# целей по умолчанию содержит пустой список ([]) — алертов нет и ошибок +# разрешения имени тоже нет. Prometheus перечитывает file_sd на лету, так что +# включение профиля alerts сводится к наполнению файла, без рестарта и без +# правки этого конфига. alerting: alertmanagers: - - static_configs: - - targets: ["alertmanager:9093"] + - file_sd_configs: + - files: ["/etc/prometheus/alertmanager_targets.yml"] scrape_configs: # Сам Prometheus. Нужен не для красоты: по нему строится алерт на здоровье @@ -38,11 +41,12 @@ scrape_configs: labels: host: infra + # Тот же опциональный source, что и в alerting.alertmanagers выше: пока + # профиль alerts не включён, файл целей пуст — up{job="alertmanager"} + # просто не появится в выдаче вместо ошибки "no such host". - job_name: alertmanager - static_configs: - - targets: ["alertmanager:9093"] - labels: - host: infra + file_sd_configs: + - files: ["/etc/prometheus/alertmanager_targets.yml"] - job_name: loki static_configs: -- 2.45.3