docs: update README and architecture docs for Airflow orchestration workflow
This commit is contained in:
@@ -4,54 +4,62 @@
|
|||||||
[](./docs/ARCHITECTURE.md)
|
[](./docs/ARCHITECTURE.md)
|
||||||
[]()
|
[]()
|
||||||
|
|
||||||
Многослойное хранилище данных (STG → ODS → DDS → DM) для анализа кликстрима e-commerce.
|
Мини-демо для решения задания [DE-task.md](./data/DE-task.md): развернуть инфраструктуру на своей машине, прогнать кликстрим через Kafka в ClickHouse, сделать регулярный расчёт в Airflow и подготовить витрины под дашборд.
|
||||||
|
|
||||||
Данные поступают из Kafka, проходят типизацию и обогащение, формируя витрины для BI-аналитики.
|
Фокус проекта: быстро показать работающий end-to-end сценарий и понятным языком объяснить, как устроены слои и почему пайплайн не падает на "грязных" данных.
|
||||||
|
|
||||||
> **Соответствие заданию:** Реализован полный цикл Data Engineering: ingestion → хранилище со слоями → регулярный процесс трансформации → витрины для дашборда.
|
Коротко про поток:
|
||||||
|
`data/*.jsonl` -> Kafka (1 строка = 1 сообщение) -> ClickHouse `stg` (сырые JSON) -> `ods` (типизация + DQ) -> Airflow batch -> `dds` (сущности) -> `dm` (витрины VIEW) -> Superset.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Быстрый старт
|
## Быстрый старт (демо-сценарий)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Поднять инфраструктуру (Kafka + ClickHouse + Superset)
|
# 1) Поднять инфраструктуру
|
||||||
make up
|
make up
|
||||||
|
|
||||||
# 2. Создать структуру БД
|
# Проверить статусы контейнеров
|
||||||
make ddl
|
docker compose ps
|
||||||
|
|
||||||
# 3. Загрузить данные (автоматически потекут STG → ODS)
|
|
||||||
make data # первые 50 строк
|
|
||||||
# или: FULL=1 make data # полный датасет (1000 строк)
|
|
||||||
|
|
||||||
# 4. Подождать 5-10 сек (данные проходят через Kafka)
|
|
||||||
sleep 10
|
|
||||||
|
|
||||||
# 5. Запустить batch-трансформацию (ODS → DDS → DM)
|
|
||||||
make transform
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Проверка:**
|
Дальше основной путь идёт через Airflow (как в задании).
|
||||||
```bash
|
|
||||||
# Статистика по слоям
|
|
||||||
docker compose exec clickhouse clickhouse-client \
|
|
||||||
--user=default --password=123456 --query="
|
|
||||||
SELECT database, countDistinct(table) AS tables, sum(rows) AS rows
|
|
||||||
FROM system.parts WHERE database IN ('stg','ods','dds','dm')
|
|
||||||
GROUP BY database ORDER BY database
|
|
||||||
"
|
|
||||||
|
|
||||||
# Пример запроса к витрине
|
1. Открыть Airflow UI: `http://localhost:8080` (admin/admin)
|
||||||
docker compose exec clickhouse clickhouse-client \
|
2. Включить (unpause) и запустить `ddl_init` (создаёт базы/таблицы/VIEW в ClickHouse)
|
||||||
--user=default --password=123456 --query="
|
|
||||||
SELECT * FROM dm.v_utm_effectiveness ORDER BY clicks DESC LIMIT 5
|
Опционально можно триггернуть DAG из CLI (удобно для CI/скрипта):
|
||||||
"
|
```bash
|
||||||
|
docker compose exec -T airflow-webserver airflow dags trigger ddl_init
|
||||||
|
```
|
||||||
|
|
||||||
|
Загрузка небольшого среза данных в Kafka:
|
||||||
|
```bash
|
||||||
|
make data # по умолчанию первые 50 строк
|
||||||
|
# или: FULL=1 make data # полный датасет (1000 строк)
|
||||||
|
```
|
||||||
|
|
||||||
|
Запуск batch-трансформации (ODS -> DDS -> DM) в Airflow (если DAG выключен, сначала unpause):
|
||||||
|
```bash
|
||||||
|
docker compose exec -T airflow-webserver airflow dags trigger etl_pipeline \
|
||||||
|
--conf '{"full_refresh": true}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Smoke-check результата в ClickHouse:
|
||||||
|
```bash
|
||||||
|
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 --query \
|
||||||
|
"SELECT 'ods.browser_event' AS t, count() AS rows FROM ods.browser_event"
|
||||||
|
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 --query \
|
||||||
|
"SELECT 'dds.click' AS t, count() AS rows FROM dds.click"
|
||||||
|
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 --query \
|
||||||
|
"SELECT 'dds.event' AS t, count() AS rows FROM dds.event"
|
||||||
|
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 --query \
|
||||||
|
"SELECT 'dm.dq_summary' AS t, count() AS rows FROM dm.dq_summary"
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📊 Доступные сервисы
|
## Доступные сервисы
|
||||||
|
|
||||||
| Сервис | URL | Назначение |
|
| Сервис | URL | Назначение |
|
||||||
|--------|-----|------------|
|
|--------|-----|------------|
|
||||||
@@ -64,47 +72,43 @@ docker compose exec clickhouse clickhouse-client \
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🏗️ Архитектура
|
## Архитектура (в двух словах)
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TB
|
flowchart TB
|
||||||
subgraph Sources["📁 JSON файлы"]
|
subgraph Sources["JSONL файлы"]
|
||||||
BE[browser_events.jsonl]
|
BE[browser_events.jsonl]
|
||||||
LE[location_events.jsonl]
|
LE[location_events.jsonl]
|
||||||
DE[device_events.jsonl]
|
DE[device_events.jsonl]
|
||||||
GE[geo_events.jsonl]
|
GE[geo_events.jsonl]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Kafka["🚀 Kafka"]
|
subgraph Kafka["Kafka"]
|
||||||
KT[Топики]
|
KT[Топики]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph CH["🗄️ ClickHouse"]
|
subgraph CH["ClickHouse"]
|
||||||
STG["STG — сырые JSON"]
|
STG["stg: сырьё + Kafka MV"]
|
||||||
ODS["ODS — типизированные"]
|
ODS["ods: типизация + DQ"]
|
||||||
DDS["DDS — сущности"]
|
DDS["dds: сущности"]
|
||||||
DM["DM — витрины"]
|
DM["dm: витрины (VIEW)"]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Airflow["⚙️ Airflow"]
|
subgraph Airflow["Airflow"]
|
||||||
DAG[ETL DAGs]
|
DAG[DAG: ddl_init / etl_pipeline]
|
||||||
end
|
end
|
||||||
|
|
||||||
Sources -->|make data| Kafka -->|MV| STG -->|MV| ODS -->|Batch SQL| DDS -->|VIEW| DM
|
Sources -->|make data| Kafka -->|MV| STG -->|MV| ODS -->|Batch SQL| DDS -->|VIEW| DM
|
||||||
DAG -.->|оркестрация| ODS & DDS & DM
|
DAG -.->|оркестрация| ODS & DDS & DM
|
||||||
```
|
```
|
||||||
|
|
||||||
**Поток данных:**
|
Особенность задания про "грязные данные": парсинг не валит pipeline, ошибки фиксируются в `ods.*_errors` и в поле `parse_errors`.
|
||||||
1. **STG** — сырые JSON из Kafka (MergeTree)
|
|
||||||
2. **ODS** — типизированные данные + DQ (ReplacingMergeTree)
|
|
||||||
3. **DDS** — собранные сущности event + click (Batch SQL)
|
|
||||||
4. **DM** — витрины для BI (VIEW)
|
|
||||||
|
|
||||||
[Подробное описание архитектуры →](./docs/ARCHITECTURE.md)
|
[Подробное описание архитектуры →](./docs/ARCHITECTURE.md)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📁 Структура проекта
|
## Структура проекта
|
||||||
|
|
||||||
```
|
```
|
||||||
.
|
.
|
||||||
@@ -130,22 +134,24 @@ flowchart TB
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🛠️ Команды Makefile
|
## Команды Makefile
|
||||||
|
|
||||||
| Команда | Описание |
|
| Команда | Описание |
|
||||||
|---------|----------|
|
|---------|----------|
|
||||||
| `make up` | Поднять инфраструктуру |
|
| `make up` | Поднять инфраструктуру |
|
||||||
| `make ddl` | Создать структуру БД |
|
| `make ddl` | Применить DDL в ClickHouse (вне Airflow) |
|
||||||
| `make data` | Загрузить данные в Kafka (50 строк) |
|
| `make data` | Загрузить данные в Kafka (50 строк) |
|
||||||
| `FULL=1 make data` | Загрузить полный датасет |
|
| `FULL=1 make data` | Загрузить полный датасет |
|
||||||
| `make transform` | Запустить batch-процесс |
|
| `make transform` | Запустить batch-процесс (вне Airflow) |
|
||||||
|
|
||||||
> Примечание: данные ClickHouse теперь сохраняются в Docker volume `clickhouse-data`.
|
Примечания про сохранность данных:
|
||||||
> `docker compose down` сохраняет данные, `docker compose down -v` удаляет все volume (включая ClickHouse).
|
- Данные ClickHouse сохраняются в Docker volume `clickhouse-data`.
|
||||||
|
- Данные Kafka сохраняются в Docker volume `kafka-data`.
|
||||||
|
- `docker compose down` сохраняет named volumes, `docker compose down -v` удаляет их (и данные пропадут).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔗 Ключи данных
|
## Ключи данных (как джойним)
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
@@ -181,41 +187,46 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📚 Документация
|
## Дашборд в Superset (опционально, но полезно)
|
||||||
|
|
||||||
|
1. Открыть `http://localhost:8088`
|
||||||
|
2. Database -> Add:
|
||||||
|
- URI: `clickhouse+connect://default:123456@clickhouse:8123/default`
|
||||||
|
3. Создать datasets из `dm.v_*` (VIEW) и собрать несколько графиков
|
||||||
|
|
||||||
|
Идеи графиков под задание:
|
||||||
|
- Трафик по дням: `dm.v_daily_traffic` (events, uniq_users)
|
||||||
|
- Эффективность UTM: `dm.v_utm_effectiveness` (clicks, purchases)
|
||||||
|
- Популярные страницы: `dm.v_top_pages_daily` (pageviews)
|
||||||
|
- Качество данных: `dm.v_dq_errors_daily` (rows_cnt по error_code)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Частые проблемы
|
||||||
|
|
||||||
|
- `etl_pipeline` падает с сообщением про схему: сначала запустите `ddl_init`.
|
||||||
|
- После `docker compose down -v` схема и данные исчезнут: нужно заново `ddl_init` и `make data`.
|
||||||
|
- Подключения используют разные протоколы:
|
||||||
|
- Airflow (ClickHouseOperator) ходит в ClickHouse по native TCP (порт `9000` внутри сети Docker).
|
||||||
|
- Superset (clickhouse-connect) ходит по HTTP (порт `8123` внутри сети Docker).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Статус проекта
|
||||||
|
|
||||||
|
Реализовано (Этап 1):
|
||||||
|
- DAG `ddl_init`: последовательное применение DDL + проверка схемы.
|
||||||
|
- DAG `etl_pipeline`: precheck, ожидание данных в ODS, пересчёт DDS/DM, базовые проверки.
|
||||||
|
- Устойчивость к "грязным" данным: ошибки парсинга сохраняются в ODS, а не валят ingest.
|
||||||
|
|
||||||
|
В планах (не требуется для MVP задания):
|
||||||
|
- DAG `kafka_load` (чистый ingest из `.jsonl` в Kafka средствами Airflow).
|
||||||
|
- Инкрементальный batch (watermark вместо `full_refresh`).
|
||||||
|
- DQ мониторинг по расписанию.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Документация
|
||||||
|
|
||||||
- [Архитектура и слои](./docs/ARCHITECTURE.md) — подробное описание STG/ODS/DDS/DM, ER-диаграммы, обоснование решений
|
- [Архитектура и слои](./docs/ARCHITECTURE.md) — подробное описание STG/ODS/DDS/DM, ER-диаграммы, обоснование решений
|
||||||
- [DE-task.md](./data/DE-task.md) — исходное задание
|
- [DE-task.md](./data/DE-task.md) — исходное задание
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 Дашборд в Superset
|
|
||||||
|
|
||||||
1. Открыть http://localhost:8088
|
|
||||||
2. Database → Add:
|
|
||||||
- **URI:** `clickhouse+connect://default:123456@clickhouse:8123/default`
|
|
||||||
3. Datasets → Add from `dm.v_*`
|
|
||||||
4. Charts & Dashboard
|
|
||||||
|
|
||||||
Основные витрины:
|
|
||||||
- `v_events_enriched` — полное обогащение
|
|
||||||
- `v_daily_traffic` — агрегация по дням
|
|
||||||
- `v_utm_effectiveness` — эффективность кампаний
|
|
||||||
- `v_top_pages_daily` — воронка страниц
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔮 Развитие проекта
|
|
||||||
|
|
||||||
### ✅ Реализовано
|
|
||||||
- [x] **Airflow** — оркестрация batch-процесса (инфраструктура готова, DAGs в разработке)
|
|
||||||
|
|
||||||
### 📋 В планах
|
|
||||||
- [ ] **Инкрементальный batch** — watermark-based загрузка
|
|
||||||
- [ ] **Материализация витрин** — для тяжёлых агрегаций
|
|
||||||
- [ ] **DQ мониторинг** — алерты на ошибки парсинга
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📝 Лицензия
|
|
||||||
|
|
||||||
Проект создан для образовательных целей в рамках DE-тестового задания.
|
|
||||||
|
|||||||
+1
-1
@@ -37,7 +37,7 @@ services:
|
|||||||
# container_name: kafka
|
# container_name: kafka
|
||||||
ports:
|
ports:
|
||||||
- 9092:9092
|
- 9092:9092
|
||||||
# Если хочешь сохранять топики/сообщения между `docker compose down/up` — раскомментируй:
|
# Данные топиков/сообщений сохраняются в named volume `kafka-data` (между `docker compose down/up`).
|
||||||
volumes:
|
volumes:
|
||||||
- kafka-data:/tmp/kraft-combined-logs
|
- kafka-data:/tmp/kraft-combined-logs
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
+12
-9
@@ -589,19 +589,22 @@ INSERT INTO dm.daily_traffic SELECT * FROM dm.v_daily_traffic;
|
|||||||
Инфраструктура Airflow развёрнута и готова к использованию:
|
Инфраструктура Airflow развёрнута и готова к использованию:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# dags/etl_pipeline_dag.py
|
# dags/ddl_init_dag.py и dags/etl_pipeline_dag.py
|
||||||
with DAG('etl_pipeline'):
|
#
|
||||||
ddl = BashOperator(task_id='ddl', bash_command='make ddl')
|
# Учебный формат:
|
||||||
load = BashOperator(task_id='load', bash_command='make data')
|
# - DDL и трансформации выполняются явными SQL-task через ClickHouseOperator;
|
||||||
transform = BashOperator(task_id='transform', bash_command='make transform')
|
# - SQL-файлы вызываются по фиксированным путям;
|
||||||
|
# - загрузка данных в Kafka (Этап 1) выполняется через `make data`.
|
||||||
ddl >> load >> transform
|
#
|
||||||
|
# Основной demo-сценарий:
|
||||||
|
# ddl_init -> make data -> etl_pipeline
|
||||||
```
|
```
|
||||||
|
|
||||||
**Подключение к ClickHouse:**
|
**Подключение к ClickHouse:**
|
||||||
- Connection: `clickhouse_default`
|
- Connection: `clickhouse_default`
|
||||||
- URL: `clickhouse://default:123456@clickhouse:8123/default`
|
- URL: `clickhouse://default:123456@clickhouse:9000/default` (native TCP для Airflow plugin)
|
||||||
- Provider: `clickhouse-connect` (в `airflow/requirements.txt`)
|
- Provider/интеграция: `airflow-clickhouse-plugin` (в `airflow/requirements.txt`), задачи выполняются через `ClickHouseOperator`.
|
||||||
|
- Примечание: Superset подключается к ClickHouse по HTTP (обычно `clickhouse+connect://...:8123/...`).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user