Как записывать логи в ClickHouse: схема и минимальный pipeline
Для централизованного сбора и анализа логов часто используют связку Vector и ClickHouse. Vector принимает события из разных источников, нормализует их и отправляет в ClickHouse пакетами. ClickHouse хранит логи в таблицах семейства MergeTree, позволяя быстро выполнять аналитические запросы.
Минимальный pipeline состоит из трех компонентов: source, transform и sink. Source читает логи из файла, journald или stdin. Transform разбирает сообщения и приводит поля к нужным типам. Sink ClickHouse формирует INSERT-запросы и отправляет их на HTTP-интерфейс сервера.
Пример минимальной конфигурации Vector:
[sources.app_logs]
type = "file"
include = ["/var/log/app/*.log"]
[transforms.parse_logs]
type = "remap"
inputs = ["app_logs"]
source = '''
. = parse_json!(.message)
.timestamp = parse_timestamp!(.timestamp, "%Y-%m-%dT%H:%M:%S%.fZ")
'''
[sinks.clickhouse]
type = "clickhouse"
inputs = ["parse_logs"]
endpoint = "http://clickhouse:8123"
database = "logs"
table = "app_events"
auth.strategy = "basic"
auth.user = "vector"
auth.password = "${CLICKHOUSE_PASSWORD}"
skip_unknown_fields = trueЗамените endpoint, database, table и учетные данные на свои. Перед запуском создайте таблицу в ClickHouse, иначе события будут отклонены.
Что должно быть готово перед настройкой
Проверьте, что у вас установлен Vector версии 0.31 или новее, доступен ClickHouse 22.8+ и создана база данных. Пользователь Vector должен иметь права INSERT на целевую таблицу и SELECT на системные таблицы для проверки схемы.
Убедитесь, что Vector может подключиться к HTTP-порту ClickHouse (обычно 8123). Если используется HTTPS, настройте TLS. Подготовьте пример лог-события, чтобы проверить парсинг и типы полей.
Минимальная конфигурация доставки
В конфигурации выше source file читает все файлы по шаблону. Transform remap разбирает JSON из поля message и преобразует timestamp в DateTime64. Sink clickhouse отправляет события в таблицу app_events, пропуская неизвестные поля.
Ключевые параметры sink: endpoint, database, table, auth. Формат по умолчанию JSONEachRow, имена полей должны совпадать со столбцами таблицы. Если поле отсутствует в таблице, оно будет пропущено при skip_unknown_fields=true.
Подготовка ClickHouse: таблица для логов и схема данных
Правильная схема таблицы предотвращает большинство ошибок вставки и ускоряет запросы. Минимальный набор полей: время события, уровень, источник, сообщение, хост и идентификатор запроса.
Какие поля хранить в событии
Разделите поля на временные, идентификационные, классификационные и диагностические. Временные: timestamp (время события), ingested_at (время приема). Идентификационные: request_id, trace_id. Классификационные: level, source, service, host. Диагностические: message, raw, metadata.
Для фильтрации и агрегаций чаще всего используются timestamp, level, source и host. Поэтому их стоит вынести в отдельные столбцы с подходящими типами.
Типы данных и преобразование перед INSERT
Используйте DateTime64(3) для timestamp с миллисекундной точностью. Для повторяющихся строковых значений, таких как level или source, подойдет LowCardinality(String). Числовые коды ответов храните в UInt16 или Int32. Необязательные поля объявляйте как Nullable.
Не передавайте timestamp в виде строки, если столбец ожидает DateTime64. Это вызовет ошибку TYPE_MISMATCH. Преобразуйте время в Vector с помощью parse_timestamp.
Engine, PARTITION BY и ORDER BY
Используйте MergeTree. Партиционируйте по дате события: PARTITION BY toYYYYMM(timestamp) для месячных партиций или toYYYYMMDD(timestamp) для дневных. Ключ сортировки должен соответствовать частым условиям WHERE: ORDER BY (timestamp, service, level).
Пример DDL:
CREATE TABLE logs.app_events (
timestamp DateTime64(3),
ingested_at DateTime DEFAULT now(),
level LowCardinality(String),
source LowCardinality(String),
service LowCardinality(String),
host String,
message String,
request_id String DEFAULT '',
metadata String DEFAULT ''
)
ENGINE = MergeTree
PARTITION BY toYYYYMM(timestamp)
ORDER BY (timestamp, service, level)
TTL timestamp + INTERVAL 30 DAY;Такая таблица готова к приему данных из Vector.
Подключение источников логов к Vector
Vector поддерживает множество источников. Для файлов используйте source file, для journald - source journald, для Docker - source docker_logs. Настройте чтение с учетом multiline и позиции.
JSON-логи приложений и контейнеров
Если приложение пишет JSON, Vector может разобрать его напрямую. Пример события:
{"timestamp":"2026-09-07T12:34:56.789Z","level":"info","message":"User login","service":"auth","host":"web-01","request_id":"abc123"}В transform remap используйте parse_json для всего сообщения, затем переименуйте поля при необходимости. Вложенные объекты можно извлечь через точку: .user.id.
Текстовые логи и syslog
Для неструктурированных логов используйте регулярные выражения или специализированные парсеры. Например, для syslog-формата:
. = parse_syslog!(.message)
.timestamp = parse_timestamp!(.timestamp, "%Y-%m-%dT%H:%M:%S%.fZ")Учитывайте разные форматы дат и возможные multiline stack trace. Настройте source file с параметром multiline.
Преобразование логов в Vector с помощью VRL
VRL (Vector Remap Language) позволяет манипулировать событиями. Основные операции: parse_json, parse_timestamp, переименование полей, условная логика.
Разбор JSON и выделение ключевых полей
Пример transform:
[transforms.normalize]
type = "remap"
inputs = ["app_logs"]
source = '''
. = parse_json!(.message)
.timestamp = parse_timestamp!(.timestamp, "%Y-%m-%dT%H:%M:%S%.fZ")
.level = downcase!(string!(.level))
.source = .service
del(.service)
'''Сохраните исходное сообщение в поле raw, если нужно диагностировать ошибки парсинга.
Преобразование timestamp в корректный тип времени
Используйте parse_timestamp с явным форматом. Для ISO 8601: "%Y-%m-%dT%H:%M:%S%.fZ". Если timestamp отсутствует или некорректен, подставьте время приема Vector: .timestamp = now().
Нормализация level, source и необязательных полей
Приведите level к нижнему регистру и ограниченному набору значений: info, warn, error, debug. Заполните source из имени сервиса или файла. Пустые строки замените на null, если столбец Nullable.
Настройка Vector sink ClickHouse
Sink ClickHouse отправляет события через HTTP-интерфейс. Основные параметры: endpoint, database, table, auth, encoding, batch, buffer.
Endpoint, база, таблица и авторизация
Endpoint указывает на HTTP-порт ClickHouse, обычно http://clickhouse:8123. Для HTTPS используйте https:// и настройте TLS. Авторизация может быть basic или через заголовки. Рекомендуется хранить пароль в переменных окружения.
Создайте отдельного пользователя с правами только на нужную базу:
CREATE USER vector IDENTIFIED WITH sha256_password BY 'strong_password';
GRANT INSERT ON logs.app_events TO vector;Формат отправляемых строк и соответствие схеме
По умолчанию Vector использует формат JSONEachRow. Имена полей в событии должны совпадать со столбцами таблицы. Если поле лишнее, оно будет пропущено при skip_unknown_fields=true. Если поле отсутствует, ClickHouse использует значение по умолчанию или NULL, если столбец Nullable.
Обработка ошибок отправки
При временной недоступности ClickHouse Vector выполняет повторные попытки с экспоненциальной задержкой. Настройте request.retry_attempts и request.retry_max_duration_secs. Если ошибка связана с неверной схемой, повторы не помогут, нужно исправить данные или таблицу.
Батчинг и буферизация: как снизить число мелких вставок
ClickHouse эффективнее обрабатывает пакеты, чем одиночные INSERT. Vector по умолчанию буферизует события и отправляет их батчами.
Настройка размера и времени batch
Параметры batch.max_events, batch.max_bytes и batch.timeout_secs управляют размером пакета. Например:
[sinks.clickhouse.batch]
max_events = 1000
max_bytes = 10485760 # 10 MB
timeout_secs = 1Пакет отправляется, когда достигнут любой из лимитов. Это балансирует задержку и пропускную способность.
Памятный и дисковый buffer
Буфер может быть в памяти или на диске. Memory buffer быстрее, но теряет данные при сбое. Disk buffer надежнее, но требует места и прав на запись. Настройте buffer.type = "disk" и buffer.max_size для ограничения использования диска.
Задержка доставки, повторы и дубликаты
Большие батчи увеличивают задержку, но снижают нагрузку. Повторы после сетевых ошибок могут привести к дубликатам. Учитывайте это в аналитических запросах, используя функции для дедупликации.
Партиционирование, поиск и TTL в таблице логов
Партиционирование и ключ сортировки определяют скорость запросов и управление хранением.
Как выбрать партицию по времени
Для логов с объемом до 100 ГБ в месяц достаточно месячных партиций. Дневные партиции полезны, если нужно часто удалять старые данные. Избегайте партиционирования по высококардинальным полям.
ORDER BY для типовых запросов к логам
Ключ сортировки должен начинаться с timestamp, затем service, level. Это ускоряет фильтрацию по времени и сервису. Пример запроса:
SELECT count() FROM logs.app_events
WHERE timestamp >= now() - INTERVAL 1 HOUR
AND service = 'auth'
AND level = 'error';TTL и политика хранения
TTL автоматически удаляет старые данные. Укажите TTL timestamp + INTERVAL 30 DAY в определении таблицы. Проверяйте фактическое удаление с помощью system.parts. Учитывайте требования аудита при выборе срока хранения.
Проверка доставки и диагностика типовых ошибок
После настройки проверьте, что события доходят до ClickHouse.
Проверка конфигурации и тестового события
Запустите Vector с флагом --config-toml для проверки синтаксиса. Затем отправьте тестовое событие через stdin и проверьте логи Vector. Убедитесь, что timestamp и другие поля имеют ожидаемые типы.
Ошибки timestamp и типов данных
Частые ошибки: неверный формат даты, отсутствие часового пояса, передача null в непустой столбец, числовое значение как строка. Исправляйте данные в transform VRL.
Таймауты, переполнение буфера и недоступный ClickHouse
Смотрите метрики Vector: processed_events_total, sent_events_total, buffer_events. При росте буфера проверьте доступность ClickHouse, права пользователя, лимиты на вставку.
Контроль полноты и задержки доставки
Сравните количество событий на источнике и в ClickHouse за интервал. Проверьте максимальный timestamp в таблице и задержку относительно текущего времени. Используйте запрос:
SELECT max(timestamp), now() - max(timestamp) AS lag
FROM logs.app_events;Production-чеклист для эксплуатации Vector и ClickHouse
Перед запуском в production выполните следующие шаги.
Безопасность подключения и права
Используйте отдельного пользователя с минимальными правами. Храните пароль в переменной окружения или секрете. Включите TLS при передаче по открытым сетям. Ограничьте сетевой доступ к ClickHouse.
Наблюдаемость и регламент проверки
Настройте мониторинг метрик Vector: скорость входящих событий, успешные и неуспешные отправки, размер буфера, retries. Периодически проверяйте объем таблицы и эффективность TTL.
Дополнительно изучите настройку централизованного сбора логов в S3 и оптимизацию хранения логов.