Componentes relevantes de ClickHouse
- El OpenTelemetry Collector es un proxy que recibe, procesa y exporta datos de telemetría. Una solución basada en ClickHouse utiliza este componente tanto para la recopilación de logs como para el procesamiento de eventos antes de agruparlos en lotes e insertarlos.
- Los SDK para distintos lenguajes implementan la especificación, las API y la exportación de datos de telemetría. En la práctica, estos SDK garantizan que las trazas se registren correctamente en el código de una aplicación, generando los spans que las componen y asegurando que el contexto se propague entre servicios mediante metadatos; de este modo, se crean trazas distribuidas y se garantiza que los spans puedan correlacionarse. Estos SDK se complementan con un ecosistema que instrumenta automáticamente bibliotecas y frameworks comunes, por lo que el usuario no necesita modificar su código y obtiene instrumentación lista para usar.
Distribuciones
- Reducir el tamaño del colector, lo que acorta los tiempos de implementación
- Mejorar la seguridad del colector al reducir la superficie de ataque disponible
Ingesta de datos con OTel
Roles de despliegue del colector
- Agent - Las instancias de Agent recopilan datos en el extremo, por ejemplo, en servidores o en nodos de Kubernetes, o reciben eventos directamente de aplicaciones instrumentadas con un SDK de OpenTelemetry. En este último caso, la instancia de Agent se ejecuta junto con la aplicación o en el mismo host que la aplicación (como un sidecar o un conjunto de daemon). Los agentes pueden enviar sus datos directamente a ClickHouse o a una instancia de gateway. En el primer caso, esto se conoce como patrón de despliegue Agent.
- Gateway - Las instancias de Gateway proporcionan un servicio independiente (por ejemplo, un despliegue en Kubernetes), normalmente por clúster, centro de datos o región. Estas reciben eventos de aplicaciones (u otros colectores que actúan como agentes) a través de un único endpoint OTLP. Normalmente, se despliega un conjunto de instancias de gateway, con un balanceador de carga listo para usar que distribuye la carga entre ellas. Si todos los agentes y las aplicaciones envían sus señales a este único endpoint, suele denominarse patrón de despliegue Gateway.
Recopilación de logs
- Scraping mediante el filelog receiver - Este receiver sigue archivos en disco y genera mensajes de log que luego envía a ClickHouse. También se encarga de tareas complejas, como detectar mensajes multilínea, gestionar la rotación de logs, crear puntos de control para resistir reinicios y extraer estructura. Además, este receiver también puede seguir logs de contenedores de Docker y Kubernetes, desplegado como un gráfico de Helm, extraer su estructura y enriquecerlos con los detalles del pod de Kubernetes.
Consejo:
otelbin.iootelbin.io es útil para validar y visualizar configuraciones.Estructurados vs. no estructurados
Ejemplo
json_parser porque nuestros logs están estructurados. Modifique la ruta al archivo access-structured.log.
Considere usar ClickHouse para el análisisEl ejemplo siguiente extrae el timestamp del log. Esto requiere el uso del operator
json_parser, que convierte toda la línea del log en una cadena JSON y coloca el resultado en LogAttributes. Esto puede ser costoso desde el punto de vista computacional y puede hacerse de forma más eficiente en ClickHouse: Extracción de estructura con SQL. Puede encontrar aquí un ejemplo equivalente no estructurado que usa regex_parser para lograrlo.filelog); por ejemplo, en lugar de otelcol_0.102.1_darwin_arm64.tar.gz, los usuarios descargarían otelcol-contrib_0.102.1_darwin_arm64.tar.gz. Las versiones están disponibles aquí.
Una vez instalado, el OTel collector puede ejecutarse con los siguientes comandos:
Body, pero el JSON se ha extraído automáticamente al campo Attributes gracias a json_parser. Este mismo operator se ha utilizado para extraer la marca de tiempo a la columna Timestamp correspondiente. Para ver recomendaciones sobre cómo procesar logs con OTel, consulte Procesamiento.
OperadoresLos operadores son la unidad más básica del procesamiento de logs. Cada operador cumple una única función, como leer líneas de un archivo o analizar JSON de un campo. Después, los operadores se encadenan en un pipeline para lograr el resultado deseado.
TraceID ni SpanID. Si están presentes, p. ej., en casos en los que los usuarios estén implementando distributed tracing, podrían extraerse del JSON usando las mismas técnicas mostradas anteriormente.
Para los usuarios que necesitan recopilar archivos de log locales o de Kubernetes, recomendamos familiarizarse con las opciones de configuración disponibles para el filelog receiver, así como con la forma en que se gestionan los offsets y el análisis de logs multilínea.
Recopilación de logs de Kubernetes
ResourceAttributes. Actualmente, ClickHouse utiliza el tipo Map(String, String) para esta columna. Consulta Using Maps y Extracting from maps para obtener más información sobre cómo manejar y optimizar este tipo.
Recopilación de trazas
Ejemplo
telemetrygen para generar datos de trazas. Siga las instrucciones aquí para instalarla.
La siguiente configuración recibe eventos de trazas en un receiver OTLP antes de enviarlos a stdout.
config-traces.xml
telemetrygen:
Procesamiento: filtrado, transformación y enriquecimiento
-
Procesadores: los procesadores toman los datos recopilados por los receivers y los modifican o transforman antes de enviarlos a los exportadores. Los procesadores se aplican en el orden en que están configurados en la sección
processorsde la configuración del collector. Son opcionales, pero normalmente se recomienda el conjunto mínimo. Al usar un OTel collector con ClickHouse, recomendamos limitar los procesadores a:- Un memory_limiter se usa para evitar situaciones de falta de memoria en el collector. Consulte Estimating Resources para ver recomendaciones.
- Cualquier processor que realice enriquecimiento basado en contexto. Por ejemplo, el Kubernetes Attributes Processor permite establecer automáticamente atributos de recursos en spans, métricas y logs con metadatos de k8s; por ejemplo, enriquecer eventos con el id de su pod de origen.
- Tail or head sampling si es necesario para traces.
- Filtrado básico: descarte de eventos que no se necesitan si esto no puede hacerse mediante un operator (véase más abajo).
- Batching: esencial al trabajar con ClickHouse para garantizar que los datos se envíen en batches. Consulte “Exporting to ClickHouse”.
- Operators: los Operators proporcionan la unidad de procesamiento más básica disponible en el receiver. Se admite parsing básico, lo que permite establecer campos como Severity y Timestamp. Aquí se admite parsing de JSON y regex, junto con el filtrado de eventos y transformaciones básicas. Recomendamos realizar aquí el filtrado de eventos.
Ejemplo
regex_parser) y filtrar eventos, junto con un processor para agrupar eventos en lotes y limitar el uso de memoria.
config-unstructured-logs-with-processor.yaml
Exportación a ClickHouse
Utilice OpenTelemetry Collector ContribEl exportador de ClickHouse forma parte de OpenTelemetry Collector Contrib, no de la distribución principal. Puede usar la distribución contrib o compilar su propio collector.
- pipelines - La configuración anterior destaca el uso de pipelines, compuestas por un conjunto de receptores, procesadores y exportadores, con una para logs y trazas.
- endpoint - La comunicación con ClickHouse se configura mediante el parámetro
endpoint. La cadena de conexióntcp://localhost:9000?dial_timeout=10s&compress=lz4&async_insert=1hace que la comunicación se realice a través de TCP. Si prefieres HTTP por motivos de conmutación de tráfico, modifica esta cadena de conexión como se describe aquí. Los detalles completos de la conexión, incluida la posibilidad de especificar un nombre de usuario y una contraseña dentro de esta cadena de conexión, se describen aquí.
- ttl - el valor aquí determina durante cuánto tiempo se conservan los datos. Más detalles en “Gestión de datos”. Debe especificarse como una unidad de tiempo en horas, por ejemplo, 72h. Deshabilitamos TTL en el ejemplo siguiente, ya que nuestros datos son de 2019 y ClickHouse los eliminará inmediatamente si se insertan.
- traces_table_name y logs_table_name - determinan el nombre de las tablas de logs y trazas.
- create_schema - determina si las tablas se crean con los esquemas predeterminados al iniciar. El valor predeterminado es true para empezar. Debes establecerlo en false y definir el esquema manualmente.
- database - database de destino.
- retry_on_failure - ajustes para determinar si deben reintentarse los batches fallidos.
- batch - un batch processor garantiza que los eventos se envíen en batches. Recomendamos un valor de al menos 10,000 con un timeout de 5s (pueden usarse valores de hasta 100,000 si la memoria lo permite). Lo que se alcance primero iniciará un batch para volcarlo al exportador. Reducir estos valores implicará una pipeline de menor latencia, con datos disponibles antes para consulta, a costa de más conexiones y batches enviados a ClickHouse. Esto no se recomienda si no estás usando asynchronous inserts, ya que puede causar problemas de Too many parts en ClickHouse. Por el contrario, si estás usando inserciones asíncronas, la disponibilidad de estos datos para consulta también dependerá de la configuración de inserción asíncrona, aunque los datos seguirán volcándose antes desde el conector. Consulta Batching para más detalles.
- sending_queue - controla el tamaño de la cola de envío. Cada elemento de la cola contiene un batch. Si se supera esta cola, por ejemplo, porque ClickHouse no está accesible pero los eventos siguen llegando, los batches se descartarán.
telemetrygen:
Esquema predeterminado
create_schema. Además, los nombres de las tablas de logs y trazas pueden cambiarse con respecto a sus valores predeterminados, otel_logs y otel_traces, mediante las configuraciones indicadas anteriormente.
En los esquemas siguientes, asumimos que TTL está habilitado con 72h.
otelcol-contrib v0.102.1):
- De forma predeterminada, la tabla está particionada por fecha mediante
PARTITION BY toDate(Timestamp). Esto permite eliminar con eficiencia los datos que caducan. - El TTL se establece mediante
TTL toDateTime(Timestamp) + toIntervalDay(3)y corresponde al valor definido en la configuración del collector.ttl_only_drop_parts=1significa que solo se eliminan partes completas cuando todas las filas que contienen han caducado. Esto es más eficiente que eliminar filas dentro de las partes, lo que implica una operación de borrado costosa. Recomendamos tenerlo siempre configurado así. Consulta Data management with TTL para más detalles. - La tabla utiliza el motor clásico
MergeTreemotor. Se recomienda para logs y trazas, y no debería ser necesario cambiarlo. - La tabla está ordenada por
ORDER BY (ServiceName, SeverityText, toUnixTimestamp(Timestamp), TraceId). Esto significa que las consultas se optimizarán para filtros sobreServiceName,SeverityText,TimestampyTraceId: las columnas que aparecen antes en la lista se filtrarán más rápido que las posteriores; por ejemplo, filtrar porServiceNameserá significativamente más rápido que filtrar porTraceId. Debes modificar este orden según los patrones de acceso previstos; consulta Choosing a primary key. - El esquema anterior aplica
ZSTD(1)a las columnas. Esto ofrece la mejor compresión para logs. Puedes aumentar el nivel de compresión de ZSTD (por encima del valor predeterminado de 1) para obtener una mejor compresión, aunque rara vez resulta beneficioso. Aumentar este valor implicará una mayor sobrecarga de CPU en el momento de la inserción (durante la compresión), aunque la descompresión (y, por tanto, las consultas) debería seguir siendo comparable. Consulta aquí para más detalles. También se aplica codificación delta adicional aTimestampcon el objetivo de reducir su tamaño en disco. - Observa que
ResourceAttributes,LogAttributesyScopeAttributesson mapas. Es importante comprender las diferencias entre ellos. Consulta “Using maps” para ver cómo acceder a estos mapas y optimizar el acceso a sus claves. - La mayoría de los demás tipos aquí, por ejemplo
ServiceNamecomo LowCardinality, ya están optimizados. Ten en cuenta queBody, que es JSON en nuestros logs de ejemplo, se almacena como un String. - Se aplican bloom filters a las claves y los valores de los mapas, así como a la columna
Body. Su objetivo es mejorar los tiempos de consulta al acceder a estas columnas, pero por lo general no son necesarios. Consulta Secondary/Data skipping indices.
Optimización de las inserciones
Agrupación por lotes
- (1) Si el nodo que recibe los datos tiene problemas, la consulta de inserción agotará el tiempo de espera (o devolverá un error más específico) y no se recibirá ninguna confirmación.
- (2) Si el nodo escribió los datos, pero la confirmación no puede devolverse al remitente de la consulta debido a interrupciones de red, el remitente recibirá un timeout o un error de red.
timeout del batch processor, lo que garantiza que la latencia de extremo a extremo de la pipeline se mantenga baja y que los lotes tengan un tamaño uniforme.
Usar inserciones asíncronas
timeout del batch processor. Esto puede causar problemas, y es ahí donde se requieren las inserciones asíncronas. Este caso suele darse cuando los collectors en el rol de agente están configurados para enviar directamente a ClickHouse. Los gateways, al actuar como agregadores, pueden aliviar este problema; consulta Escalado con gateways.
Si no se pueden garantizar lotes grandes, puedes delegar el batching en ClickHouse usando Inserciones asíncronas. Con las inserciones asíncronas, los datos se insertan primero en un búfer y luego se escriben en el almacenamiento de la base de datos más tarde, es decir, de forma asíncrona.
Con las inserciones asíncronas habilitadas, cuando ClickHouse ① recibe una consulta de inserción, los datos de la consulta ② se escriben inmediatamente en un búfer en memoria. Cuando ③ se produce el siguiente vaciado del búfer, los datos del búfer se ordenan y se escriben como una parte en el almacenamiento de la base de datos. Ten en cuenta que los datos no se pueden consultar antes de escribirse en el almacenamiento de la base de datos; el vaciado del búfer es configurable.
Para habilitar las inserciones asíncronas para el collector, añade async_insert=1 a la cadena de conexión. Recomendamos usar wait_for_async_insert=1 (el valor predeterminado) para obtener garantías de entrega; consulta aquí para más detalles.
Los datos de una inserción asíncrona se insertan una vez que se vacía el búfer de ClickHouse. Esto ocurre cuando se supera async_insert_max_data_size o después de async_insert_busy_timeout_ms milisegundos desde la primera consulta INSERT. Si async_insert_stale_timeout_ms se establece en un valor distinto de cero, los datos se insertan después de async_insert_stale_timeout_ms milliseconds desde la última consulta. Puedes ajustar esta configuración para controlar la latencia de extremo a extremo de tu pipeline. Otras opciones que pueden usarse para ajustar el vaciado del búfer están documentadas aquí. En general, los valores predeterminados son adecuados.
Considere las inserciones asíncronas adaptativasEn casos en los que se usa un número reducido de agentes, con bajo rendimiento pero requisitos estrictos de latencia de extremo a extremo, las inserciones asíncronas adaptativas pueden ser útiles. En general, no son aplicables a casos de uso de observabilidad de alto rendimiento, como los habituales en ClickHouse.
async_insert_deduplicate.
Los detalles completos sobre cómo configurar esta función se pueden encontrar aquí, y un análisis más detallado aquí.
Arquitecturas de implementación
Solo agentes
- Escalado de conexiones - Cada agente establecerá una conexión con ClickHouse. Aunque ClickHouse puede mantener cientos, si no miles, de conexiones de inserción concurrentes, esto acabará convirtiéndose en un factor limitante y hará que las inserciones sean menos eficientes; es decir, ClickHouse consumirá más recursos en mantener conexiones. El uso de gateways minimiza el número de conexiones y hace que las inserciones sean más eficientes.
- Procesamiento en el borde - En esta arquitectura, cualquier transformación o procesamiento de eventos debe realizarse en el borde o en ClickHouse. Además de ser restrictivo, esto puede implicar vistas materializadas complejas en ClickHouse o trasladar una carga de cómputo significativa al borde, donde los servicios críticos pueden verse afectados y los recursos pueden ser escasos.
- Lotes pequeños y latencias - Los collectors de agentes pueden recopilar individualmente muy pocos eventos. Esto normalmente significa que deben configurarse para vaciar el búfer a intervalos fijos a fin de cumplir los SLA de entrega. Como resultado, el collector puede enviar lotes pequeños a ClickHouse. Aunque esto supone una desventaja, puede mitigarse con inserciones asíncronas; consulte Optimización de inserciones.