From 77e4fb6119d6566a703aef87d717dc9f382994d7 Mon Sep 17 00:00:00 2001 From: Dmitry Dementev Date: Sun, 5 Jul 2026 22:01:00 +0300 Subject: [PATCH] =?UTF-8?q?docs(course):=20=D1=81=D0=BE=D0=B3=D0=BB=D0=B0?= =?UTF-8?q?=D1=81=D0=BE=D0=B2=D0=B0=D0=BD=D1=8B=20=D1=83=D1=80=D0=BE=D0=BA?= =?UTF-8?q?=D0=B8=20=D1=81=D0=BE=20startup-history?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Зачем: - курс должен проходить на чистом стенде без архивного сида и скрытых шагов. - Что: - уроки 00, 01 и 05 согласованы с генераторными топиками, no-live default и consumer lag. - упражнение с kafka_msg_ts переведено на повторную заливку без сброса схемы. - учебный путь в операционной документации ведёт через startup-history. - Проверка: - make generated-history-analytics; make up; doc rg checks; git diff --check. --- docs/OPERATIONS.md | 7 +-- docs/course/README.md | 19 ++++++++ docs/course/lessons/00_kafka_intro.md | 16 +++++-- docs/course/lessons/01_kafka_to_clickhouse.md | 16 +++---- docs/course/lessons/05_monitoring.md | 47 ++++++++++++++----- 5 files changed, 78 insertions(+), 27 deletions(-) diff --git a/docs/OPERATIONS.md b/docs/OPERATIONS.md index 5e0212e..5a985c3 100644 --- a/docs/OPERATIONS.md +++ b/docs/OPERATIONS.md @@ -103,9 +103,10 @@ docker compose exec -T airflow-webserver airflow dags unpause etl_pipeline - Параметры: - `full_refresh` (`bool`, default `true`) — очистить DDS перед загрузкой - `wait_stg_timeout_sec` (`int`, default `600`, minimum `30`) — сколько секунд задача `wait_for_stg_data` ждёт появления данных в STG, прежде чем упасть по таймауту -- Зависимость: требует наличия данных в STG (от `kafka_load` или `make data`) -- В штатном сценарии STG наполняет `make generated-history-analytics` через - backfill генератора. +- Зависимость: требует наличия данных в STG. В штатном сценарии STG наполняет + `make generated-history-analytics` через backfill генератора. +- Архивные `kafka_load` и `make data` оставлены только для ручных экспериментов + и старых проверок. - Гейт целостности DDS: `check_dds_integrity` считает события без клика, а `assert_dds_integrity` роняет DAG при `orphan_events > 0`. Проверка идёт после `load_dds` и до `load_dm_summary`, чтобы DM не собирался поверх нарушенной связи diff --git a/docs/course/README.md b/docs/course/README.md index 2d1121c..42d9f1d 100644 --- a/docs/course/README.md +++ b/docs/course/README.md @@ -72,6 +72,25 @@ - **Идёшь с ментором** — еженедельный созвон покрывает несколько уроков сразу: туда несёшь затыки и ответы «своими словами» из тех же секций «Проверь себя». +## Проверка чистого маршрута + +Перед проверкой уроков 0, 1 и 5 подними стенд с нуля: + +```bash +make generated-history-analytics +make up +``` + +Что должен подтвердить человек: + +- урок 0: в Kafka UI видны четыре топика событий и понятны служебные топики + генератора; +- урок 1: после `CLEAN_START=0 make generated-history-analytics` учебная колонка + `kafka_msg_ts` не пропадает и заполняется; +- урок 5: Prometheus targets `clickhouse`, `kafka`, `airflow` находятся в `UP`, + а `Kafka No Messages Produced` трактуется с учётом того, запущен live-генератор + или только стартовая история. + ## Границы Материалы под конкретного менти (привязка к работодателю, легенда, подготовка к diff --git a/docs/course/lessons/00_kafka_intro.md b/docs/course/lessons/00_kafka_intro.md index 3b450a6..df24bff 100644 --- a/docs/course/lessons/00_kafka_intro.md +++ b/docs/course/lessons/00_kafka_intro.md @@ -60,9 +60,17 @@ make up - `geo_events` — гео; - `location_events` — местоположение. -Рядом может быть служебный топик `__consumer_offsets` (если включён показ внутренних -топиков) — его Kafka использует сама, мы его не трогаем. У каждого нашего топика в колонке -с партициями стоит **1**: топик маленький, делить не на что. +Рядом могут быть служебные топики: + +- `__consumer_offsets` — внутренний топик Kafka. В нём Kafka хранит прогресс + consumer-групп; +- `generator_state` — слепок состояния генератора на правой границе стартовой истории. + По нему live-продолжение понимает, откуда продолжать тот же мир; +- `generator_startup_history_manifest` — паспорт стартовой истории: seed, границы + модельного времени и контрольные числа. + +Служебные топики нужны стенду, но в упражнениях курса мы их не меняем. У каждого нашего +топика событий в колонке с партициями стоит **1**: топик маленький, делить не на что. **Сообщения в топике.** Открой `browser_events` → вкладку *Messages*. Это и есть события стенда. У каждого сообщения видно: @@ -127,7 +135,7 @@ consumer-группа помнит, где остановилась: дочит | Действие | Где смотреть | Что ожидать | |----------|--------------|-------------| -| открыть список топиков | Kafka UI → *Topics* | 4 топика событий (`*_events`), у каждого 1 партиция | +| открыть список топиков | Kafka UI → *Topics* | 4 топика событий (`*_events`) и, возможно, служебные топики генератора | | открыть `browser_events` | вкладка *Messages* | сообщения с offset'ами 0, 1, 2, …; в Value — JSON события | | сравнить два времени | Value (`event_timestamp`) vs Kafka *Timestamp* | время события и время доставки в Kafka различаются | | открыть consumers | Kafka UI → *Consumers* | 4 группы `ch_stg_*`, статус `STABLE`, по 1 участнику | diff --git a/docs/course/lessons/01_kafka_to_clickhouse.md b/docs/course/lessons/01_kafka_to_clickhouse.md index 4d31d81..e345d91 100644 --- a/docs/course/lessons/01_kafka_to_clickhouse.md +++ b/docs/course/lessons/01_kafka_to_clickhouse.md @@ -189,20 +189,20 @@ SELECT FROM stg.kafka_browser_raw; ``` -**Перезаливаем срез, чтобы новая колонка заполнилась.** Сначала чистим таблицу — иначе -рядом останутся строки из секции 2, вставленные ещё *до* `ADD COLUMN`, и в них -`kafka_msg_ts` будет пустой (`1970-01-01`): +**Перезаливаем срез, чтобы новая колонка заполнилась.** Сначала чистим только таблицу +приёмника — иначе рядом останутся строки из секции 2, вставленные ещё *до* +`ADD COLUMN`, и в них `kafka_msg_ts` будет пустой (`1970-01-01`): ```sql TRUNCATE TABLE stg.browser_raw; ``` -Затем возвращаем стенд в чистое состояние и заново создаём стартовую историю, чтобы -Kafka-движок прочитал сообщения уже с новой схемой: +Затем повторно создаём стартовую историю **без очистки volumes**. Это важно: +`CLEAN_START=0` сохраняет твою новую колонку и пересозданное MV, но добавляет свежие +сообщения в Kafka, чтобы ClickHouse прочитал их уже с новой схемой. ```bash -make generated-history-analytics -make up +CLEAN_START=0 make generated-history-analytics ``` **Смотрим результат:** @@ -232,7 +232,7 @@ ALTER TABLE stg.browser_raw DROP COLUMN kafka_msg_ts; ``` ```bash -make ddl # пересоздаёт эталонный MV из 10_stg.sql — схема снова как в репозитории +make ddl # пересоздаёт эталонное MV из 10_stg.sql, схема снова как в репозитории ``` Если запутался в состоянии — всегда есть полный чистый прогон: diff --git a/docs/course/lessons/05_monitoring.md b/docs/course/lessons/05_monitoring.md index d693cc2..4b24d7a 100644 --- a/docs/course/lessons/05_monitoring.md +++ b/docs/course/lessons/05_monitoring.md @@ -81,16 +81,19 @@ make up Открой Prometheus: `http://localhost:9090`. В меню зайди в **Status → Targets**. -Ожидаем три job: +Ожидаем основные job: | Job | Target внутри Docker | Что это значит | |-----|----------------------|----------------| | `clickhouse` | `clickhouse:9126` | ClickHouse отдаёт встроенный Prometheus endpoint | | `kafka` | `kafka-exporter:9308` | `kafka-exporter` подключился к Kafka и отдаёт метрики | | `airflow` | `statsd-exporter:9102` | `statsd-exporter` отдаёт метрики Airflow в формате Prometheus | +| `generator` | `generator:9109` | live-генератор отдаёт свои метрики, только когда явно запущен | -У всех трёх состояние должно быть `UP`. Если один target `DOWN`, Grafana дальше будет показывать -`No data` или старые значения. +У `clickhouse`, `kafka` и `airflow` состояние должно быть `UP`. `generator` на штатном +backfill-only стенде может быть `DOWN`, потому что `make up` не запускает live-генератор. +Это нормально для курса до явного `make generator-continue`. Если один из трёх основных +target `DOWN`, Grafana дальше будет показывать `No data` или старые значения. То же можно проверить из терминала: @@ -157,8 +160,14 @@ URL: `http://localhost:3000/d/kafka-overview/kafka-overview` - **Partitions** и **Partition Offsets (Current)** — текущие offset-ы по партициям. **Consumer lag** — это разница между тем, что уже лежит в топике, и тем, что consumer group -успела прочитать. В нашем стенде lag обычно быстро возвращается к нулю: данных мало, ClickHouse -читает быстро. Если lag растёт и не снижается, downstream не успевает за Kafka. +успела прочитать. В нашем стенде ClickHouse обычно читает быстро, поэтому большой устойчивый +lag не ожидается. + +Есть важная оговорка из урока 0: ClickHouse-движок не всегда показывает свой прогресс как +обычная Kafka-группа. Поэтому в Kafka UI колонки offset/lag у `ch_stg_*` могут быть пустыми, +а в Grafana панель lag может быть пустой или нулевой. Это не конфликт между уроками. Для +точной проверки чтения со стороны ClickHouse смотри `system.kafka_consumers`, а Grafana здесь +используй как общий сигнал: появился ли большой lag, который не уходит. ### Airflow Overview @@ -187,7 +196,7 @@ URL: `http://localhost:3000/d/airflow-overview/airflow-overview` ### `scrape_configs`: кого опрашивает Prometheus -В файле три блока: +В файле четыре блока: ```yaml scrape_configs: @@ -202,10 +211,12 @@ scrape_configs: `localhost`: Prometheus живёт внутри compose-сети и ходит к соседним контейнерам по их service name. -Kafka и Airflow устроены так же, но с exporter-ами: +Kafka, Airflow и generator устроены так же, но источники разные: - `kafka` → `kafka-exporter:9308`; -- `airflow` → `statsd-exporter:9102`. +- `airflow` → `statsd-exporter:9102`; +- `generator` → `generator:9109`, только когда live-генератор явно запущен. + Без live этот target может быть `DOWN`, как в проверке Prometheus выше. ### Почему Airflow идёт через StatsD @@ -257,9 +268,21 @@ Grafana. | Airflow Alerts | `High Task Failure Rate` | растёт rate failed tasks | | Airflow Alerts | `High DAG Parse Time` | DAG-файлы долго парсятся | -Не все эти правила обязаны стрелять в учебном стенде. Часть порогов специально похожа на -продовые: они показывают, как формулируется условие, но не создают шум на каждом маленьком -прогоне. +Не все эти правила обязаны быть тихими в учебном стенде. По умолчанию `make up` и +`make generated-history-analytics` **не запускают live-генератор**, поэтому после готовой +стартовой истории новые сообщения перестают приходить. Из-за этого `Kafka No Messages Produced` +может перейти в `Alerting` на полностью здоровом backfill-only стенде. Если хочешь проверить +это правило в спокойном состоянии, явно включи live: + +```bash +make generator-continue +``` + +После наблюдения останови live-генератор, чтобы он не менял стенд дальше: + +```bash +make generator-down +``` --- @@ -334,7 +357,7 @@ make recover-monitoring | Действие | Где смотреть | Что ожидать | |----------|--------------|-------------| -| открыть Prometheus targets | `http://localhost:9090` → Status → Targets | `clickhouse`, `kafka`, `airflow` в состоянии `UP` | +| открыть Prometheus targets | `http://localhost:9090` → Status → Targets | `clickhouse`, `kafka`, `airflow` в состоянии `UP`; `generator` может быть `DOWN` без live | | открыть `ClickHouse Overview` | Grafana dashboards | панели `Queries per Second`, `Failed Queries (total)`, `Inserted Rows/sec` не пустые | | открыть `Kafka Overview` | Grafana dashboards | видны `Brokers Up`, `Topics`, `Consumer Lag by Group` | | открыть `Airflow Overview` | Grafana dashboards | видны `Scheduler Heartbeat Rate`, `Queued Tasks`, `Task Failures vs Success Rate` |