Merge branch 'feature/monitoring'

This commit is contained in:
2026-02-09 10:27:59 +03:00
5 changed files with 346 additions and 8 deletions
+24 -1
View File
@@ -1,10 +1,18 @@
.PHONY: up ddl data transform reload-monitoring
.PHONY: up down clean ddl data transform reload-monitoring recover-monitoring
COMPOSE ?= docker compose
up:
$(COMPOSE) up -d
# Остановить и удалить контейнеры/сети текущего проекта
down:
$(COMPOSE) down
# Полная очистка окружения проекта (включая volumes)
clean:
$(COMPOSE) down -v --remove-orphans
ddl:
bash ./scripts/apply_clickhouse_ddl.sh
@@ -26,3 +34,18 @@ reload-monitoring:
@curl -s -u admin:admin -X POST http://localhost:3000/api/admin/provisioning/alerting/reload && echo " [alerting]"
@echo "=== Проверка ==="
@curl -s http://localhost:9090/api/v1/targets | grep -o '"job":"[^"]*"' | sort | uniq
# Восстановление мониторинга после сбоев (например, out of bounds / пустые дашборды)
recover-monitoring:
@echo "=== Восстановление мониторинга (жесткий режим) ==="
$(COMPOSE) rm -sf prometheus statsd-exporter
$(COMPOSE) up -d prometheus grafana kafka-exporter statsd-exporter
$(COMPOSE) restart airflow-scheduler airflow-webserver
@echo "=== Перезагрузка provisioning Grafana ==="
@sleep 2
@curl -s -u admin:admin -X POST http://localhost:3000/api/admin/provisioning/datasources/reload && echo " [datasources]"
@curl -s -u admin:admin -X POST http://localhost:3000/api/admin/provisioning/dashboards/reload && echo " [dashboards]"
@curl -s -u admin:admin -X POST http://localhost:3000/api/admin/provisioning/alerting/reload && echo " [alerting]"
@echo "=== Проверка targets ==="
@curl -s http://localhost:9090/api/v1/targets | grep -o '"job":"[^"]*"' | sort | uniq
@curl -s http://localhost:9090/api/v1/targets | grep -o '"health":"[^"]*"' | sort | uniq -c
@@ -116,11 +116,7 @@
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus_uid"
},
"expr": "delta(ClickHouseProfileEvents_OSCPUVirtualTimeMicroseconds[1m]) / 60 / 1000000 / scalar(count({__name__=~\"ClickHouseAsyncMetrics_CPUFrequencyMHz_.*\"})) * 100",
"expr": "rate(ClickHouseProfileEvents_OSCPUVirtualTimeMicroseconds[1m]) / 1000000 / scalar(count({__name__=~\"ClickHouseAsyncMetrics_CPUFrequencyMHz_.*\"})) * 100",
"refId": "A"
}
],
+1
View File
@@ -35,6 +35,7 @@ services:
ports:
- "8002:9000"
- "9123:8123"
- "9126:9126" # ClickHouse Prometheus metrics endpoint
kafka:
image: apache/kafka:3.8.0
+10 -2
View File
@@ -7,6 +7,8 @@
Базовые команды:
- `make up` (или `docker compose up -d`)
- `make down` (остановить и удалить контейнеры/сети проекта)
- `make clean` (полная очистка: `down -v --remove-orphans`)
- `make ddl` (применяет SQL из `sql/ddl/00_databases.sql` и `sql/ddl/*/*.sql` в ClickHouse)
- `make data` (пересоздаёт топики и заливает небольшой срез данных в Kafka; полный режим — `FULL=1 make data`)
- `make transform` (запускает batch-процесс ODS -> DDS -> DM)
@@ -95,6 +97,8 @@ docker compose exec -T clickhouse clickhouse-client --user=default --password=12
```bash
# Быстрый вариант (make)
make reload-monitoring
# Если мониторинг "залип" (No data/out of bounds) — жесткое восстановление:
make recover-monitoring
# Или вручную:
docker compose up -d prometheus grafana kafka-exporter statsd-exporter
@@ -136,10 +140,13 @@ curl -s http://localhost:9090/api/v1/targets | grep -o '"health":"[^"]*"'
```bash
# Рекомендуемый способ (через make)
make reload-monitoring
# Если метрики пропали/залипли:
make recover-monitoring
# Или вручную:
docker compose up -d prometheus grafana kafka-exporter
docker compose restart prometheus
docker compose rm -sf prometheus statsd-exporter
docker compose up -d prometheus grafana kafka-exporter statsd-exporter
docker compose restart airflow-scheduler airflow-webserver
curl -s -u admin:admin -X POST http://localhost:3000/api/admin/provisioning/datasources/reload
curl -s -u admin:admin -X POST http://localhost:3000/api/admin/provisioning/dashboards/reload
curl -s -u admin:admin -X POST http://localhost:3000/api/admin/provisioning/alerting/reload
@@ -248,6 +255,7 @@ curl -s -X POST -u admin:admin http://localhost:3000/api/admin/provisioning/aler
**Общие проблемы:**
- **"No data" в Grafana**: проверить, что Prometheus видит target (`Status -> Targets` в UI)
- **Dashboard не загрузился**: проверить логи Grafana — provisioning работает при первом старте контейнера
- **Prometheus spam `out of bounds` и дашборды пустые**: выполнить `make recover-monitoring`
**ClickHouse:**
- **Метрики не обновляются**: ClickHouse экспортирует метрики на `0.0.0.0:9126` внутри сети Docker
+310
View File
@@ -0,0 +1,310 @@
# План тестирования стенда (Smoke + Full)
Документ описывает два контура проверки системы:
- быстрый `smoke` для регрессий после изменений;
- полный `full` для финальной валидации end-to-end.
## Цель
Проверить, что стек `Kafka + ClickHouse + Airflow + Superset + Prometheus/Grafana`:
- стабильно поднимается;
- загружает данные по пути `kafka_load -> STG -> etl_pipeline -> ODS/DDS/DM`;
- корректно обрабатывает «грязные» записи (ошибки фиксируются в ODS, пайплайн не падает);
- отдает метрики и дашборды мониторинга.
## Общие принципы
- По умолчанию используем малый срез (`limit=50`) для быстрых и повторяемых проверок.
- Полный прогон (`limit=0`) выполняем отдельно как long-run сценарий.
- Основной путь запуска — через Airflow DAG.
- Критерий успеха: не только `Success` DAG, но и проверки данных/ошибок/мониторинга.
---
## Контур A: Smoke (быстрый)
Ожидаемая длительность: ~10-20 минут.
### A.1 Подготовка окружения
```bash
# Полная очистка стенда
make clean
# Запуск инфраструктуры
make up
# Проверка контейнеров
docker compose ps
```
Ожидаем:
- `airflow-init` в `Exited (0)`;
- остальные сервисы в `Up` (включая `superset`, `prometheus`, `grafana`, `kafka-exporter`, `statsd-exporter`).
### A.2 DDL и минимальная загрузка данных
```bash
# Инициализация схемы
docker compose exec -T airflow-webserver airflow dags trigger ddl_init
# Быстрый ingest: по 50 строк на поток
docker compose exec -T airflow-webserver airflow dags trigger kafka_load \
--conf '{"limit": 50, "reset_topics": true}'
```
Проверки:
```bash
# STG не пустой
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
--query "
SELECT 'browser_raw' AS table, count() AS cnt FROM stg.browser_raw
UNION ALL
SELECT 'location_raw', count() FROM stg.location_raw
UNION ALL
SELECT 'device_raw', count() FROM stg.device_raw
UNION ALL
SELECT 'geo_raw', count() FROM stg.geo_raw
"
```
Ожидаем: во всех 4 таблицах `cnt > 0`.
### A.3 ETL и проверки слоев
```bash
docker compose exec -T airflow-webserver airflow dags trigger etl_pipeline \
--conf '{"full_refresh": true}'
```
Проверки:
```bash
# ODS / DDS / DM
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
--query "
SELECT 'ods.browser_event' AS table, count() AS cnt FROM ods.browser_event
UNION ALL
SELECT 'dds.event', count() FROM dds.event
UNION ALL
SELECT 'dds.click', count() FROM dds.click
UNION ALL
SELECT 'dm.dq_summary', count() FROM dm.dq_summary
"
# Базовая целостность DDS (ожидаем 0 orphan-событий)
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
--query "
SELECT countIf(click_id IS NOT NULL AND click_id NOT IN (SELECT click_id FROM dds.click)) AS orphan_events
FROM dds.event
"
```
Ожидаем:
- `ods.browser_event`, `dds.event`, `dm.dq_summary` > 0;
- `orphan_events = 0`.
### A.4 Smoke мониторинга
```bash
# Prometheus targets
curl -s http://localhost:9090/api/v1/targets | grep -o '"health":"[^"]*"'
# Grafana health
curl -s -u admin:admin http://localhost:3000/api/health
# Дашборды по UID
curl -s -u admin:admin "http://localhost:3000/api/dashboards/uid/clickhouse-overview" | grep -o '"title":"[^"]*"'
curl -s -u admin:admin "http://localhost:3000/api/dashboards/uid/kafka-overview" | grep -o '"title":"[^"]*"'
curl -s -u admin:admin "http://localhost:3000/api/dashboards/uid/airflow-overview" | grep -o '"title":"[^"]*"'
```
Критерий успеха smoke:
- сервисы подняты;
- 3 DAG (`ddl_init`, `kafka_load`, `etl_pipeline`) успешны;
- данные проходят до DM;
- мониторинг и дашборды доступны.
---
## Контур B: Full (полный)
Ожидаемая длительность: ~30-60 минут.
### B.1 Полная загрузка и полный ETL
```bash
# Чистый старт
make clean && make up
# DDL
docker compose exec -T airflow-webserver airflow dags trigger ddl_init
# Полный ingest (все строки)
docker compose exec -T airflow-webserver airflow dags trigger kafka_load \
--conf '{"limit": 0, "reset_topics": true}'
# Полный ETL
docker compose exec -T airflow-webserver airflow dags trigger etl_pipeline \
--conf '{"full_refresh": true}'
```
### B.2 Проверка объемов и DQ
```bash
# Фактические размеры входа
wc -l data/*.jsonl
# Сводка по слоям
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 --query "
SELECT 'STG' as layer, sum(rows) as total_rows FROM (
SELECT count() as rows FROM stg.browser_raw UNION ALL
SELECT count() FROM stg.location_raw UNION ALL
SELECT count() FROM stg.device_raw UNION ALL
SELECT count() FROM stg.geo_raw
) UNION ALL
SELECT 'ODS', sum(rows) FROM (
SELECT count() FROM ods.browser_event UNION ALL
SELECT count() FROM ods.location_event UNION ALL
SELECT count() FROM ods.device_by_click UNION ALL
SELECT count() FROM ods.geo_by_click
) UNION ALL
SELECT 'DDS', sum(rows) FROM (
SELECT count() FROM dds.event UNION ALL
SELECT count() FROM dds.click
)"
# DQ summary
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
--query "SELECT * FROM dm.dq_summary ORDER BY layer, table_name, check_name"
```
Критерии успеха full:
- STG заполнен по всем 4 потокам;
- ODS/DDS/DM заполнены;
- `dm.dq_summary` не пуста и содержит метрики всех слоев.
### B.3 Тест восстановления stop/start
```bash
# Остановить без удаления данных
docker compose stop
# Проверить volumes
docker volume ls | grep -E 'clickhouse-data|kafka-data|pgmeta|grafana_lib|superset_data|superset_config'
# Поднять обратно
docker compose start
# Проверить, что данные сохранились
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
--query "SELECT count() FROM dds.event"
```
---
## Сценарий C: «Грязные» данные не валят пайплайн
Цель: доказать, что невалидные записи фиксируются в `ods.*_errors`, а ETL завершается успешно.
Предусловие: выполнен `Контур A` или `Контур B` (в STG уже есть валидные данные).
### C.1 Инъекция невалидных записей в STG
```bash
# browser: невалидные UUID и timestamp
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
--query "INSERT INTO stg.browser_raw (raw) VALUES ('{\"event_id\":\"bad-uuid\",\"event_timestamp\":\"bad-ts\",\"click_id\":\"bad-click\",\"event_type\":\"pageview\"}')"
# location: невалидный event_id
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
--query "INSERT INTO stg.location_raw (raw) VALUES ('{\"event_id\":\"bad-uuid\",\"page_url\":\"https://example.com\"}')"
# device: невалидные click_id и user_domain_id
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
--query "INSERT INTO stg.device_raw (raw) VALUES ('{\"click_id\":\"bad-uuid\",\"user_domain_id\":\"bad-uuid\",\"device_type\":\"Mobile\"}')"
# geo: невалидные click_id и координаты
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
--query "INSERT INTO stg.geo_raw (raw) VALUES ('{\"click_id\":\"bad-uuid\",\"geo_latitude\":\"abc\",\"geo_longitude\":\"def\"}')"
```
### C.2 Повторный ETL
```bash
docker compose exec -T airflow-webserver airflow dags trigger etl_pipeline \
--conf '{"full_refresh": true}'
```
Ожидаем: DAG `etl_pipeline` завершен в `Success`.
### C.3 Ассерты по ошибкам
```bash
# Ошибки должны попасть в *_errors таблицы
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
--query "
SELECT 'browser_event_errors' AS table, count() AS cnt FROM ods.browser_event_errors
UNION ALL
SELECT 'location_event_errors', count() FROM ods.location_event_errors
UNION ALL
SELECT 'device_by_click_errors', count() FROM ods.device_by_click_errors
UNION ALL
SELECT 'geo_by_click_errors', count() FROM ods.geo_by_click_errors
"
# При этом пайплайн продолжает давать бизнес-слой
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
--query "SELECT count() FROM dds.event"
```
Критерии успеха сценария C:
- `etl_pipeline` не падает;
- хотя бы одна `*_errors` таблица увеличилась;
- `dds.event` остается непустой (валидные данные продолжают обрабатываться).
---
## Проверка алертов (фактический набор)
Проверяем, что в Grafana загружены именно текущие provisioned-правила:
```bash
curl -s -u admin:admin http://localhost:3000/api/v1/provisioning/alert-rules | grep -o '"title":"[^"]*"'
```
Ожидаемые правила:
- ClickHouse:
- `ClickHouse Failed Queries Rate`
- `ClickHouse Memory Resident High`
- `ClickHouse Parts Active High`
- Airflow:
- `Airflow Scheduler Down`
- `Airflow Queue Backlog`
- `High Task Failure Rate`
- `High DAG Parse Time`
- Kafka:
- `Kafka Broker Down`
- `Kafka Consumer Lag High`
- `Kafka No Messages Produced`
---
## Финальный чек-лист приемки
- `Smoke` проходит стабильно после изменений в коде.
- `Full` проходит перед демонстрацией/релизом.
- Сценарий `Грязные данные` подтверждает, что ошибки локализуются в ODS и не ломают ETL.
- Метрики и дашборды доступны, алерты совпадают с текущей конфигурацией provisioning.