Настроить логирование в ClickHouse

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Настроить логирование в ClickHouse

1. Цель задачи

Научиться проектировать и развёртывать высоконагруженную систему сбора JSON-логов на базе ClickHouse. Освоить оптимальную схему таблицы для логов, настройку партиционирования и индексов, обеспечивающих быстрый поиск по trace_id. Сымитировать нагрузку и проверить, что система выдерживает прием 1 млн записей в секунду.

Ключевой результат Полностью рабочая инсталляция ClickHouse с таблицей для логов, скриптами загрузки синтетических данных, демонстрацией поиска по trace_id и измерением пропускной способности не менее 1 млн логов/сек.


2. Исходные данные

Что нужноОткуда взять
ClickHouse (сервер, клиент)Официальный сайт clickhouse.com – установка локально или через Docker
Язык Python с библиотекамиclickhouse-driver, json, uuid, time, random
Инструмент генерации логовСобственный Python-скрипт
Инструмент мониторингаGrafana (опционально) – для визуализации
Тестовый дашбордГрафана + altinity/clickhouse-grafana плагин

Если нет реального инструмента — симулируем:

  • Устанавливаем ClickHouse одной командой Docker:
    docker run -d --name ch-logging -p 8123:8123 -p 9000:9000 clickhouse/clickhouse-server:latest
    
  • Генерируем синтетические логи в формате JSON с полями: timestamp, level, service, message, trace_id.
  • Нагрузку создаём multiprocessing-скриптом, отправляющим пакеты по 10 000 строк через HTTP Bulk Insert.

3. Технологический стек

КомпонентИнструментыНазначение
Хранилище логовClickHouse 24+Высокопроизводительная колоночная БД для логов
Клиент для вставкиPython + clickhouse-driverВставка JSON-логов через Native Protocol
Генератор логовPython + json, uuid, datetimeСоздание синтетических логов с заданной интенсивностью
Анализаторclickhouse-clientВыполнение SQL-запросов, поиск по trace_id
ВизуализацияGrafana + ClickHouse data sourceДашборд с QPS (queries per second), задержками
Нагрузочное тестированиеPython multiprocessing + timeИзмерение пропускной способности до 1 млн логов/сек

4. Этапы выполнения

Этап 1: Поднятие ClickHouse и создание таблицы (30 минут)

Действия

  1. Запустить ClickHouse в Docker:

    docker run -d --name ch-logging -p 8123:8123 -p 9000:9000 \
      -e CLICKHOUSE_DB=logs_db \
      clickhouse/clickhouse-server:latest
    
  2. Подключиться через clickhouse-client внутри контейнера или через docker exec:

    docker exec -it ch-logging clickhouse-client
    
  3. Создать базу данных:

    CREATE DATABASE IF NOT EXISTS logs_db;
    
  4. Создать таблицу для JSON-логов с учётом высокой нагрузки и поиска по trace_id:

    CREATE TABLE logs_db.json_logs (
        timestamp   DateTime64(3) CODEC(Delta, ZSTD),
        level       LowCardinality(String),
        service     LowCardinality(String),
        message     String,
        trace_id    UUID,
        additional  String DEFAULT ''
    ) ENGINE = MergeTree()
    PARTITION BY toYYYYMM(timestamp)
    ORDER BY (trace_id, timestamp)
    SETTINGS index_granularity = 4096;
    

    Важно ORDER BY (trace_id, timestamp) создаёт первичный индекс по trace_id, что критично для быстрого поиска. Партиционирование по месяцу облегчает удаление старых данных.

  5. Проверить создание: SHOW CREATE TABLE logs_db.json_logs;

Ожидаемый результат этапа Работающий ClickHouse с пустой таблицей, готовой принимать логи.


Этап 2: Скрипт генерации синтетических логов (45 минут)

Действия

  1. Написать Python-файл log_generator.py, который:
    • Создаёт строки в формате JSON.
    • Случайные trace_id (UUID v4).
    • Уровни: INFO, WARN, ERROR (распределение 70:20:10).
    • Случайные service: api-gateway, auth, payment, inventory.
    • Текущее время в миллисекундах.
  2. Добавить функцию generate_batch(batch_size=10000), возвращающую список словарей.
  3. Сгенерировать 100 000 строк для первичной загрузки (debug).
  4. Вывести пример строки:
    {"timestamp": "2025-03-25 10:00:00.123", "level": "INFO", "service": "api-gateway", "message": "request processed", "trace_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"}
    

Ожидаемый результат этапа Рабочий генератор, способный создавать миллионы строк за разумное время.


Этап 3: Вставка логов в ClickHouse (60 минут)

Действия

  1. Использовать clickhouse-driver (установить pip install clickhouse-driver).
  2. Подключиться к ClickHouse:
    from clickhouse_driver import Client
    client = Client('localhost')
    
  3. Оптимальная вставка – через INSERT INTO ... VALUES с пакетом кортежей:
    data = [(row['timestamp'], row['level'], row['service'],
             row['message'], row['trace_id']) for row in batch]
    client.execute('INSERT INTO logs_db.json_logs VALUES', data)
    
  4. Настроить буферизацию и повторные попытки при ошибках.
  5. Вставить 100 000 строк, проверить количество:
    SELECT count() FROM logs_db.json_logs;
    
  6. Провести поиск по одному trace_id и замерить время:
    SELECT * FROM logs_db.json_logs WHERE trace_id = '...' FORMAT JSON;
    
    Убедиться, что время < 50 мс.

Ожидаемый результат этапа Таблица заполнена данными, поиск по trace_id работает быстро.


Этап 4: Нагрузочное тестирование – 1 млн логов/сек (90 минут)

Действия

  1. Написать скрипт load_test.py, который:
    • Использует multiprocessing.Pool с 8–16 воркерами.
    • Каждый воркер генерирует пачки по 10 000 строк и отправляет через client.execute.
    • Засекает время начала и окончания.
    • Вычисляет throughput = общее количество / затраченное время (сек).
  2. Настроить ClickHouse на приём высокой нагрузки:
    • Отключить fsync на время теста (для чистоты замера): SETTINGS fsync_after_insert = 0 (в конфиге).
    • Увеличить max_insert_block_size до 1 048 576.
    • Убедиться, что сетевые каналы не узкие (использовать локальный хост).
  3. Запустить тест с целью достичь 1 млн строк в секунду (суммарно на всех воркерах).
  4. Если не хватает – увеличить количество воркеров или размер пачки.
  5. Зафиксировать результат в консоли:
    Inserted 1,000,000 rows in 1.02 sec → 980,392 rows/sec
    
  6. Одновременно в другом окне проверить, что поиск по trace_id остаётся быстрым (с помощью отдельного процесса).

Ожидаемый результат этапа Измеренный throughput не менее 1 млн записей/сек.


Этап 5: Визуализация в Grafana (40 минут)

Действия

  1. Установить Grafana (Docker):
    docker run -d --name=grafana -p 3000:3000 grafana/grafana-oss
    
  2. Добавить источник данных ClickHouse (плагин altinity-clickhouse-datasource).
  3. Создать дашборд с панелями:
    • QPS (вставок в секунду) – используя system.query_log.
    • Распределение уровней – pie chart.
    • Последние ошибки – таблица с последними 20 ERROR-логами.
    • Поиск по trace_id – text-виджет с переменной trace_id.
  4. Экспортировать дашборд в JSON и сохранить в репозиторий.

Ожидаемый результат этапа Дашборд Grafana, отображающий текущее состояние логов.


5. Критерии приемки (Definition of Done)

  • ClickHouse запущен в Docker или нативно, таблица logs_db.json_logs создана с указанной схемой.
  • Python-скрипт генерации логов работает и создаёт строки с полями timestamp, level, service, message, trace_id.
  • Вставка 100 000 строк завершается без ошибок.
  • Поиск по произвольному trace_id возвращает результат за < 100 мс.
  • Нагрузочный тест показывает пропускную способность ≥ 1 млн строк/сек (средняя за 10 секунд).
  • Дашборд Grafana подключён к ClickHouse и отображает хотя бы одну полезную панель.
  • Весь код и конфигурация зафиксированы в Git-репозитории.

6. Ожидаемый результат

Основной артефакт Папка проекта со следующей структурой:

logging-clickhouse/
├── docker-compose.yml           # ClickHouse + Grafana
├── schema.sql                   # DDL таблицы
├── log_generator.py             # генератор логов
├── load_test.py                 # нагрузочный тест
├── grafana_dashboard.json       # экспортированный дашборд
└── README.md                    # инструкция по запуску

Дополнительно Видео/скриншот консоли с результатом throughput > 1 млн/сек и скриншот дашборда.


7. Возможные сложности и их решение

СложностьРешение
ClickHouse не принимает вставки с высокой скоростьюПроверить max_insert_block_size, увеличить max_threads до 16; отключить fsync
Поиск по trace_id медленныйУбедиться, что trace_id – первая колонка в ORDER BY. Использовать UUID тип
Docker потребляет слишком много памятиОграничить память контейнера: --memory=4g
Python-скрипт падает при большой нагрузкеУвеличить таймауты client.execute(timeout=60); использовать connection_pool
JSON с произвольными полямиИспользовать тип String для additional или Nested для структурированных

8. Бюджет времени (оценка)

ЭтапВремя
Этап 1: Поднятие ClickHouse и таблица30 мин
Этап 2: Скрипт генерации логов45 мин
Этап 3: Вставка и базовая проверка60 мин
Этап 4: Нагрузочное тестирование90 мин
Этап 5: Визуализация в Grafana40 мин
Итого~4 ч

При первом выполнении возможны задержки на отладку производительности – заложите дополнительно 1-2 часа.


9. Связанные вопросы из базы знаний

ВопросТема
12Хранение логов в ClickHouse
45Индексы и партиционирование
78Bulk Insert в ClickHouse
101Мониторинг производительности БД
203Эфемерные UUID и их хранение
256Оптимизация запросов по trace_id
302Настройка Grafana для логов
401Утилиты нагрузочного тестирования
567Docker Compose для ClickHouse
612JSON-логи и их парсинг

10. Чек-лист самопроверки

  • Я запустил ClickHouse через Docker и проверил, что порты 8123 и 9000 открыты.
  • Я выполнил DDL-скрипт и убедился, что таблица создана с ORDER BY (trace_id, timestamp).
  • Мой log_generator.py выдаёт строки в нужном формате и может сгенерировать миллион строк менее чем за 2 минуты.
  • Я измерил поиск по одному trace_id – время выполнения меньше 50 мс.
  • Нагрузочный тест показал throughput не ниже 1 млн/сек (например: 1,050,000 rows in 1.04 sec).
  • В Grafana я добавил источник данных ClickHouse и создал хотя бы одну панель.
  • Все файлы проекта сохранены в Git с осмысленными commit’ами.