Files
clickstream-data-platform/docs/architecture/monitoring.md
T
ddadmin e5c573bb25 fix(monitoring): панели уточнены по ревью, приёмка снята на стенде
- Зачем:
  - две линии ревью нашли, что колонки панели брака читаются как
    непересекающиеся кучи, «вовремя ли» из ADR 0002 не считается вовсе, а
    отказ от ряда «против вчерашнего» нигде не записан рядом с решением.
- Что:
  - в панели брака столбец назван «Из них в брак»: брак — часть приехавшего,
    а не соседняя куча.
  - панель слепка считает паузу после прошлого слепка — это и есть «вовремя
    ли» из ADR 0002; пустая пауза у самого раннего слепка держится
    обнуляемым типом.
  - отказ от ряда «против вчерашнего» записан в ADR 0002 и в monitoring.md с
    доводом: сдвиг на модельные сутки прописал бы ускорение мира в JSON.
  - в monitoring.md сказано, почему свежесть читает физическую _dist, а не
    ods.*_v, и почему панель слепка не знает окна; довод про сломанный JSON
    оставлен у кода, а не задвоен в доке.
  - README больше не утверждает, что панелей нет.
  - у разбора дашбордов убран флаг -e: он обещал утверждение, которого нет.
- Проверка:
  - make config-test (краснеет на нарочно сломанном JSON дашборда), make lint,
    make smoke 21/21, make check-clickhouse 10/10; все шесть панелей отвечают
    через /api/ds/query. Опыты приёмки и их следы — monitoring.md,
    «Что проверено».
2026-08-21 11:23:05 -04:00

17 KiB
Raw Blame History

Мониторинг: из чего собран и что скребёт

Документ описывает зону мониторинга: какие сервисы в неё входят, что собирает Prometheus, какие источники данных подготовлены в Grafana, каким пользователем она ходит в ClickHouse и что показывают собранные дашборды. Зачем зоне именно такой состав и какие дашборды на нём задуманы — ADR 0002; устройство доступа — ADR 0007.

Что здесь есть и чего ещё нет. Собран пол: свой образ Grafana с плагином ClickHouse, два подготовленных источника данных, экспортёр метрик Kafka и цель для него у Prometheus. Поверх пола стоит главный дашборд — «Данные»; «Кластер» и «Запросы» приносят отдельные тикеты. Правил оповещения и серверного журнала system.text_log тоже пока нет.

Состав

Сервис Образ Что делает
prometheus prom/prometheus:v3.13.2 опрашивает цели раз в 15 секунд, порт стенда 29090
grafana clickstream-grafana:local интерфейс мониторинга; собирается из infra/grafana/, порт стенда 23000
kafka-exporter danielqsj/kafka-exporter:v1.9.0 переводит состояние брокера в метрики Prometheus, порт 9308

Плагин ClickHouse 4.20.0 входит в образ Grafana. Фоновая установка плагинов при запуске отключена.

Что скребёт Prometheus

Цель Задание Метка role Откуда метрики
clickhouse-01:9363 clickhouse server встроенный экспортёр ноды
clickhouse-02:9363 clickhouse server встроенный экспортёр ноды
clickhouse-keeper:9363 clickhouse-keeper keeper встроенный экспортёр keeper
kafka-exporter:9308 kafka exporter внешний экспортёр

Ровно эти четыре цели пересчитывает check_prometheus_targets в scripts/stand-smoke.sh — и по значению up, и по списку адресов. Пятая цель красит смоук; это и есть способ заметить, что состав пола поменяли молча.

Источники данных Grafana

Оба источника подготовлены файлами в infra/grafana/provisioning/datasources/ и объявлены editable: false: править их через интерфейс нельзя, источник истины — репозиторий.

  • prometheus — основной по умолчанию, ходит на http://prometheus:9090.
  • clickhouse — плагин grafana-clickhouse-datasource, нативный протокол, clickhouse-01:9000, пользователь grafana, пароль из .env.

Источник ClickHouse смотрит на clickhouse-01. Системные таблицы обеих нод панели читают через clusterAllReplicas, как принято в ADR 0002.

Доступ к ClickHouse

Grafana ходит пользователем grafana с ролью monitoring_reader: читает слои, таблицы базы dic и системные таблицы. REMOTE разрешает clusterAllReplicas, CLUSTER — запросы ON CLUSTER. Решение о своей роли записано в ADR 0007. Пароль задаётся переменной CLICKHOUSE_GRAFANA_PASSWORD.

Куда кладутся дашборды

Каталог infra/grafana/provisioning/dashboards/ смонтирован в контейнер и объявлен провайдером в provider.yml: Grafana подхватывает оттуда файлы дашбордов, удалять и править их через интерфейс запрещено. Новый дашборд — это новый файл в каталоге и больше ничего: в compose.yaml заходить не нужно, ради этого каталог и заведён заранее.

Файлы каталога разбирает make config-test, рядом с файлами дагов и по тому же доводу (testing.md).

Дашборд «Данные»

infra/grafana/provisioning/dashboards/data.json — шесть панелей на один экран. Вопрос у экрана один: с потоком всё в порядке? Состав панелей и довод за каждую — ADR 0002.

Порогов и цветовых вердиктов на панелях нет: на дашборд смотрят глазами, красное и зелёное живёт в проверках и дагах DQ.

Панель Откуда берёт О чём отвечает
Свежесть ods.event_dist, ods.order_dist сколько прошло с последней доехавшей строки
Поток stg.hits_raw_dist, stg.orders_raw_dist сколько строк приезжает в слой сырья
Брак те же и ods.*_errors_dist сколько приехавшего не разобралось и какая это доля
Сходимость экспортёр Kafka и stg.hits_raw_dist произведённое против приземлившегося
Отставание kafka_consumergroup_lag сколько сообщений не прочитано
Дневной слепок stg.orders_raw_dist какой слепок доехал, когда и после какой паузы

Почему слои у панелей разные

Свежесть спрашивают у ODS. Это слой, который читают, и у заказов дорога до него длиннее: сырьё привозит забор, а типизированную строку кладёт разбор. Встань разбор — свежесть ODS это покажет, свежесть сырья нет.

Читает свежесть физическую _dist, а не каноническое ods.*_v. Представление прячет за собой FINAL и отвечает про текущее состояние сущностей, а вопрос здесь про доставку: местом диагностики доставленных версий физическая таблица названа там же, где заведено представление (sql/ddl/30-ods-views.sql).

Поток и брак считаются по слою сырья. Сырьё записывает каждую доставку и ничего не схлопывает, а ReplacingMergeTree в ODS схлопнет и повтор доставки события, и все версии одного заказа. Считай мы поток по ODS — он занизился бы, и тем сильнее, чем дольше живёт стенд.

Доля брака берётся от приехавшего, а не от суммы «годные плюс брак»: брак не отдельная куча, а часть сырья, которая не прошла разбор.

Окно у панели брака — окно дашборда, и это не косметика. Сырьё живёт трое суток, а брак — месяц: доля за всё время делила бы месяц брака на трое суток приехавшего и однажды перевалила бы за сотню процентов.

Панель дневного слепка, наоборот, окна не знает. Её вопрос про состояние, а не про отрезок, и последние десять слепков она показывает всегда.

Отдельного ряда «против вчерашнего» на панели потока нет, и это решение, а не пропуск. Реальное «вчера» на стенде чаще всего приходится на время, когда стенда ещё не было. А сдвинуть ряд на модельные сутки значит записать в JSON дашборда ускорение мира — ровно ту пометку про тик, которую ADR 0002 в дашборде держать не велит: поменяют ускорение, и панель молча соврёт. Сравнение остаётся глазу: модельные сутки укладываются в минуты реального времени, и соседние дни мира помещаются в одно окно панели сами.

Чтецов Kafka панели не читают

stg.hits_raw_kafka и stg.orders_raw_kafka — таблицы движка Kafka, а не хранилище: запрос из панели у второго забрал бы слепок себе и сдвинул офсеты (kafka_commit_on_select, см. sql/ddl/10-stg-tables.sql), а даг приёма приехал бы к пустому топику. Приземлившееся панели считают по stg.*_raw_dist, произведённое — по метрикам экспортёра.

Как считается сходимость

Произведённое живёт в Kafka, приземлившееся — в ClickHouse, и со своей стороны ни одна из них не видит другую. Поэтому панель смешанная: ряд «произведено в Kafka» приходит из Prometheus, ряд «приземлилось в ClickHouse» — из ClickHouse. Единицы общие — сообщений за интервал панели. Со стороны Kafka это разность офсетов топика, «сейчас минус интервал назад», с суммой по обеим партициям; со стороны ClickHouse — count() строк сырья по тому же интервалу.

Два соседних способа отвергнуты, и оба по замеру на живом стенде. increase растягивает приращение на всё окно и на минутном интервале промахивается на проценты: здоровый стенд выглядел бы расходящимся. Накопленный итог от начала окна точнее обоих приростов, но отсчитывать его надо от края окна — а на свежем стенде окно начинается раньше, чем появился экспортёр, и ряд пропадает целиком.

Столбцы ClickHouse подписаны правым краем, а не левым. Точка Prometheus — это прирост к моменту времени, а count() по интервалу — сумма, которая набирается к его концу; подпиши мы её началом, здоровые ряды разъехались бы ровно на один столбец. Шаг панели при этом не мельче минуты: экспортёр опрашивают раз в пятнадцать секунд, и на более мелком шаге ряд Kafka пошёл бы ступеньками там, где ряд ClickHouse идёт гладко.

Точка в точку ряды всё равно не совпадут, и ждать этого не надо: опрос экспортёра не попадает на границы столбцов, поэтому на разгоне потока линия Kafka идёт чуть выше. Читается устойчивый разрыв между линиями, а не разница в один столбец.

Панель смотрит на топик событий. У него приём непрерывный, и разрыв виден сразу. Слепок заказов уезжает пачкой раз в модельные сутки, и вопрос к нему другой — не «сходится ли поток», а «доехала ли выгрузка и вовремя ли». На него отвечает своя панель, и модельный день она берёт из самого сообщения: в сырьё он приезжает полем snapshot_date.

Что проверено

Права роли monitoring_reader сверены 21 августа 2026 года по документации ClickHouse через MCP Context7; разбор — в ADR 0007, раздел «Что проверено».

Сам состав проверен на живом стенде 21 августа 2026 года, собранном заново после make clean.

  • Grafana видит плагин grafana-clickhouse-datasource 4.20.0 (/api/plugins?type=datasource), оба подготовленных источника здоровы.
  • Запрос источника ClickHouse к system.one через clusterAllReplicas вернул 2 — с одной ноды видны обе.
  • Prometheus видит ровно четыре цели, все со значением up = 1; запрос kafka_consumergroup_lag вернул ряды по группам clickstream_*.
  • make smoke 21/21, make check-clickhouse 10/10, make check-services 7/7.

Дашборд «Данные» проверен на живом стенде в тот же день, world_live снят с паузы.

  • Дашборд поднимается из каталога сам: файл положен, панели появились без ручного импорта. Сохранение и удаление через API отвергнуты («Cannot save provisioned dashboard», «provisioned dashboard cannot be deleted»).
  • Все шесть панелей отвечают через /api/ds/query — и в окне часа по умолчанию, и в окне на шесть часов, которое начинается раньше, чем поднялся экспортёр. Пустых нет ни при идущем потоке, ни при остановленном.
  • Брак: подсунутое в топик hits негодное сообщение легло в ods.event_errors с классом keyset_mismatch; панель показала единицу и долю 0,039 % в десятиминутном окне. Строка убрана мутациями из stg.hits_raw_rep и ods.event_errors_rep, обе таблицы вернулись к нулю.
  • Сходимость: на живом потоке за двадцать пять минут медиана расхождения между рядами 3 %, худший столбец 11 % — на переломе потока, как и описано выше. Отказы тоже по замеру: increase расходился сильнее, а накопленный итог пропадал целиком, стоило окну начаться раньше экспортёра.
  • Свежесть реагирует на остановленный поток и возвращается. Через пять минут после конца модельного дня панель показала 305 с у событий и 288 с у заказов; стоило пустить следующий день, события вернулись к секунде. Свежесть заказов при этом растёт до конца дня и садится, когда доедет его слепок, — так и устроен второй источник.