Любому решению для обсервабилити нужен механизм для сбора и экспорта журналов и трассировок. Для этого ClickHouse рекомендует проект OpenTelemetry (OTel).
“OpenTelemetry — это фреймворк и набор инструментов для обсервабилити, предназначенный для создания и управления телеметрическими данными, такими как трассировки, метрики и журналы.”
В отличие от ClickHouse или Prometheus, OpenTelemetry не является бэкендом для обсервабилити, а вместо этого сосредоточен на генерации, сборе, управлении и экспорте телеметрических данных. Хотя изначально OpenTelemetry был задуман для того, чтобы упростить инструментирование приложений и систем с помощью SDK для конкретных языков, со временем проект расширился и теперь также включает сбор журналов через OpenTelemetry Collector — агент или прокси, который принимает, обрабатывает и экспортирует телеметрические данные.
Компоненты ClickHouse, имеющие отношение к OpenTelemetry
OpenTelemetry включает ряд компонентов. Помимо спецификации данных и API, стандартизированного протокола и соглашений об именовании полей/столбцов, OTel предоставляет две возможности, которые имеют ключевое значение для построения решения обсервабилити на базе ClickHouse:
- OpenTelemetry Collector — это прокси-компонент, который принимает, обрабатывает и экспортирует данные телеметрии. В решениях на базе ClickHouse этот компонент используется как для сбора логов, так и для обработки событий перед батчингом и вставкой.
- SDK для языков программирования, реализующие спецификацию, API и экспорт данных телеметрии. Эти SDK обеспечивают корректную запись трассировок в коде приложения, создают входящие в них спаны и передают контекст между сервисами через метаданные, формируя тем самым распределённые трассировки и позволяя коррелировать спаны. Эти SDK дополняются экосистемой средств автоматической поддержки распространённых библиотек и фреймворков, поэтому пользователю не нужно изменять свой код, и он получает инструментацию из коробки.
Решение обсервабилити на базе ClickHouse использует оба этих инструмента.
У OpenTelemetry Collector есть несколько дистрибутивов. Приёмник filelog вместе с экспортёром ClickHouse, необходимым для решения на базе ClickHouse, доступен только в дистрибутиве OpenTelemetry Collector Contrib.
Этот дистрибутив включает множество компонентов и позволяет экспериментировать с различными конфигурациями. Однако для production-среды рекомендуется ограничить состав коллектора только теми компонентами, которые действительно нужны в конкретной среде. Вот несколько причин сделать это:
- Уменьшить размер коллектора, что сокращает время его развертывания
- Повысить безопасность коллектора за счёт сокращения доступной поверхности атаки
Собрать кастомный коллектор можно с помощью OpenTelemetry Collector Builder.
Роли развертывания коллектора
Чтобы собирать журналы и выполнять их вставку в ClickHouse, мы рекомендуем использовать OpenTelemetry Collector. OpenTelemetry Collector можно развернуть в двух основных ролях:
- Агент - Экземпляры агента собирают данные на периферии, например на серверах или узлах Kubernetes, либо получают события напрямую от приложений, в которые встроен OpenTelemetry SDK. Во втором случае экземпляр агента запускается вместе с приложением или на том же хосте, что и приложение (например, как sidecar или ДемонСет). Агенты могут отправлять свои данные либо напрямую в ClickHouse, либо в экземпляр шлюза. В первом случае это называется моделью развертывания агента.
- Шлюз - Экземпляры шлюза предоставляют отдельный сервис (например, в виде Развертывания в Kubernetes), обычно на кластер, центр обработки данных или регион. Они получают события от приложений (или других коллекторов в роли агентов) через единую конечную точку OTLP. Обычно развертывают набор экземпляров шлюза, а для распределения нагрузки между ними используют стандартный балансировщик нагрузки. Если все агенты и приложения отправляют свои сигналы в эту единую конечную точку, это часто называется моделью развертывания шлюза.
Ниже мы предполагаем простой коллектор в роли агента, который отправляет свои события напрямую в ClickHouse. Дополнительные сведения об использовании шлюзов и о том, когда они уместны, см. в разделе Масштабирование с помощью шлюзов.
Основное преимущество использования коллектора в том, что он позволяет сервисам быстро выгружать данные, а всю дополнительную обработку — повторные попытки, батчинг, шифрование и даже фильтрацию конфиденциальных данных — берёт на себя сам коллектор.
Коллектор использует термины приёмник, процессор и экспортер для обозначения трёх основных этапов обработки. Приёмники используются для сбора данных и могут работать либо по модели pull, либо push. Процессоры позволяют выполнять преобразования и обогащение сообщений. Экспортеры отвечают за отправку данных в целевой сервис. Хотя теоретически таким сервисом может быть и другой коллектор, в дальнейшем мы исходим из того, что все данные отправляются напрямую в ClickHouse.
Мы рекомендуем ознакомиться с полным набором приёмников, процессоров и экспортеров.
Коллектор предоставляет два основных приёмника для сбора журналов:
Через OTLP - В этом случае журналы отправляются (push) напрямую в коллектор из OpenTelemetry SDK по протоколу OTLP. В демо OpenTelemetry используется именно этот подход: OTLP-экспортеры для каждого языка предполагают локальную конечную точку коллектора. В этом случае коллектор должен быть настроен с OTLP-приёмником — см. пример конфигурации в демо выше. Преимущество этого подхода в том, что данные журналов автоматически будут содержать Trace ID, что позволит впоследствии находить трассировки для конкретного журнала и наоборот.
Этот подход требует, чтобы пользователи инструментировали свой код с помощью подходящего SDK для своего языка.
- Сбор через ресивер filelog - Этот приёмник читает файлы журналов на диске, формирует сообщения журналов и отправляет их в ClickHouse. Он также решает более сложные задачи: определяет многострочные сообщения, обрабатывает ротацию журналов, сохраняет контрольные точки для устойчивости к перезапуску и извлекает структуру. Кроме того, этот приёмник может читать журналы контейнеров Docker и Kubernetes, разворачиваться как Helm-чарт, извлекать из них структуру и обогащать их сведениями о поде.
В большинстве развертываний используется комбинация указанных выше приёмников. Рекомендуем прочитать документацию по коллектору и ознакомиться с базовыми понятиями, а также со структурой конфигурации и способами установки.
Совет: otelbin.iootelbin.io удобно использовать для проверки и визуализации конфигураций.
Структурированные и неструктурированные
Журналы могут быть как структурированными, так и неструктурированными.
В структурированном журнале используется такой формат данных, как JSON, в котором определены поля метаданных, например код HTTP и IP-адрес источника.
Неструктурированные журналы, хотя обычно и обладают некоторой внутренней структурой, которую можно извлечь с помощью шаблона регулярного выражения, представляют журнал просто как строку.
Мы рекомендуем по возможности использовать структурированное логирование и записывать журналы в формате JSON (то есть ndjson). Это упростит дальнейшую обработку журналов: либо перед отправкой в ClickHouse с помощью процессоров Collector, либо во время вставки с использованием materialized views. В конечном итоге структурированные журналы помогут сэкономить ресурсы на последующей обработке и снизить требуемую загрузку CPU в вашем решении на базе ClickHouse.
В качестве примера мы предоставляем структурированный (JSON) и неструктурированный наборы журнальных данных, каждый примерно по 10 млн строк, доступные по следующим ссылкам:
В примере ниже используется структурированный набор данных. Чтобы воспроизвести следующие примеры, скачайте и распакуйте этот файл.
Ниже приведена простая конфигурация для OTel Collector, которая читает эти файлы с диска с помощью приёмника filelog и выводит полученные сообщения в stdout. Поскольку наши журналы структурированы, мы используем оператор json_parser. Измените путь к файлу access-structured.log.
Рассмотрите использование ClickHouse для разбораВ примере ниже из журнала извлекается временная метка. Для этого требуется оператор json_parser, который преобразует всю строку журнала в JSON-строку и помещает результат в LogAttributes. Это может быть ресурсоёмко, и в ClickHouse это можно сделать эффективнее — Извлечение структуры с помощью SQL. Эквивалентный пример для неструктурированных данных, в котором для этого используется regex_parser, можно найти здесь.
config-structured-logs.yaml
Вы можете воспользоваться официальными инструкциями для локальной установки OTel collector. Важно: убедитесь, что в инструкциях используется дистрибутив contrib (он содержит приёмник filelog); например, вместо otelcol_0.102.1_darwin_arm64.tar.gz нужно скачать otelcol-contrib_0.102.1_darwin_arm64.tar.gz. Релизы доступны здесь.
После установки OTel collector можно запустить следующими командами:
Если используются структурированные журналы, сообщения на выходе будут иметь следующий вид:
Выше показано одно сообщение лога в том виде, в котором его создаёт OTel collector. В последующих разделах мы выполняем приём этих же сообщений в ClickHouse.
Полная схема сообщений лога, а также дополнительные столбцы, которые могут присутствовать при использовании других приёмников, приведены здесь. Мы настоятельно рекомендуем пользователям ознакомиться с этой схемой.
Ключевой момент здесь в том, что сама строка лога хранится как строка в поле Body, а JSON автоматически извлекается в поле Attributes благодаря json_parser. Этот же оператор используется для извлечения временной метки в соответствующий столбец Timestamp. Рекомендации по обработке журналов с помощью OTel см. в разделе Обработка.
ОператорыОператоры — это базовая единица обработки журналов. Каждый оператор выполняет одну конкретную задачу, например читает строки из файла или разбирает JSON из поля. Затем операторы объединяются в конвейер, чтобы получить нужный результат.
В приведённых выше сообщениях нет поля TraceID или SpanID. Если они присутствуют, например когда пользователи реализуют распределённую трассировку, их можно извлечь из JSON теми же способами, что показаны выше.
Пользователям, которым нужно собирать локальные файлы журналов или файлы журналов Kubernetes, мы рекомендуем ознакомиться с доступными параметрами конфигурации приёмника filelog, а также с тем, как обрабатываются смещения и разбор многострочных журналов.
Для сбора журналов Kubernetes мы рекомендуем воспользоваться руководством из документации OpenTelemetry. Для обогащения журналов и метрик метаданными подов рекомендуется Kubernetes Attributes Processor. Это может приводить к появлению динамических метаданных, например меток, которые хранятся в столбце ResourceAttributes. В настоящее время ClickHouse использует для этого столбца тип Map(String, String). Подробнее о работе с этим типом и его оптимизации см. в разделах Using Maps и Extracting from maps.
Пользователям, которые хотят инструментировать свой код и собирать трассировки, мы рекомендуем обратиться к официальной документации OTel.
Чтобы доставлять события в ClickHouse, вам потребуется развернуть OTel collector, который будет принимать события трассировки по протоколу OTLP через соответствующий приёмник. В демо OpenTelemetry приведён пример инструментирования для каждого поддерживаемого языка и отправки событий в OTel collector. Ниже показан пример подходящей конфигурации OTel collector, которая выводит события в stdout:
Поскольку трассировки нужно принимать через OTLP, для генерации данных трассировок мы используем инструмент telemetrygen. Инструкции по установке приведены здесь.
Следующая конфигурация принимает события трассировок через OTLP-приёмник, а затем отправляет их в stdout.
config-traces.xml
Запустите эту конфигурацию командой:
Отправьте события трассировки в коллектор с помощью telemetrygen:
В результате в stdout будут выводиться сообщения trace, аналогичные примеру ниже:
Выше показано отдельное сообщение трассировки, которое формирует OTel collector. В следующих разделах мы организуем приём этих же сообщений в ClickHouse.
Полная схема сообщений трассировки доступна здесь. Мы настоятельно рекомендуем пользователям ознакомиться с этой схемой.
Как было показано в предыдущем примере с установкой временной метки для события журнала, вам почти наверняка потребуется фильтровать, преобразовывать и обогащать сообщения событий. Это можно сделать с помощью ряда возможностей OpenTelemetry:
-
Процессоры - Процессоры берут данные, собранные приёмниками, и изменяют или преобразуют их перед отправкой в экспортёры. Процессоры применяются в том порядке, в котором они указаны в разделе
processors конфигурации collector. Они необязательны, но обычно рекомендуется использовать минимальный набор. При использовании OTel collector с ClickHouse мы рекомендуем ограничиться следующими процессорами:
-
Операторы - Операторы представляют собой самую базовую единицу обработки, доступную на уровне приёмника. Поддерживается базовый парсинг, позволяющий задавать такие поля, как Severity и Timestamp. Здесь поддерживаются парсинг JSON и regex, а также фильтрация событий и базовые преобразования. Мы рекомендуем выполнять фильтрацию событий именно здесь.
Мы рекомендуем избегать избыточной обработки событий с помощью операторов или transform processors. Это может приводить к значительным накладным расходам по памяти и CPU, особенно при парсинге JSON. За некоторыми исключениями всю обработку можно выполнять в ClickHouse во время вставки с помощью materialized views и столбцов — в частности, исключением является обогащение с учётом контекста, например добавление метаданных k8s. Подробнее см. в разделе Извлечение структуры с помощью SQL.
Если обработка выполняется с помощью OTel collector, мы рекомендуем выполнять преобразования на экземплярах шлюза и сводить к минимуму любую работу на экземплярах агента. Это позволит сделать требования к ресурсам агентов на периферии, работающих на серверах, настолько низкими, насколько это возможно. Обычно пользователи выполняют в агентах только фильтрацию (чтобы минимизировать лишний сетевой трафик), установку временных меток (через операторы) и обогащение, требующее контекста. Например, если экземпляры шлюза находятся в другом кластере Kubernetes, обогащение k8s нужно будет выполнять в агенте.
Следующая конфигурация показывает сбор данных из неструктурированного файла журнала. Обратите внимание, что здесь используются операторы для извлечения структуры из строк журнала (regex_parser) и фильтрации событий, а также процессор для объединения событий в батчи и ограничения использования памяти.
config-unstructured-logs-with-processor.yaml
Экспортеры отправляют данные в одну или несколько целевых систем или пунктов назначения. Экспортеры могут работать по модели pull или push. Чтобы отправлять события в ClickHouse, нужно использовать push-ориентированный экспортер ClickHouse.
Ниже приведен полный файл конфигурации.
clickhouse-config.yaml
Обратите внимание на следующие важные настройки:
- pipelines - Приведённая выше конфигурация показывает использование конвейеров, состоящих из набора приёмников, процессоров и экспортеров, с отдельными конвейерами для журналов и трассировок.
- endpoint - Взаимодействие с ClickHouse настраивается через параметр
endpoint. Строка подключения tcp://localhost:9000?dial_timeout=10s&compress=lz4&async_insert=1 задаёт обмен данными по TCP. Если по соображениям переключения трафика вы предпочитаете HTTP, измените эту строку подключения, как описано здесь. Полные сведения о подключении, включая возможность указать имя пользователя и пароль в этой строке подключения, приведены здесь.
Важно: Обратите внимание, что приведённая выше строка подключения включает и сжатие (lz4), и асинхронные вставки. Мы рекомендуем всегда включать оба параметра. Дополнительные сведения об асинхронных вставках см. в разделе Batching. Сжатие следует указывать всегда, а в старых версиях экспортера оно по умолчанию может быть отключено.
- ttl - это значение определяет, как долго хранятся данные. Дополнительные сведения приведены в разделе “Управление данными”. Его следует задавать в часах, например 72h. В примере ниже мы отключаем TTL, поскольку наши данные относятся к 2019 году и ClickHouse немедленно удалит их после вставки.
- traces_table_name and logs_table_name - определяют имена таблиц для журналов и трассировок.
- create_schema - определяет, будут ли при запуске создаваться таблицы со схемами по умолчанию. Для начальной настройки по умолчанию используется true. Следует установить false и определить собственную схему.
- database - целевая база данных.
- retry_on_failure - настройки, определяющие, следует ли повторять отправку неудачных батчей.
- batch - пакетный процессор гарантирует, что события отправляются батчами. Мы рекомендуем значение не менее 10 000 и тайм-аут 5s (если позволяет память, можно использовать значения до 100 000). Как только будет достигнут любой из этих порогов, батч будет отправлен в экспортер. Уменьшение этих значений снижает задержку конвейера, и данные становятся доступны для запросов быстрее, но ценой большего числа подключений и батчей, отправляемых в ClickHouse. Это не рекомендуется, если вы не используете асинхронные вставки, так как это может вызвать проблему too many parts в ClickHouse. И наоборот, если вы используете асинхронные вставки, доступность этих данных для запросов также будет зависеть от настроек асинхронной вставки, хотя данные всё равно будут раньше отправляться из коннектора. Подробнее см. в разделе Batching.
- sending_queue - управляет размером очереди отправки. Каждый элемент очереди содержит батч. Если очередь будет переполнена, например из-за недоступности ClickHouse при продолжающем поступлении событий, батчи будут отброшены.
Предполагая, что пользователи уже извлекли структурированный файл журнала и у них запущен локальный экземпляр ClickHouse (со стандартной аутентификацией), вы можете запустить эту конфигурацию командой:
Чтобы отправить данные трассировки в этот коллектор, выполните следующую команду, используя инструмент telemetrygen:
После запуска убедитесь с помощью простого запроса, что события логов поступают:
ClickStack поставляется с оптимизированной схемой по умолчаниюClickStack предоставляет готовые схемы для журналов, трассировок и метрик, которые используют новейшие возможности ClickHouse (текстовые индексы для полнотекстового поиска и поиска по ключам Map, материализованные столбцы и ALIAS-массивы для фильтрации при прямом чтении, поиск строк по номеру блока) и по результатам бенчмарков обеспечивают высокую производительность «из коробки» для нагрузок журналирования и трассировки. Используйте их как отправную точку для собственной схемы.
По умолчанию экспортер ClickHouse создает целевые таблицы для журналов и трассировки. Это можно отключить с помощью настройки create_schema. Кроме того, имена таблиц для журналов и трассировки можно изменить со значений по умолчанию otel_logs и otel_traces с помощью настроек, указанных выше.
В схемах ниже предполагается, что TTL включен и равен 72h.
Ниже показана схема журналов по умолчанию (otelcol-contrib v0.102.1):
Приведённые здесь столбцы соответствуют официальной спецификации OTel для журналов, описанной здесь.
Несколько важных замечаний по этой схеме:
- По умолчанию таблица разбита на партиции по дате с помощью
PARTITION BY toDate(Timestamp). Это позволяет эффективно удалять данные после истечения срока их хранения.
- TTL задаётся через
TTL toDateTime(Timestamp) + toIntervalDay(3) и соответствует значению, заданному в конфигурации коллектора. ttl_only_drop_parts=1 означает, что удаляются только целые части, когда срок хранения истёк для всех содержащихся в них строк. Это эффективнее, чем удалять строки внутри частей, так как это требует дорогостоящей операции delete. Мы рекомендуем всегда включать этот параметр. Подробнее см. в разделе Управление данными с помощью TTL.
- В таблице используется классический движок
MergeTree. Он рекомендуется для журналов и трассировок, и менять его обычно не требуется.
- Таблица упорядочена по
ORDER BY (ServiceName, SeverityText, toUnixTimestamp(Timestamp), TraceId). Это означает, что запросы будут оптимизированы для фильтров по ServiceName, SeverityText, Timestamp и TraceId — столбцы, расположенные раньше в списке, фильтруются быстрее, чем более поздние; например, фильтрация по ServiceName будет значительно быстрее, чем по TraceId. Вам следует изменить этот порядок в соответствии с ожидаемыми шаблонами доступа — см. Выбор первичного ключа.
- В приведённой выше схеме к столбцам применяется
ZSTD(1). Это обеспечивает наилучшее сжатие для журналов. Вы можете увеличить уровень сжатия ZSTD (выше значения по умолчанию, равного 1) для лучшего сжатия, хотя на практике это редко даёт заметную пользу. Увеличение этого значения приведёт к большей нагрузке на CPU во время вставки (при сжатии), хотя распаковка (а значит, и запросы) должна остаться примерно на том же уровне. Дополнительные сведения см. здесь. Дополнительное delta-кодирование также применяется к Timestamp, чтобы уменьшить его размер на диске.
- Обратите внимание, что
ResourceAttributes, LogAttributes и ScopeAttributes имеют тип Map. Важно понимать различия между ними. О том, как обращаться к этим Map и оптимизировать доступ к ключам внутри них, см. в разделе “Использование maps”.
- Большинство остальных типов здесь, например
ServiceName как LowCardinality, уже оптимизированы. Обратите внимание, что Body, который в наших примерах логов представляет собой JSON, хранится как String.
- К ключам и значениям Map, а также к столбцу
Body применяются bloom-фильтры. Они помогают ускорить запросы, обращающиеся к этим столбцам, но обычно не являются обязательными. См. Вторичные индексы/индексы пропуска данных.
И здесь она будет коррелировать со столбцами, соответствующими официальной спецификации OTel для трасс, описанной здесь. В этой схеме используется многие из тех же настроек, что и в приведённой выше схеме журналов, а также дополнительные столбцы Link, специфичные для спанов.
Мы рекомендуем пользователям отключить автоматическое создание схем и создавать таблицы вручную. Это позволяет изменять основные и вторичные ключи, а также добавлять дополнительные столбцы для оптимизации производительности запросов. Подробнее см. в разделе Проектирование схемы.
Чтобы добиться высокой производительности вставки и при этом обеспечить строгие гарантии согласованности, при вставке данных обсервабилити в ClickHouse через OTel collector следует придерживаться простых правил. При правильной конфигурации OTel collector приведенные ниже правила будет легко соблюдать. Это также помогает избежать распространенных проблем, с которыми пользователи сталкиваются при первом знакомстве с ClickHouse.
По умолчанию каждая вставка, отправленная в ClickHouse, приводит к тому, что ClickHouse немедленно создаёт часть хранилища, содержащую данные этой вставки и другие метаданные, которые нужно сохранить. Поэтому если отправлять меньше вставок, но с большим объёмом данных в каждой, а не много вставок с меньшим объёмом данных, это уменьшит количество необходимых операций записи. Мы рекомендуем вставлять данные достаточно крупными батчами — не менее 1 000 строк за раз. Подробнее здесь.
По умолчанию вставки в ClickHouse синхронны и идемпотентны, если они идентичны. Для таблиц семейства движков MergeTree ClickHouse по умолчанию автоматически выполняет дедупликацию вставок. Это означает, что вставки устойчивы к следующим ситуациям:
- (1) Если на узле, принимающем данные, возникают проблемы, запрос на вставку завершится по тайм-ауту (или вернёт более конкретную ошибку), и подтверждение не будет получено.
- (2) Если узел записал данные, но не может вернуть подтверждение отправителю запроса из-за проблем с сетью, отправитель либо получит тайм-аут, либо ошибку сети.
С точки зрения коллектора случаи (1) и (2) бывает трудно различить. Однако в обоих случаях вставку, для которой не было получено подтверждение, можно сразу повторить. Если повторный запрос на вставку содержит те же данные в том же порядке, ClickHouse автоматически проигнорирует повторную вставку, если исходная вставка (без подтверждения) была успешной.
Мы рекомендуем использовать пакетный процессор, показанный в предыдущих конфигурациях, чтобы выполнить эти требования. Это гарантирует, что вставки отправляются в виде согласованных батчей строк, соответствующих указанным выше условиям. Если от коллектора ожидается высокая пропускная способность (событий в секунду) и в каждой вставке можно отправлять не менее 10 000 событий, то обычно этого батчинга в конвейере достаточно. Если позволяет память, можно использовать значения до 100 000. В этом случае коллектор будет сбрасывать батчи до достижения timeout пакетного процессора, что обеспечит низкую сквозную задержку конвейера и стабильный размер батчей.
Использование асинхронных вставок
Обычно при низкой пропускной способности коллектора пользователям приходится отправлять меньшие батчи, но при этом они всё равно ожидают, что данные будут поступать в ClickHouse с минимальной сквозной задержкой. В таком случае небольшие батчи отправляются по истечении timeout пакетного процессора. Это может приводить к проблемам — именно в таких ситуациях и нужны асинхронные вставки. Обычно это происходит, когда коллекторы в роли агента настроены на отправку данных напрямую в ClickHouse. Шлюзы, выступая в роли агрегаторов, могут смягчить эту проблему — см. Масштабирование с помощью шлюзов.
Если нельзя гарантировать большие батчи, можно делегировать батчинг ClickHouse, используя асинхронные вставки. При асинхронных вставках данные сначала помещаются в буфер, а затем позже, то есть асинхронно, записываются в хранилище базы данных.
При включенных асинхронных вставках, когда ClickHouse ① получает запрос на вставку, данные запроса ② сразу записываются во внутренний буфер в памяти. Когда ③ происходит следующий сброс буфера, данные из буфера сортируются и записываются как часть в хранилище базы данных. Обратите внимание, что до сброса в хранилище базы данных эти данные недоступны для поиска запросами; сброс буфера настраивается.
Чтобы включить асинхронные вставки для коллектора, добавьте async_insert=1 в строку подключения. Мы рекомендуем использовать wait_for_async_insert=1 (значение по умолчанию), чтобы получить гарантии доставки — дополнительные сведения см. здесь.
Данные из async insert записываются после сброса буфера ClickHouse. Это происходит либо после превышения async_insert_max_data_size, либо через async_insert_busy_timeout_ms миллисекунд с момента первого запроса INSERT. Если для async_insert_stale_timeout_ms установлено ненулевое значение, данные записываются через async_insert_stale_timeout_ms milliseconds с момента последнего запроса. Эти параметры можно настроить, чтобы управлять сквозной задержкой конвейера. Дополнительные параметры для настройки сброса буфера описаны здесь. Обычно значений по умолчанию достаточно.
Рассмотрите адаптивные асинхронные вставкиВ случаях, когда используется небольшое количество агентов, при низкой пропускной способности, но строгих требованиях к сквозной задержке, могут быть полезны адаптивные асинхронные вставки. Однако обычно они не подходят для сценариев обсервабилити с высокой пропускной способностью, характерных для ClickHouse.
Наконец, прежнее поведение дедупликации, связанное с синхронными вставками в ClickHouse, по умолчанию не включено при использовании асинхронных вставок. При необходимости см. параметр async_insert_deduplicate.
Полные сведения о настройке этой возможности можно найти здесь, а более подробный разбор — здесь.
Архитектуры развертывания
При использовании OTel collector с ClickHouse возможны несколько архитектур развертывания. Ниже мы описываем каждую из них и указываем, в каких случаях она обычно применяется.
В архитектуре только с агентами пользователи разворачивают OTel collector на периферии в роли агентов. Они получают трассировки от локальных приложений (например, из sidecar-контейнера) и собирают журналы с серверов и узлов Kubernetes. В этом режиме агенты отправляют данные напрямую в ClickHouse.
Эта архитектура подходит для небольших и средних развертываний. Ее главное преимущество в том, что она не требует дополнительного оборудования и позволяет свести к минимуму общее потребление ресурсов решением ClickHouse для обсервабилити, сохраняя простое соответствие между приложениями и коллекторами.
Стоит рассмотреть переход на архитектуру на основе шлюза, когда число агентов превысит несколько сотен. У этой архитектуры есть несколько недостатков, из-за которых ее сложно масштабировать:
- Масштабирование соединений - Каждый агент будет устанавливать соединение с ClickHouse. Хотя ClickHouse способен поддерживать сотни, а то и тысячи одновременных соединений для вставки, со временем это станет ограничивающим фактором и сделает вставки менее эффективными — то есть ClickHouse будет тратить больше ресурсов на поддержание соединений. Использование шлюзов уменьшает количество соединений и повышает эффективность вставок.
- Обработка на периферии - В этой архитектуре любые преобразования или обработка событий должны выполняться либо на периферии, либо в ClickHouse. Это не только накладывает ограничения, но и может означать либо сложные materialized view в ClickHouse, либо перенос значительной вычислительной нагрузки на периферию, где ресурсы ограничены и могут пострадать критически важные сервисы.
- Маленькие батчи и задержки - Коллекторы-агенты могут по отдельности собирать очень мало событий. Обычно это означает, что их нужно настроить на сброс данных через заданный интервал, чтобы соблюдать SLA доставки. В результате collector может отправлять в ClickHouse маленькие батчи. Хотя это и является недостатком, его можно смягчить с помощью асинхронных вставок — см. Оптимизация вставок.
Масштабирование со шлюзами
OTel коллекторы можно развернуть в роли шлюзов, чтобы устранить перечисленные выше ограничения. Они предоставляют автономный сервис, как правило, на каждый центр обработки данных или регион. Они получают события от приложений (или других коллекторов в роли агента) через единую конечную точку OTLP. Обычно развертывается набор экземпляров шлюза, а для распределения нагрузки между ними используется стандартный балансировщик нагрузки.
Цель этой архитектуры — снять с агентов ресурсоёмкую вычислительную обработку и тем самым минимизировать потребление ими ресурсов. Эти шлюзы могут выполнять задачи преобразования, которые в противном случае пришлось бы выполнять агентам. Кроме того, агрегируя события от множества агентов, шлюзы могут обеспечивать отправку в ClickHouse крупных батчей, что позволяет выполнять эффективную вставку. Эти коллекторы в роли шлюза можно легко масштабировать по мере добавления новых агентов и роста пропускной способности потока событий. Ниже показан пример конфигурации шлюза вместе со связанной конфигурацией агента, которая использует пример структурированного файла журнала. Обратите внимание на использование OTLP для взаимодействия между агентом и шлюзом.
clickhouse-agent-config.yaml
clickhouse-gateway-config.yaml
Эти конфигурации можно запустить с помощью следующих команд.
Основной недостаток этой архитектуры — затраты и операционные издержки, связанные с управлением набором коллекторов.
Если вам нужен пример управления более крупными архитектурами на базе шлюзов и связанных с этим практических выводов, рекомендуем этот пост в блоге.
Читатели могли заметить, что в приведённых выше архитектурах Kafka не используется как очередь сообщений.
Использование Kafka в качестве буфера сообщений — популярный архитектурный паттерн в системах логирования, получивший широкое распространение благодаря стеку ELK. У него есть несколько преимуществ: прежде всего, он позволяет обеспечить более строгие гарантии доставки сообщений и помогает справляться с backpressure. Сообщения отправляются от агентов сбора в Kafka и записываются на диск. Теоретически кластер Kafka должен обеспечивать буфер сообщений с высокой пропускной способностью, поскольку линейная запись данных на диск требует меньше вычислительных ресурсов, чем разбор и обработка сообщения — например, в Elastic токенизация и индексирование создают значительные накладные расходы. Если вынести данные с агентов, снижается и риск потери сообщений из-за ротации журналов в источнике. Наконец, Kafka даёт возможности повторного воспроизведения сообщений и межрегиональной репликации, что может быть полезно в некоторых сценариях.
Однако ClickHouse может выполнять вставку данных очень быстро — миллионы строк в секунду даже на оборудовании среднего уровня. Backpressure со стороны ClickHouse возникает редко. Во многих случаях использование Kafka означает лишь дополнительную архитектурную сложность и затраты. Если вы готовы исходить из того, что журналам не нужны такие же гарантии доставки, как банковским транзакциям и другим критически важным данным, мы рекомендуем избегать лишней сложности, связанной с Kafka.
Тем не менее, если вам нужны высокие гарантии доставки или возможность повторного воспроизведения данных (возможно, в несколько источников), Kafka может стать полезным дополнением к архитектуре.
В этом случае агентов OTel можно настроить на отправку данных в Kafka через экспортёр Kafka. Экземпляры шлюза, в свою очередь, получают сообщения с помощью приёмника Kafka. За дополнительной информацией рекомендуем обратиться к документации Confluent и OTel.
Требования к ресурсам для OTel collector зависят от пропускной способности по событиям, размера сообщений и объема выполняемой обработки. Проект OpenTelemetry поддерживает бенчмарки, которые можно использовать для оценки требуемых ресурсов.
По нашему опыту, экземпляр шлюза с 3 ядрами и 12 ГБ оперативной памяти может обрабатывать около 60 тыс. событий в секунду. Это предполагает минимальный конвейер обработки, отвечающий за переименование полей и не использующий регулярные выражения.
Для экземпляров agent, которые отправляют события на шлюз и только устанавливают временную метку события, мы рекомендуем подбирать размер исходя из ожидаемого количества журналов в секунду. Ниже приведены примерные значения, которые можно использовать в качестве отправной точки:
Последнее изменение 23 июля 2026 г.