Base de datos ClickHouse para humanos o tecnologías alienígenas

Aleksey Lizunov, Jefe del Centro de Competencia para Canales de Servicio Remoto de la Dirección de Tecnologías de la Información del MKB

Base de datos ClickHouse para humanos o tecnologías alienígenas

Como alternativa a la pila ELK (ElasticSearch, Logstash, Kibana), estamos investigando el uso de la base de datos ClickHouse como almacén de datos para registros.

En este artículo, nos gustaría hablar sobre nuestra experiencia en el uso de la base de datos ClickHouse y los resultados preliminares de la operación piloto. Cabe señalar de inmediato que los resultados fueron impresionantes.


Base de datos ClickHouse para humanos o tecnologías alienígenas

A continuación, describiremos con más detalle cómo está configurado nuestro sistema y de qué componentes consta. Pero ahora me gustaría hablar un poco sobre esta base de datos en su conjunto y por qué vale la pena prestarle atención. La base de datos ClickHouse es una base de datos columnar analítica de alto rendimiento de Yandex. Se utiliza en los servicios de Yandex, inicialmente es el principal almacenamiento de datos para Yandex.Metrica. Sistema de código abierto, gratuito. Desde el punto de vista de un desarrollador, siempre me he preguntado cómo lo implementaron, porque hay un gran volumen de datos fantástico. Y la propia interfaz de usuario de Metrica es muy flexible y rápida. Al conocer por primera vez esta base de datos, la impresión es: “¡Bueno, finalmente! ¡Hecho para la gente! Comenzando desde el proceso de instalación y terminando con el envío de solicitudes.

Esta base de datos tiene un umbral de entrada muy bajo. Incluso un desarrollador con habilidades promedio puede instalar esta base de datos en unos minutos y comenzar a usarla. Todo funciona con claridad. Incluso las personas que son nuevas en Linux pueden manejar rápidamente la instalación y realizar las operaciones más simples. Si antes, con las palabras Big Data, Hadoop, Google BigTable, HDFS, un desarrollador ordinario tenía ideas de que se trataba de algunos terabytes, petabytes, que algunos superhumanos se dedican a la configuración y el desarrollo de estos sistemas, entonces con el advenimiento de ClickHouse base de datos, obtuvimos una herramienta simple y comprensible con la que puede resolver una gama de tareas previamente inalcanzable. Solo se necesita una máquina bastante promedio y cinco minutos para instalar. Es decir, obtuvimos una base de datos como, por ejemplo, MySql, ¡pero solo para almacenar miles de millones de registros! Cierto súper-archivador con el lenguaje SQL. Es como si a la gente se le entregaran las armas de los extraterrestres.

Acerca de nuestro sistema de registro

Para recopilar información, se utilizan archivos de registro de IIS de aplicaciones web de formato estándar (actualmente también estamos analizando registros de aplicaciones, pero el objetivo principal en la etapa piloto es recopilar registros de IIS).

Por varias razones, no pudimos abandonar por completo la pila ELK y continuamos usando los componentes LogStash y Filebeat, que han demostrado su eficacia y funcionan de manera bastante confiable y predecible.

El esquema general de registro se muestra en la siguiente figura:

Base de datos ClickHouse para humanos o tecnologías alienígenas

Una característica de la escritura de datos en la base de datos de ClickHouse es la inserción poco frecuente (una vez por segundo) de registros en grandes lotes. Aparentemente, esta es la parte más “problemática” con la que te encuentras cuando trabajas por primera vez con la base de datos de ClickHouse: el esquema se vuelve un poco más complicado.
El complemento para LogStash, que inserta datos directamente en ClickHouse, ayudó mucho aquí. Este componente se implementa en el mismo servidor que la propia base de datos. Entonces, en términos generales, no se recomienda hacerlo, pero desde un punto de vista práctico, para no producir servidores separados mientras se implementa en el mismo servidor. No observamos ninguna falla o conflicto de recursos con la base de datos. Además, cabe señalar que el complemento tiene un mecanismo de reintento en caso de errores. Y en caso de errores, el complemento escribe en el disco un lote de datos que no se pudo insertar (el formato de archivo es conveniente: después de editar, puede insertar fácilmente el lote corregido usando clickhouse-client).

En la tabla se presenta una lista completa del software utilizado en el esquema:

Lista de programas utilizados

nombre

Descripción

Enlace de distribución

Nginx

Proxy inverso para restringir el acceso por puertos y organizar la autorización

Actualmente no se utiliza en el esquema

https://nginx.org/ru/download.html

https://nginx.org/download/nginx-1.16.0.tar.gz

ArchivoBeat

Transferencia de registros de archivos.

https://www.elastic.co/downloads/beats/filebeat (kit de distribución para Windows 64bit).

https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.3.0-windows-x86_64.zip

Log Stash

Recolector de troncos.

Se utiliza para recopilar registros de FileBeat, así como para recopilar registros de la cola de RabbitMQ (para servidores que se encuentran en la DMZ).

https://www.elastic.co/products/logstash

https://artifacts.elastic.co/downloads/logstash/logstash-7.0.1.rpm

Logstash-salida-clickhouse

Complemento de Loagstash para transferir registros a la base de datos de ClickHouse en lotes

https://github.com/mikechris/logstash-output-clickhouse

/usr/share/logstash/bin/logstash-plugin instalar logstash-salida-clickhouse

/usr/share/logstash/bin/logstash-plugin instalar logstash-filter-prune

/usr/share/logstash/bin/logstash-plugin instalar logstash-filter-multiline

casa de clics

Almacenamiento de registros https://clickhouse.yandex/docs/ru/

https://packagecloud.io/Altinity/clickhouse/packages/el/7/clickhouse-server-19.5.3.8-1.el7.x86_64.rpm

https://packagecloud.io/Altinity/clickhouse/packages/el/7/clickhouse-client-19.5.3.8-1.el7.x86_64.rpm

Nota. A partir de agosto de 2018, aparecieron compilaciones de rpm "normales" para RHEL en el repositorio de Yandex, por lo que puede intentar usarlas. En el momento de la instalación, estábamos usando paquetes creados por Altinity.

Grafana

Visualización de registros. Configuración de paneles

https://grafana.com/

https://grafana.com/grafana/download

Redhat y Centos (64 bits) - última versión

Fuente de datos ClickHouse para Grafana 4.6+

Complemento para Grafana con fuente de datos ClickHouse

https://grafana.com/plugins/vertamedia-clickhouse-datasource

https://grafana.com/api/plugins/vertamedia-clickhouse-datasource/versions/1.8.1/download

Log Stash

Inicie sesión en el enrutador de FileBeat a la cola de RabbitMQ.

Nota. Desafortunadamente, FileBeat no tiene una salida directa a RabbitMQ, por lo que se requiere un enlace intermedio en forma de Logstash.

https://www.elastic.co/products/logstash

https://artifacts.elastic.co/downloads/logstash/logstash-7.0.1.rpm

RabbitMQ

cola de mensajes. Este es el búfer de registro en la DMZ

https://www.rabbitmq.com/download.html

https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.7.14/rabbitmq-server-3.7.14-1.el7.noarch.rpm

Tiempo de ejecución de Erlang (requerido para RabbitMQ)

Tiempo de ejecución de Erlang. Requerido para que RabbitMQ funcione

http://www.erlang.org/download.html

https://www.rabbitmq.com/install-rpm.html#install-erlang http://www.erlang.org/downloads/21.3

La configuración del servidor con la base de datos de ClickHouse se presenta en la siguiente tabla:

nombre

Valor

Nota

Configuración

HDD: 40GB
RAM: 8GB
Procesador: Núcleo 2 2Ghz

Es necesario prestar atención a los consejos para operar la base de datos de ClickHouse (https://clickhouse.yandex/docs/ru/operations/tips/)

Software general del sistema

SO: Servidor Red Hat Enterprise Linux (Maipo)

JRE (Java 8)

 

Como puede ver, esta es una estación de trabajo ordinaria.

La estructura de la tabla para almacenar registros es la siguiente:

registro_web.sql

CREATE TABLE log_web (
  logdate Date,
  logdatetime DateTime CODEC(Delta, LZ4HC),
   
  fld_log_file_name LowCardinality( String ),
  fld_server_name LowCardinality( String ),
  fld_app_name LowCardinality( String ),
  fld_app_module LowCardinality( String ),
  fld_website_name LowCardinality( String ),
 
  serverIP LowCardinality( String ),
  method LowCardinality( String ),
  uriStem String,
  uriQuery String,
  port UInt32,
  username LowCardinality( String ),
  clientIP String,
  clientRealIP String,
  userAgent String,
  referer String,
  response String,
  subresponse String,
  win32response String,
  timetaken UInt64
   
  , uriQuery__utm_medium String
  , uriQuery__utm_source String
  , uriQuery__utm_campaign String
  , uriQuery__utm_term String
  , uriQuery__utm_content String
  , uriQuery__yclid String
  , uriQuery__region String
 
) Engine = MergeTree()
PARTITION BY toYYYYMM(logdate)
ORDER BY (fld_app_name, fld_app_module, logdatetime)
SETTINGS index_granularity = 8192;

Utilizamos partición predeterminada (por mes) y granularidad de índice. Todos los campos corresponden prácticamente a las entradas de registro de IIS para registrar solicitudes http. Por separado, observamos que hay campos separados para almacenar etiquetas utm (se analizan en la etapa de inserción en la tabla desde el campo de cadena de consulta).

Además, se han agregado varios campos del sistema a la tabla para almacenar información sobre sistemas, componentes y servidores. Consulte la siguiente tabla para obtener una descripción de estos campos. En una tabla, almacenamos registros para varios sistemas.

nombre

Descripción

ejemplo

fld_app_name

Nombre de aplicación/sistema
Valores válidos:

  • sitio1.dominio.com Sitio externo 1
  • sitio2.dominio.com Sitio externo 2
  • internal-site1.domain.local Sitio interno 1

sitio1.dominio.com

fld_app_module

Módulo del sistema
Valores válidos:

  • web - sitio web
  • svc - Servicio de sitio web
  • intgr - Servicio web de integración
  • bo - Administrador (BackOffice)

web

fld_sitio_web_nombre

Nombre del sitio en IIS

Se pueden implementar varios sistemas en un servidor, o incluso varias instancias de un módulo de sistema

web principal

fld_nombre_servidor

Nombre del servidor

web1.dominio.com

fld_log_nombre_archivo

Ruta al archivo de registro en el servidor

C:inetpublogsArchivos de registro
W3SVC1u_ex190711.log

Esto le permite construir gráficos de manera eficiente en Grafana. Por ejemplo, ver solicitudes desde la interfaz de un sistema en particular. Esto es similar al contador del sitio en Yandex.Metrica.

Aquí hay algunas estadísticas sobre el uso de la base de datos durante dos meses.

Número de registros desglosados ​​por sistemas y sus componentes

SELECT
    fld_app_name,
    fld_app_module,
    count(fld_app_name) AS rows_count
FROM log_web
GROUP BY
    fld_app_name,
    fld_app_module
    WITH TOTALS
ORDER BY
    fld_app_name ASC,
    rows_count DESC
 
┌─fld_app_name─────┬─fld_app_module─┬─rows_count─┐
│ site1.domain.ru  │ web            │     131441 │
│ site2.domain.ru  │ web            │    1751081 │
│ site3.domain.ru  │ web            │  106887543 │
│ site3.domain.ru  │ svc            │   44908603 │
│ site3.domain.ru  │ intgr          │    9813911 │
│ site4.domain.ru  │ web            │     772095 │
│ site5.domain.ru  │ web            │   17037221 │
│ site5.domain.ru  │ intgr          │     838559 │
│ site5.domain.ru  │ bo             │       7404 │
│ site6.domain.ru  │ web            │     595877 │
│ site7.domain.ru  │ web            │   27778858 │
└──────────────────┴────────────────┴────────────┘
 
Totals:
┌─fld_app_name─┬─fld_app_module─┬─rows_count─┐
│              │                │  210522593 │
└──────────────┴────────────────┴────────────┘
 
11 rows in set. Elapsed: 4.874 sec. Processed 210.52 million rows, 421.67 MB (43.19 million rows/s., 86.51 MB/s.)

La cantidad de datos en el disco

SELECT
    formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed,
    formatReadableSize(sum(data_compressed_bytes)) AS compressed,
    sum(rows) AS total_rows
FROM system.parts
WHERE table = 'log_web'
 
┌─uncompressed─┬─compressed─┬─total_rows─┐
│ 54.50 GiB    │ 4.86 GiB   │  211427094 │
└──────────────┴────────────┴────────────┘
 
1 rows in set. Elapsed: 0.035 sec.

Grado de compresión de datos en columnas

SELECT
    name,
    formatReadableSize(data_uncompressed_bytes) AS uncompressed,
    formatReadableSize(data_compressed_bytes) AS compressed,
    data_uncompressed_bytes / data_compressed_bytes AS compress_ratio
FROM system.columns
WHERE table = 'log_web'
 
┌─name───────────────────┬─uncompressed─┬─compressed─┬─────compress_ratio─┐
│ logdate                │ 401.53 MiB   │ 1.80 MiB   │ 223.16665968777315 │
│ logdatetime            │ 803.06 MiB   │ 35.91 MiB  │ 22.363966401202305 │
│ fld_log_file_name      │ 220.66 MiB   │ 2.60 MiB   │  84.99905736932571 │
│ fld_server_name        │ 201.54 MiB   │ 50.63 MiB  │  3.980924816977078 │
│ fld_app_name           │ 201.17 MiB   │ 969.17 KiB │ 212.55518183686877 │
│ fld_app_module         │ 201.17 MiB   │ 968.60 KiB │ 212.67805817411906 │
│ fld_website_name       │ 201.54 MiB   │ 1.24 MiB   │  162.7204926761546 │
│ serverIP               │ 201.54 MiB   │ 50.25 MiB  │  4.010824061219731 │
│ method                 │ 201.53 MiB   │ 43.64 MiB  │  4.617721053304486 │
│ uriStem                │ 5.13 GiB     │ 832.51 MiB │  6.311522291936919 │
│ uriQuery               │ 2.58 GiB     │ 501.06 MiB │  5.269731450124478 │
│ port                   │ 803.06 MiB   │ 3.98 MiB   │ 201.91673864241824 │
│ username               │ 318.08 MiB   │ 26.93 MiB  │ 11.812513794583598 │
│ clientIP               │ 2.35 GiB     │ 82.59 MiB  │ 29.132328640073343 │
│ clientRealIP           │ 2.49 GiB     │ 465.05 MiB │  5.478382297052563 │
│ userAgent              │ 18.34 GiB    │ 764.08 MiB │  24.57905114484208 │
│ referer                │ 14.71 GiB    │ 1.37 GiB   │ 10.736792723669906 │
│ response               │ 803.06 MiB   │ 83.81 MiB  │  9.582334090987247 │
│ subresponse            │ 399.87 MiB   │ 1.83 MiB   │  218.4831068635027 │
│ win32response          │ 407.86 MiB   │ 7.41 MiB   │ 55.050315514606815 │
│ timetaken              │ 1.57 GiB     │ 402.06 MiB │ 3.9947395692010637 │
│ uriQuery__utm_medium   │ 208.17 MiB   │ 12.29 MiB  │ 16.936148912472955 │
│ uriQuery__utm_source   │ 215.18 MiB   │ 13.00 MiB  │ 16.548367623199912 │
│ uriQuery__utm_campaign │ 381.46 MiB   │ 37.94 MiB  │ 10.055156353418509 │
│ uriQuery__utm_term     │ 231.82 MiB   │ 10.78 MiB  │ 21.502540454070672 │
│ uriQuery__utm_content  │ 441.34 MiB   │ 87.60 MiB  │  5.038260760449327 │
│ uriQuery__yclid        │ 216.88 MiB   │ 16.58 MiB  │  13.07721335008116 │
│ uriQuery__region       │ 204.35 MiB   │ 9.49 MiB   │  21.52661903446796 │
└────────────────────────┴──────────────┴────────────┴────────────────────┘
 
28 rows in set. Elapsed: 0.005 sec.

Descripción de los componentes usados

FileBeat. Transferir registros de archivos

Este componente rastrea los cambios en los archivos de registro en el disco y pasa la información a LogStash. Instalado en todos los servidores donde se escriben los archivos de registro (generalmente IIS). Funciona en modo cola (es decir, transfiere solo los registros agregados al archivo). Pero por separado se puede configurar para transferir archivos completos. Esto es útil cuando necesita descargar datos de meses anteriores. Simplemente coloque el archivo de registro en una carpeta y lo leerá en su totalidad.

Cuando se detiene el servicio, los datos ya no se transfieren más al almacenamiento.

Una configuración de ejemplo se ve así:

archivobeat.yml

filebeat.inputs:
- type: log
  enabled: true
  paths:
    - C:/inetpub/logs/LogFiles/W3SVC1/*.log
  exclude_files: ['.gz$','.zip$']
  tail_files: true
  ignore_older: 24h
  fields:
    fld_server_name: "site1.domain.ru"
    fld_app_name: "site1.domain.ru"
    fld_app_module: "web"
    fld_website_name: "web-main"
 
- type: log
  enabled: true
  paths:
    - C:/inetpub/logs/LogFiles/__Import/access_log-*
  exclude_files: ['.gz$','.zip$']
  tail_files: false
  fields:
    fld_server_name: "site2.domain.ru"
    fld_app_name: "site2.domain.ru"
    fld_app_module: "web"
    fld_website_name: "web-main"
    fld_logformat: "logformat__apache"
 
 
filebeat.config.modules:
  path: ${path.config}/modules.d/*.yml
  reload.enabled: false
  reload.period: 2s
 
output.logstash:
  hosts: ["log.domain.com:5044"]
 
  ssl.enabled: true
  ssl.certificate_authorities: ["C:/filebeat/certs/ca.pem", "C:/filebeat/certs/ca-issuing.pem"]
  ssl.certificate: "C:/filebeat/certs/site1.domain.ru.cer"
  ssl.key: "C:/filebeat/certs/site1.domain.ru.key"
 
#================================ Processors =====================================
 
processors:
  - add_host_metadata: ~
  - add_cloud_metadata: ~

almacenamiento de registros Recolector de registros

Este componente está diseñado para recibir entradas de registro de FileBeat (oa través de la cola de RabbitMQ), analizando e insertando lotes en la base de datos de ClickHouse.

Para la inserción en ClickHouse, se utiliza el complemento Logstash-output-clickhouse. El complemento Logstash tiene un mecanismo de reintento de solicitud, pero con un apagado regular, es mejor detener el servicio en sí. Cuando se detenga, los mensajes se acumularán en la cola de RabbitMQ, por lo que si la detención es prolongada, es mejor detener Filebeats en los servidores. En un esquema en el que no se utiliza RabbitMQ (en la red local, Filebeat envía directamente los registros a Logstash), Filebeats funciona de manera bastante aceptable y segura, por lo que para ellos la falta de disponibilidad de la salida pasa sin consecuencias.

Una configuración de ejemplo se ve así:

log_web__filebeat_clickhouse.conf

input {
 
    beats {
        port => 5044
        type => 'iis'
        ssl => true
        ssl_certificate_authorities => ["/etc/logstash/certs/ca.cer", "/etc/logstash/certs/ca-issuing.cer"]
        ssl_certificate => "/etc/logstash/certs/server.cer"
        ssl_key => "/etc/logstash/certs/server-pkcs8.key"
        ssl_verify_mode => "peer"
 
            add_field => {
                "fld_server_name" => "%{[fields][fld_server_name]}"
                "fld_app_name" => "%{[fields][fld_app_name]}"
                "fld_app_module" => "%{[fields][fld_app_module]}"
                "fld_website_name" => "%{[fields][fld_website_name]}"
                "fld_log_file_name" => "%{source}"
                "fld_logformat" => "%{[fields][fld_logformat]}"
            }
    }
 
    rabbitmq {
        host => "queue.domain.com"
        port => 5671
        user => "q-reader"
        password => "password"
        queue => "web_log"
        heartbeat => 30
        durable => true
        ssl => true
        #ssl_certificate_path => "/etc/logstash/certs/server.p12"
        #ssl_certificate_password => "password"
 
        add_field => {
            "fld_server_name" => "%{[fields][fld_server_name]}"
            "fld_app_name" => "%{[fields][fld_app_name]}"
            "fld_app_module" => "%{[fields][fld_app_module]}"
            "fld_website_name" => "%{[fields][fld_website_name]}"
            "fld_log_file_name" => "%{source}"
            "fld_logformat" => "%{[fields][fld_logformat]}"
        }
    }
 
}
 
filter { 
 
      if [message] =~ "^#" {
        drop {}
      }
 
      if [fld_logformat] == "logformat__iis_with_xrealip" {
     
          grok {
            match => ["message", "%{TIMESTAMP_ISO8601:log_timestamp} %{IP:serverIP} %{WORD:method} %{NOTSPACE:uriStem} %{NOTSPACE:uriQuery} %{NUMBER:port} %{NOTSPACE:username} %{IPORHOST:clientIP} %{NOTSPACE:userAgent} %{NOTSPACE:referer} %{NUMBER:response} %{NUMBER:subresponse} %{NUMBER:win32response} %{NUMBER:timetaken} %{NOTSPACE:xrealIP} %{NOTSPACE:xforwarderfor}"]
          }
      } else {
   
          grok {
             match => ["message", "%{TIMESTAMP_ISO8601:log_timestamp} %{IP:serverIP} %{WORD:method} %{NOTSPACE:uriStem} %{NOTSPACE:uriQuery} %{NUMBER:port} %{NOTSPACE:username} %{IPORHOST:clientIP} %{NOTSPACE:userAgent} %{NOTSPACE:referer} %{NUMBER:response} %{NUMBER:subresponse} %{NUMBER:win32response} %{NUMBER:timetaken}"]
          }
 
      }
 
      date {
        match => [ "log_timestamp", "YYYY-MM-dd HH:mm:ss" ]
          timezone => "Etc/UTC"
        remove_field => [ "log_timestamp", "@timestamp" ]
        target => [ "log_timestamp2" ]
      }
 
        ruby {
            code => "tstamp = event.get('log_timestamp2').to_i
                        event.set('logdatetime', Time.at(tstamp).strftime('%Y-%m-%d %H:%M:%S'))
                        event.set('logdate', Time.at(tstamp).strftime('%Y-%m-%d'))"
        }
 
      if [bytesSent] {
        ruby {
          code => "event['kilobytesSent'] = event['bytesSent'].to_i / 1024.0"
        }
      }
 
 
      if [bytesReceived] {
        ruby {
          code => "event['kilobytesReceived'] = event['bytesReceived'].to_i / 1024.0"
        }
      }
 
   
        ruby {
            code => "event.set('clientRealIP', event.get('clientIP'))"
        }
        if [xrealIP] {
            ruby {
                code => "event.set('clientRealIP', event.get('xrealIP'))"
            }
        }
        if [xforwarderfor] {
            ruby {
                code => "event.set('clientRealIP', event.get('xforwarderfor'))"
            }
        }
 
      mutate {
        convert => ["bytesSent", "integer"]
        convert => ["bytesReceived", "integer"]
        convert => ["timetaken", "integer"] 
        convert => ["port", "integer"]
 
        add_field => {
            "clientHostname" => "%{clientIP}"
        }
      }
 
        useragent {
            source=> "useragent"
            prefix=> "browser"
        }
 
        kv {
            source => "uriQuery"
            prefix => "uriQuery__"
            allow_duplicate_values => false
            field_split => "&"
            include_keys => [ "utm_medium", "utm_source", "utm_campaign", "utm_term", "utm_content", "yclid", "region" ]
        }
 
        mutate {
            join => { "uriQuery__utm_source" => "," }
            join => { "uriQuery__utm_medium" => "," }
            join => { "uriQuery__utm_campaign" => "," }
            join => { "uriQuery__utm_term" => "," }
            join => { "uriQuery__utm_content" => "," }
            join => { "uriQuery__yclid" => "," }
            join => { "uriQuery__region" => "," }
        }
 
}
 
output { 
  #stdout {codec => rubydebug}
    clickhouse {
      headers => ["Authorization", "Basic abcdsfks..."]
      http_hosts => ["http://127.0.0.1:8123"]
      save_dir => "/etc/logstash/tmp"
      table => "log_web"
      request_tolerance => 1
      flush_size => 10000
      idle_flush_time => 1
        mutations => {
            "fld_log_file_name" => "fld_log_file_name"
            "fld_server_name" => "fld_server_name"
            "fld_app_name" => "fld_app_name"
            "fld_app_module" => "fld_app_module"
            "fld_website_name" => "fld_website_name"
 
            "logdatetime" => "logdatetime"
            "logdate" => "logdate"
            "serverIP" => "serverIP"
            "method" => "method"
            "uriStem" => "uriStem"
            "uriQuery" => "uriQuery"
            "port" => "port"
            "username" => "username"
            "clientIP" => "clientIP"
            "clientRealIP" => "clientRealIP"
            "userAgent" => "userAgent"
            "referer" => "referer"
            "response" => "response"
            "subresponse" => "subresponse"
            "win32response" => "win32response"
            "timetaken" => "timetaken"
             
            "uriQuery__utm_medium" => "uriQuery__utm_medium"
            "uriQuery__utm_source" => "uriQuery__utm_source"
            "uriQuery__utm_campaign" => "uriQuery__utm_campaign"
            "uriQuery__utm_term" => "uriQuery__utm_term"
            "uriQuery__utm_content" => "uriQuery__utm_content"
            "uriQuery__yclid" => "uriQuery__yclid"
            "uriQuery__region" => "uriQuery__region"
        }
    }
 
}

tuberías.yml

# This file is where you define your pipelines. You can define multiple.
# For more information on multiple pipelines, see the documentation:
#   https://www.elastic.co/guide/en/logstash/current/multiple-pipelines.html
 
- pipeline.id: log_web__filebeat_clickhouse
  path.config: "/etc/logstash/log_web__filebeat_clickhouse.conf"

casa de clics. Almacenamiento de registros

Los registros de todos los sistemas se almacenan en una tabla (ver al principio del artículo). Está destinado a almacenar información sobre solicitudes: todos los parámetros son similares para diferentes formatos, como registros de IIS, registros de apache y nginx. Para los logs de la aplicación, en los que se registran, por ejemplo, errores, mensajes de información, advertencias, se proporcionará una tabla separada con la estructura adecuada (actualmente en la etapa de diseño).

Al diseñar una tabla, es muy importante decidir la clave principal (según la cual se ordenarán los datos durante el almacenamiento). El grado de compresión de datos y la velocidad de consulta dependen de esto. En nuestro ejemplo, la clave es
ORDENAR POR (fld_app_name, fld_app_module, logdatetime)
Es decir, por el nombre del sistema, el nombre del componente del sistema y la fecha del evento. Inicialmente, la fecha del evento fue lo primero. Después de moverlo al último lugar, las consultas comenzaron a funcionar aproximadamente el doble de rápido. Cambiar la clave principal requerirá volver a crear la tabla y recargar los datos para que ClickHouse vuelva a ordenar los datos en el disco. Esta es una operación pesada, por lo que es una buena idea pensar mucho sobre lo que debe incluirse en la clave de ordenación.

También se debe tener en cuenta que el tipo de datos LowCardinality ha aparecido relativamente en versiones recientes. Al usarlo, el tamaño de los datos comprimidos se reduce drásticamente para aquellos campos que tienen baja cardinalidad (pocas opciones).

La versión 19.6 está actualmente en uso y planeamos intentar actualizar a la última versión. Tienen características tan maravillosas como la granularidad adaptativa, los índices de omisión y el códec DoubleDelta, por ejemplo.

De forma predeterminada, durante la instalación, el nivel de registro se establece en seguimiento. Los registros se rotan y archivan, pero al mismo tiempo se expanden hasta un gigabyte. Si no es necesario, puede establecer el nivel de advertencia, luego el tamaño del registro se reduce drásticamente. La configuración de registro se establece en el archivo config.xml:

<!-- Possible levels: https://github.com/pocoproject/poco/blob/develop/Foundation/include/Poco/Logger. h#L105 -->
<level>warning</level>

Algunos comandos útiles

Поскольку оригинальные пакеты установки собираются по Debian, то для других версий Linux необходимо использовать пакеты собранные компанией Altinity.
 
Вот по этой ссылке есть инструкции с ссылками на их репозиторий: https://www.altinity.com/blog/2017/12/18/logstash-with-clickhouse
sudo yum search clickhouse-server
sudo yum install clickhouse-server.noarch
  
1. проверка статуса
sudo systemctl status clickhouse-server
 
2. остановка сервера
sudo systemctl stop clickhouse-server
 
3. запуск сервера
sudo systemctl start clickhouse-server
 
Запуск для выполнения запросов в многострочном режиме (выполнение после знака ";")
clickhouse-client --multiline
clickhouse-client --multiline --host 127.0.0.1 --password pa55w0rd
clickhouse-client --multiline --host 127.0.0.1 --port 9440 --secure --user default --password pa55w0rd
 
Плагин кликлауза для логстеш в случае ошибки в одной строке сохраняет всю пачку в файл /tmp/log_web_failed.json
Можно вручную исправить этот файл и попробовать залить его в БД вручную:
clickhouse-client --host 127.0.0.1 --password password --query="INSERT INTO log_web FORMAT JSONEachRow" < /tmp/log_web_failed__fixed.json
 
sudo mv /etc/logstash/tmp/log_web_failed.json /etc/logstash/tmp/log_web_failed__fixed.json
sudo chown user_dev /etc/logstash/tmp/log_web_failed__fixed.json
sudo clickhouse-client --host 127.0.0.1 --password password --query="INSERT INTO log_web FORMAT JSONEachRow" < /etc/logstash/tmp/log_web_failed__fixed.json
sudo mv /etc/logstash/tmp/log_web_failed__fixed.json /etc/logstash/tmp/log_web_failed__fixed_.json
 
выход из командной строки
quit;
## Настройка TLS
https://www.altinity.com/blog/2019/3/5/clickhouse-networking-part-2
 
openssl s_client -connect log.domain.com:9440 < /dev/null

almacenamiento de registros Registrar el enrutador de FileBeat a la cola de RabbitMQ

Este componente se usa para enrutar registros provenientes de FileBeat a la cola de RabbitMQ. Hay dos puntos aquí:

  1. Desafortunadamente, FileBeat no tiene un complemento de salida para escribir directamente en RabbitMQ. Y dicha funcionalidad, a juzgar por el problema en su github, no está planificada para su implementación. Hay un complemento para Kafka, pero por alguna razón no podemos usarlo en casa.
  2. Existen requisitos para recopilar registros en la DMZ. Según ellos, los registros primero deben agregarse a la cola y luego LogStash lee las entradas de la cola desde el exterior.

Por lo tanto, es en el caso de que los servidores estén ubicados en la DMZ que uno tiene que usar un esquema un poco complicado. Una configuración de ejemplo se ve así:

iis_w3c_logs__filebeat_rabbitmq.conf

input {
 
    beats {
        port => 5044
        type => 'iis'
        ssl => true
        ssl_certificate_authorities => ["/etc/pki/tls/certs/app/ca.pem", "/etc/pki/tls/certs/app/ca-issuing.pem"]
        ssl_certificate => "/etc/pki/tls/certs/app/queue.domain.com.cer"
        ssl_key => "/etc/pki/tls/certs/app/queue.domain.com-pkcs8.key"
        ssl_verify_mode => "peer"
    }
 
}
 
output { 
  #stdout {codec => rubydebug}
 
    rabbitmq {
        host => "127.0.0.1"
        port => 5672
        exchange => "monitor.direct"
        exchange_type => "direct"
        key => "%{[fields][fld_app_name]}"
        user => "q-writer"
        password => "password"
        ssl => false
    }
}

ConejoMQ. cola de mensajes

Este componente se utiliza para almacenar en búfer las entradas de registro en la DMZ. La grabación se realiza a través de un montón de Filebeat → LogStash. La lectura se realiza desde fuera de la DMZ a través de LogStash. Al operar a través de RabboitMQ, se procesan alrededor de 4 mil mensajes por segundo.

El enrutamiento de mensajes se configura por nombre del sistema, es decir, en función de los datos de configuración de FileBeat. Todos los mensajes van a una cola. Si por algún motivo se detiene el servicio de cola, esto no provocará la pérdida de mensajes: FileBeats recibirá errores de conexión y suspenderá temporalmente el envío. Y LogStash que lee de la cola también recibirá errores de red y esperará a que se restablezca la conexión. En este caso, los datos, por supuesto, ya no se escribirán en la base de datos.

Las siguientes instrucciones se utilizan para crear y configurar colas:

sudo /usr/local/bin/rabbitmqadmin/rabbitmqadmin declare exchange --vhost=/ name=monitor.direct type=direct sudo /usr/local/bin/rabbitmqadmin/rabbitmqadmin declare queue --vhost=/ name=web_log durable=true
sudo /usr/local/bin/rabbitmqadmin/rabbitmqadmin --vhost="/" declare binding source="monitor.direct" destination_type="queue" destination="web_log" routing_key="site1.domain.ru"
sudo /usr/local/bin/rabbitmqadmin/rabbitmqadmin --vhost="/" declare binding source="monitor.direct" destination_type="queue" destination="web_log" routing_key="site2.domain.ru"

Grafana. Tableros

Este componente se utiliza para visualizar los datos de seguimiento. En este caso, debe instalar la fuente de datos ClickHouse para el complemento Grafana 4.6+. Tuvimos que modificarlo un poco para mejorar la eficiencia del procesamiento de filtros SQL en el tablero.

Por ejemplo, usamos variables, y si no están configuradas en el campo de filtro, nos gustaría que no generara una condición en el WHERE del formulario ( uriStem = » AND uriStem != » ). En este caso, ClickHouse leerá la columna uriStem. En general, probamos diferentes opciones y finalmente corregimos el complemento (la macro $valueIfEmpty) para que, en el caso de un valor vacío, devuelva 1, sin mencionar la columna en sí.

Y ahora puedes usar esta consulta para el gráfico.

$columns(response, count(*) c) from $table where $adhoc
and $valueIfEmpty($fld_app_name, 1, fld_app_name = '$fld_app_name')
and $valueIfEmpty($fld_app_module, 1, fld_app_module = '$fld_app_module') and $valueIfEmpty($fld_server_name, 1, fld_server_name = '$fld_server_name') and $valueIfEmpty($uriStem, 1, uriStem like '%$uriStem%')
and $valueIfEmpty($clientRealIP, 1, clientRealIP = '$clientRealIP')

que se traduce a este SQL (tenga en cuenta que los campos vacíos de uriStem se han convertido en solo 1)

SELECT
t,
groupArray((response, c)) AS groupArr
FROM (
SELECT
(intDiv(toUInt32(logdatetime), 60) * 60) * 1000 AS t, response,
count(*) AS c FROM default.log_web
WHERE (logdate >= toDate(1565061982)) AND (logdatetime >= toDateTime(1565061982)) AND 1 AND (fld_app_name = 'site1.domain.ru') AND (fld_app_module = 'web') AND 1 AND 1 AND 1
GROUP BY
t, response
ORDER BY
t ASC,
response ASC
)
GROUP BY t ORDER BY t ASC

Conclusión

La aparición de la base de datos ClickHouse se ha convertido en un hito en el mercado. Era difícil imaginar que, de forma totalmente gratuita, en un instante dispusiéramos de una potente y práctica herramienta para trabajar con big data. Por supuesto, con el aumento de las necesidades (por ejemplo, fragmentación y replicación en varios servidores), el esquema se volverá más complicado. Pero a primera vista, trabajar con esta base de datos es muy agradable. Se puede ver que el producto está hecho "para personas".

En comparación con ElasticSearch, se estima que el costo de almacenamiento y procesamiento de registros se reduce entre cinco y diez veces. En otras palabras, si para la cantidad actual de datos tuviéramos que configurar un clúster de varias máquinas, entonces al usar ClickHouse, una máquina de bajo consumo es suficiente para nosotros. Sí, por supuesto, ElasticSearch también tiene mecanismos de compresión de datos en disco y otras funciones que pueden reducir significativamente el consumo de recursos, pero en comparación con ClickHouse, esto será más costoso.

Sin optimizaciones especiales de nuestra parte, en la configuración predeterminada, la carga de datos y la selección de la base de datos funciona a una velocidad asombrosa. Todavía no tenemos muchos datos (alrededor de 200 millones de registros), pero el servidor en sí es débil. Podemos utilizar esta herramienta en el futuro para otros fines no relacionados con el almacenamiento de registros. Por ejemplo, para análisis de extremo a extremo, en el campo de la seguridad, aprendizaje automático.

Al final, un poco sobre los pros y los contras.

Contras

  1. Cargar registros en grandes lotes. Por un lado, esta es una característica, pero aún debe usar componentes adicionales para almacenar registros en búfer. Esta tarea no siempre es fácil, pero aún tiene solución. Y me gustaría simplificar el esquema.
  2. Algunas funcionalidades exóticas o nuevas características a menudo se rompen en las nuevas versiones. Esto causa preocupación, reduciendo el deseo de actualizar a una nueva versión. Por ejemplo, el motor de tablas de Kafka es una función muy útil que le permite leer directamente eventos de Kafka, sin implementar consumidores. Pero a juzgar por la cantidad de problemas en github, todavía tenemos cuidado de no usar este motor en producción. Sin embargo, si no hace gestos bruscos hacia un lado y usa la funcionalidad principal, entonces funciona de manera estable.

Pros

  1. No se ralentiza.
  2. Umbral de entrada bajo.
  3. Fuente abierta.
  4. Gratis.
  5. Escala bien (fragmentación/replicación lista para usar)
  6. Incluido en el registro de software ruso recomendado por el Ministerio de Comunicaciones.
  7. La presencia de apoyo oficial de Yandex.

Fuente: habr.com

Añadir un comentario